본문 바로가기

IT/OPENCV

[OPENCV] 5. 색 공간 모델 HSV

반응형

☞ 메인보드 : Jetson Nano Developer Kit

운영 체제 : Ubuntu 18.04 - JetPack 4.4.1

☞ OpenCV 버전 : 4.2.0

☞ IDE : Visual Studio Code

☞ 언어 : C++

 

 

 


목차

1. HSV란?

2. OPENCV의 HSV함수

3. 코드 작성

4. HSV 활용

 

 

 


 

 

구글에 HSV를 검색하면 신생아의 단순포진이나....함부르크 SV 축구팀이 나오지만 오늘은 색공간 모델인 HSV에 대해서 알아보도록 하겠다.

 

 

 

 

 

① HSV란?

 

 

 

출처: https://medium.com/neurosapiens/segmentation-and-classification-with-hsv-8f2406c62b39

 

 

지금은 TV의 화면을 보면 아주 부드럽다는? 느낌이 들어서 아무 생각이 안들지만 라떼는 브라운관 TV라 해서 화면에 가까이 다가가면 색이 변하는 아주 작은 점들을 볼 수 있었다. 이 점을 "몇 백만 화소" 할때의 화소라고 하는데 영어로 픽셀(Pixel)이라고 한다. 이 픽셀은 RED, GLEEN, BLUE 세 가지 색의 정도에 따라 혼합되어 다양한 색을 출력한다.

 

일반적으로는 픽셀의 index 원 투 쓰리를 담당하는 R, G, B 값은 색을 혼합할 수록 명도가 올라가는 가산혼합 방식으로 색을 표현한다. 많이 섞으면 흰색에 가깝고 덜 섞을수록 검정색에 가까운 모델인 것이다. 그에 비해 HSV 색 공간은 색을 혼합할 수록 밝아지는 가산혼합과 혼합할 수록 어두워지는 감산혼합과 같은 방식처럼 극단적이지 않기 때문에 말로 표현하기 쉽고 직관적이어 시각 예술에 자주 쓰인다고 한다.

 

HSV는 Hue(색상), Saturation(채도), Value(명도)의 앞 알파벳을 따서 이름이 지어졌는데 각 채널들은 아래와 같은 의미를 갖고 있다.

 

Hue 색상 ☞ 색깔

Saturation 채도 ☞ 색의 진하고 연한 정도

Value 명도 ☞ 색의 밝고 어두운 정도

 

세 가지 채널에 대해 어떤 느낌인지 알려면 직접 코딩을 해서 눈알로 보는 수 밖에 없다. 일단은 HSV가 세 가지 채널에 의해 어떻게 출력되는지 결정된다는 것만 알고 넘어가면 된다.

 

 

 


② OPENCV의 HSV 함수

 

OPENCV의 함수를 볼 수 있는 사이트가 있다. 함수의 타입이나 매개변수 그리고 다양한 예제들까지 볼 수 있는 아주 유용한 사이트이다. 본인의 OPENCV 버전에 맞게 입장하자.

 

 

OpenCV documentation index

 

docs.opencv.org

 

오른쪽에 보이는 검색창에 RGB2HSV나 cvtcolor 검색하면 OPENCV에서 사용하는 함수에 대해 알 수 있다.

바로 아래에 있는 cv::gapi::RGB2HSV(), cv::cvtColor()를 누르자.

 

 

 

 

 

 

RGB에서 HSV로 이미지를 전환시켜주는 함수라하고, 각 채널 값의 범위를 아두이노 PWM 출력처럼 0~255값으로 매핑해놓았다. 

여기서 중요한 내용은 이미지 형식이 CV_8UC3 라는 것이다.

 

8UC3 ☞ 8bit의 Unsigned형 + 3개의 Channel

 

여기서 Unsigned는 부호가 없다는 것을 의미한다. 즉, 0값 아래의 음수(-)는 사용하지 않는다는 말이다.

3개의 Channel은 아까 설명한 Hue(색상), Saturation(채도), Value(명도) 세 가지를 의미한다.

 

[+ 0~255까지 256개의 값으로 매핑된 이유는 8bit 형식이기 때문이다. 8bit(1byte)는 0과 1로 이루어진 2진수를 이용하여 1bit짜리 메모리 박스 8칸을 채워넣을 수 있다.  (00000000 ~ 11111111) 처럼 0과 1을 이용하여 2의 8제곱만큼의 경우의 수를 만들어낼 수 있기 때문에 0~255 값으로 사용되는 것이다. Hue는 (원기둥의 360도) 360가지의 색상을 다뤄야 했지만 256가지 밖에 다루지 못하여 최대값이 179이다.]

 

 

 

 

 

 

cvtColor(InputArray src, OutputArray dst, code) 함수를 이용해서 HSV로 변환하는 과정은 조금 다르다.

함수의 첫 번째 매개변수 src에는 입력 이미지(RGB)를, 두 번째 매개변수 dst에는 출력 이미지(HSV) 변수들을 입력한다.

함수의 세 번째 매개변수 code 자리에 cv::COLOR_RGB2HSV를 입력해서 RGB 형식의 이미지를 HSV 이미지로 전환할 수 있다.

 

입력 이미지인 src 자리에는 부호가 없는 8비트[8UC], 16비트[16UC]의 이미지 형식을 넣을 수 있고 출력 이미지인 dst도 src와 같은 형식의 이미지를 반환하도록 되어있다. code 자리에는 다양한 전환 코드가 들어갈 수 있다. RGB2GRAY나 RGB2HSL 등 다양한 색 공간 모델을 전환할 수 있다.

 

위에서 언급한 이미지 형식과 다른 이미지 형식으로 해당 함수에 대입하게되면 곧 에러를 반환한다. 다양한 이미지 형식으로 작업을 하고 싶다면 각각 객체를 만들어 지정하는 작업을 한 후에 하도록 하는 것이 바람직하다.

 

이렇게만 봐서는 느낌이 안오니 직접 해보도록 하자.

 

 

 

 


③ 코드 작성

 

프로젝트를 생성하고 VS Code를 실행한다. 다운로드를 안했으면 아래 링크에서 설치 방법이 있으니 참고 바란다.

 

Jetson Nano(젯슨나노) vscode- Microsoft Visual Studio Code 설치하기

최근 핫한 코드편집기인 마이크로소프트사의 Visual Studio Code(이하 vscode)를 ARM기반의 싱글보드 PC인 젯슨나노에 설치하기위해 알아보았습니다. IDE 장인으로 유명한 마소의 vscode는 다양한 개발환

opencourse.tistory.com

mkdir ~/workspace/colorModel/src
code

 

그리고 예제에 사용할 요 이미지를 받아서 ~/workspace/colorModel 디렉터리 안에 넣어준다.

 

 

 

 

 

 

※ hsv.cpp

 

파일 위치 ~/workspace/colorModel/src/hsv.cpp

 

#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

int main(){

	int width = 600, height = 400;

	cv::Mat img, hsv;
	img = cv::imread("road2.jpg", cv::IMREAD_COLOR);
	cv::cvtColor(img, hsv, cv::COLOR_RGB2HSV);
	if(img.empty()){
		std::cout << "No Image" << std::endl;
		return -1;
	}

	cv::namedWindow("ORIGINAL IMAGE", cv::WINDOW_NORMAL);
	cv::resizeWindow("ORIGINAL IMAGE", width, height);
	cv::imshow("ORIGINAL IMAGE", img);
	
	cv::namedWindow("HSV IMAGE", cv::WINDOW_NORMAL);
	cv::resizeWindow("HSV IMAGE", width, height);
	cv::imshow("HSV IMAGE", hsv);
	
	cv::waitKey(0);
}

 

 

 

 

 

※  CMakeLists.txt

 

파일 위치 ~/workspace/colorModel/src/hsv.cpp

 

cmake_minimum_required(VERSION 2.9)
project(colorModel)
find_package(OpenCV REQUIRED)
add_executable(${PROJECT_NAME} src/hsv.cpp)
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})

 

 

 

 

 

 

 

※ 빌드 과정

 

// 디렉터리 : ~/workspace/colorModel
cmake .
make

 

 

파일 작성을 완료했으면 cmake . 명령어로 makefile을 생성할 수 있다. cmake가 마무리되면 터미널의 같은 디렉터리에서 make 명령어를 입력하자. 모든 명령어로 마무리하고 나면 colorModel 디렉터리에는 위 사진과 같이 디렉터리, 파일들이 생성된다.

 

 

 

 

 

 

 

※ 실행 결과

 

// 실행방법
./colorModel

 

 

 

 

왼쪽이 원본, 오른쪽이 HSV 이미지다. 이렇게 HSV 이미지는 환술에 걸린 이미지 같이 생겼는데 일반 RGB 이미지를 색 공간 모델로 전환해서 할 수 있는게 뭘까?

 

Hue는 0에서 180의 범위 값을 갖는다. 이는 범위를 통해 사용자가 직접 원하는 색상을 골라내 영상 처리에 이용할 수 있다는 뜻이다. 아래의 표를 보면 색에 해당하는 Hue 값을 얻을 수 있다. 

 

 

 

출처 : https://www.rapidtables.org/ko/convert/color/rgb-to-hsv.html

 

 

 

 


④ HSV 활용

 

위에 있는 표를 보고 HSV를 활용하여 추가로 내용을 더 적어보았다. 

 

 

※  ROI(Region Of  Interest)

 

#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

int main(){

	cv::Mat img, ROI;
	img = cv::imread("road2.jpg", cv::IMREAD_COLOR);
	cv::resize(img, img, cv::Size(600,480));

	if(img.empty()){
		std::cout << "No Image" << std::endl;
		return -1;
	}
	
	ROI = img(cv::Rect(0,250,600,230));

	cv::namedWindow("ORIGINAL IMAGE", cv::WINDOW_NORMAL);
	cv::resizeWindow("ORIGINAL IMAGE",600, 480);

	cv::imshow("ORIGINAL IMAGE", ROI);
    
    	cv::waitKey(0);
}

 

 

양쪽에 있는 흰색 차선을 인식해보려고 한다. 그럼 인식하기 위해서 첫째로 할 수 있는 일은 차선 이외에 나머지 부분을 지워버리는 일이다 이를 "ROI(관심영역)을 지정한다." 라고 한다. cv::Rect를 이용하면 이미지 화면의 크기를 편집할 수 있다. 

 

 

 

 

int main(){

	cv::Mat img, ROI;
	img = cv::imread("road2.jpg", cv::IMREAD_COLOR);
	cv::resize(img, img, cv::Size(600,480));

 

Mat 형식의 img, ROI 객체를 만들고 img에 cv::imread로 불러온 이미지를 저장한다.

이미지 사이즈가 너무 커서 cv::resize를 이용하여 이미지 너비와 높이를 600, 480 값으로 재지정했다.

 

 

 

 

 

	if(img.empty()){
		std::cout << "No Image" << std::endl;
		return -1;
	}

 

cv::imread로 이미지를 불러오지 못한 경우에는 오류를 출력할 수 있도록 하였다.

 

 

 

 

	ROI = img(cv::Rect(0,250,600,230));

	cv::namedWindow("ORIGINAL IMAGE", cv::WINDOW_NORMAL);
	cv::resizeWindow("ORIGINAL IMAGE",600, 480);

	cv::imshow("ORIGINAL IMAGE", ROI);

	cv::waitKey(0);   
}

 

 

cv::Rect(시작 점X 값, 시작 점Y 값, 너비 값, 높이 값)

 

 

opencv docs에서 cv::Rect를 읽었는데 머리가 안좋아서 이해를 못하다보니 그림으로 그려가면서 하게되었다. 매개변수 4곳에 무엇이 들어가는지 몰라서 계속 에러가 생겼다.

 

 

 

 

 

 

 

무튼 위와 같은 코드를 작성하면 아래와 같은 화면 원본 이미지의 일정 부분이 잘린 화면이 나온다.

 

 

 

 

 

 

 

 

※  추가 소스코드  

 

#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

int main(){

	cv::Mat img, HSV, ROI;
	img = cv::imread("road2.jpg", cv::IMREAD_COLOR);
	cv::resize(img, img, cv::Size(600,480));

	if(img.empty()){
		std::cout << "No Image" << std::endl;
		return -1;
	}
	
	ROI = img(cv::Rect(0,250,600,230));
	cvtColor(ROI,HSV,cv::COLOR_RGB2HSV);
	
	cv::Scalar lower_white = cv::Scalar(0,0,200);
	cv::Scalar upper_white = cv::Scalar(180,255,255);
	cv::inRange(HSV, lower_white, upper_white, HSV);

	cv::imshow("ORIGINAL IMAGE", ROI);
	cv::imshow("HSV IMAGE", HSV);    
	
	cv::waitKey(0);
}

 

 

ROI 관심영역으로만 픽셀을 줄여놓은 후 그 부분을 HSV 색 공간 모델로 전환하였다. 차선이 흰색이므로 HSV에서의 흰색의 범위를 lower_white, upper_white로 지정해주었고 이를 cv::inRange()로 지정된 범위 만큼의 색만 출력하게끔 했다.

 

 

 

 

만약 중간에 있는 차선이 거슬린다면 허프라인으로 흰색 선을 따고 해당 라인의 X,Y 값을 이용하여 아크탄젠트 값을 구하고 기울기가 90도에 가깝다면 흰색 선으로 인식하지 않는 방법을 사용해도 된다. 이는 필터 효과, 이미지 프로세싱을 함께 다루며 진행하도록 하겠다.

 

 

 

 

 

 

 

 

반응형