본문 바로가기
코딩/OpenCV

[C++ opencv] HSV 색상 검출하기

by DIYver 2020. 7. 27.

본문 목표

영상처리를 하다보면 특정 물체의 색을 인식해야할 상황이 주어진다.

컴퓨터에서 다룰 수 있는 색 영역은 크게 RGB 와 HSV 가 있는데,

RGB는 컴퓨터에 최적화 되어있고,

HSV는 사람의 눈과 비슷하다.

 

물체의 색을 인식한다면 사람과 같은 방식인 HSV 색 영역을 사용해야 하는데,

Opencv 에서 어떻게 HSV 색채널을 다룰 수 있는지 알아보자.

 

 

키워드 :  inRange( ) ,  bitwise_and( ), HSV

 

 

 

 

 

알아볼 함수 원형

- 이중 임계값 처리 ( inRange )

	Mat img = imread("balls.jpg");
	Mat img_hsv;

	cvtColor(img, img_hsv, COLOR_BGR2HSV);

	Mat yellow_mask, yellow_image;

	Scalar lower_yellow = Scalar(20, 20, 100);
	Scalar upper_yellow = Scalar(32, 255, 255);

	inRange(img_hsv, lower_yellow, upper_yellow, yellow_mask);
	bitwise_and(img, img, yellow_image, yellow_mask);

 

inRange( src , lower_valueupper_valuedst

 

  ○ src : 입력할 이미지 변수

 

  ○ lower_value : threshold 값 1,  임계값 1 로 낮은 값을 입력,

 

  ○ upper_value : threshold 값 2,  임계값 2 로 높은 값을 입력

 

  ○ dst : 필터가 적용되어 저장될 이미지 변수

 

 

이 때, lower_value 와 upper_value 에는 Scalar 값으로 넣어 줘야 한다.

 

Scalar 값은 총 3가지 값으로, 색 채널에따라 순서가 달라진다.

RGB 의 경우에는 R G B 순서로 입력해야하고,

BGR 의 경우 B G R 순서로 입력해야 한다.

당연 HSV 의 경우에는 H S V 순서로 입력해야 한다.

 

inRange 함수는 이미 앞에서 다룬 적 있는 내용이므로, 관심있다면 참고하면 좋을 듯 하다.

https://diyver.tistory.com/69

 

 

 

HSV 가 뭔지 알아야 이번 내용이 이해가 된다.

HSV 색 채널은 사람의 색상 인식 방식과 같다고 앞에서 언급했다.

그러면 왜 HSV 색 채널을 사용하는지 알아야 이번 내용이 이해가 된다.

잠깐동안 왜 HSV 색 채널을 사용하는지 고민하는 시간을 가져보자.

 

 

사람은 어두운 곳에 있는 빨간색 공을 보고 빨간색이라고 인식한다.

그러나 RGB 색 채널을 사용하는 컴퓨터는 알기 어렵다.

어두운 곳에 있는 빨간색은 R G B 값 모두 부족하지만 그나마 R 값이 살짝 높다고 인식할 것이다.

매우 부정확한 방법이 될 수 있다.

그래서 컴퓨터에서 HSV 색 채널을 사용하는 것이다.

 

 

HSV 는 Hue 색상, Saturation 채도, Value 밝기(명도) 의 정보를 담은 색 채널이다.

 

 

위의 색 도형은 HSV 색 채널을 가시적으로 나타낸 것이다.

Hue 에 따라서 색상은 정해지게 되고,

Saturation 에 따라서 색상의 농도가 정해진다.

마지막으로 Value 에 따라서 색상의 밝기가 정해진다.

 

색상이 있다면 어두운데에 있던지, 밝은데에 있던지 Hue 값의 범위가 있으므로 감지가 가능한 것이다.

대신 어느정도 채도와 밝기를 띄고 있어야 인식이 가능하다.

 

 

포토샵 부터 다른 이미지 다루는 프로그램은 Hue 값을 0~360 범위로 사용한다.

그 이유는 위의 그림을 보면 알 수 있듯이, 원기둥이기 때문이다.

 

Hue 값에 따른 색상표를 제작해봤다. 대략적인 수치들을 기입해놓았으므로

필요한 수치는 위에서 참고하면 된다.

 

 

간혹 다르게 사용하는 경우가 있는데, 이는 데이터 값 때문이다.

보통 8bit 를 데이터 값으로 많이 사용하는데, 0~255 까지만 사용할 수 있어서 한계가 있다.

당장 '그림판'만 해도 HSV 색채널에는 0~240 까지만 사용한다.(RGB는 255 까지 사용)

OpenCV 에서는 Hue 값은 0~179 까지 사용할 수 있다. (180 은 0과 같다.)

Saturation 과 Value 값은 0~255 까지 사용할 수 있다.

 

따라서 원하는 색상 검출을 좀 정교하게 하고 싶은 경우,

그림판의 Hue 값을 참고하되 0.75 배를 해서 값을 사용해야 한다.

예를 들면, 노란색의 Hue 값이 그림판 상에서 39를 나타내고 있다면,

OpenCV에서는 0.75 배를 하고 반올림 한 29 를 사용해야한다.

 

이외에도 무료로 Hue 값을 알아볼 수 있는 무료 사이트들이 많으므로, 찾아서 이용하면 된다.

 

 

 

 

 

따라서 이러한 HSV 색 채널에 대한 특성을 이용하여,

OpenCV 의 기본 함수인 inRange 함수를 통해 해당 범위의 색을 골라낼 수 있다.

첫 번째의 threshold 값에는 낮은 값을 넣어야 한다.

두 번째의 threshold 값에는 높은 값을 넣어야 한다.

inRange( ) 함수는 threshold1 값 이상과 threshold2 값 이하의 범위만 밝은 값이 되고, 

그 외의 범위는 검은색이 된다.

한마디로 이진화가 되는 것이다.

 

그런데 이미지에서 색상 추출한 부분을 컬러로 확인하고 싶은 경우에는

bitwise_and( ) 함수를 사용해야 한다.

bit 연산을 하는데, & 연산을 하는 것이다.

여기에는 위에서 다룬 inRange( ) 의 결과물이 mask로 사용된다.

검은색은 데이터로 나타내자면 0이 되고,

검은색 이외의 데이터는 True 값이므로,

검은색 부분은 & 연산으로 검은색이 되고,

검은색이 아닌부분은 원본의 색을 띄게 된다.

 

 

 

 

 

 

 

 

코드 테스트 결과

- CODE

#include <opencv2/opencv.hpp>


using namespace cv;
using namespace std;

int main(int ac, char** av)
{
	Mat img = imread("balls.jpg");
	Mat img_hsv;

	cvtColor(img, img_hsv, COLOR_BGR2HSV);

	Mat yellow_mask, yellow_image;

	Scalar lower_yellow = Scalar(20, 20, 100);
	Scalar upper_yellow = Scalar(32, 255, 255);

	inRange(img_hsv, lower_yellow, upper_yellow, yellow_mask);
	bitwise_and(img, img, yellow_image, yellow_mask);
	
	imshow("img", img);
	imshow("yellow_image", yellow_image);
	imshow("yellow_mask", yellow_mask);
	waitKey(0);
	return 0;
}

 

- RESULT

사용한 원본이미지는 다양한 색상의 공들이 있는 사진이다.

balls.jpg
0.11MB

 

 

원본 파일은 BGR 채널이므로, HSV 채널로 변환시켜줘야 한다.

노란색을 검출할 것이므로,

yellow_mask 와 yellow_image 라는 이미지 변수를 만들어 준다.

 

그리고 inRange( ) 에서 사용할 값 두개를 미리 선언해준다.

lower_yellow 와 upper_yellow 라고 이름짓고, Hue 값은 20~32 범위로 설정하였다.

채도와 명도는 100~255 로 하면 적당하지만, 

사용자의 입맛에 맞게끔 바꿔줘야 더 좋은 결과가 나온다.

 

inRange( ) 를 통해서 이진화된 이미지를 yellow_mask 에 저장하고,

bitwise_and( ) 함수를 이용하여, 원본이미지에 yellow_mask를 씌워서 bit 연산을 하고, yellow_image 에 저장한다.

 

 

 

inRange( ) 함수를 이용하여 노란색 범위만 이중 임계값으로 처리한 이진화 이미지이다.

노란색 부분만 하얗게 변한 것을 볼 수 있다. 

그 외의 부분들은 검정색을 띄고있다.

 

 

bitwise_and( ) 함수의 결과 이미지이다.

bit 연산을 통해 원본 이미지에서 yellow_mask 와 연산한 것이다.

노란색만 살아남고, 결과에서도 노란색만 출력이 가능하다.

 

시각적인 결과가 필요하다면 bitwise_and( ) 함수를 사용해야 한다.

 

 

 

 

추가로 반사되어 노란 빛이 나는 부분이 있는데, 없애는 방법이 있다.

바로 이전에 배웠던 Opening 기법이다.

 

침식시키고 확장시키는 방법으로

erode 후에 dilate 를 적용하면 된다.

 

 

확실히 깔끔해 진 것을 볼 수 있다.

 

이처럼 erode 와 dilate 기법은 이런 자잘한 노이즈를 없앨 때, 유용하게 사용된다.

 

 

 

 

 

결론.

inRange( ) 함수를 이용하여 HSV 색채널을 이용한 색상 검출이 가능하다.

 

bitwise_and( ) 함수를 이용하여 색상 검출 된 영역을 컬러로 표현할 수 있다.

 

erode 와 dilate 를 이용하여 검출된 이미지에서 노이즈를 제거할 수 있다.

 

 

 

 

 

 

 

 

 

도움이 되었거나, 문제가 있는 경우 댓글로 알려주세요~!

감사의 댓글은 작성자에게 큰 힘이 됩니다 ^^

댓글