본문 바로가기

프로그래밍 강좌/C++ - OpenCV

[OpenCV][C++] Thresholding 이진화 영상 만들기 총정리 (3) - cv::inRange() cv::threshold() cvtColor 범위 binarization

지난번에 이진화에 관련해서 알아봤습니다.

이런 식으로 이진화 처리가 되었습니다.

그런데 이번에는 전경(foreground), 즉 내가 추출하고자 하는 물체의 밝기가 100~150 사이라면 어떻게 할까요?

일단 오늘 강의를 하기 위한 영상을 만들어보겠습니다.

 

일단 256 x 256 사이즈의 도화지를 만들고, 전체 픽셀을 순회하면서 y 값, 즉 가로로 같은 밝기를 가지는 gradient 한 밝기를 가지는 영상을 하나 만들어보겠습니다.

cv::Mat src = cv::Mat(cv::Size(256, 256), CV_8UC1);

for (int y = 0; y < src.rows; ++y) {
    for (int x = 0; x < src.cols; ++x) {
        src.at<uchar>(y, x) = y;
    }
}
 

이 영상을 가지고 함수를 알아보겠습니다.

이전에 cv::threshold() 함수를 가지고 100~150 밝기의 값 255로, 나머지는 0으로 만들어 보면 아래와 같습니다.

int low_th = 100;
int high_th = 150;
int max_val = 255;
cv::Mat bin1, bin2;
cv::Mat rst;
cv::threshold(src, bin1, low_th, max_val, cv::THRESH_BINARY);
cv::threshold(src, bin2, high_th, max_val, cv::THRESH_BINARY_INV);
cv::bitwise_and(bin1, bin2, rst);
 

low_th 인 100 이하의 값을 0으로, 나머지는 255로 한 bin1과

high_th 인 150 이상의 값을 0으로, 나머지는 255로 한 bin2를

cv::bitwise_and() 를 하게 되면 두 영상에서 255인 픽셀만 255가 되고 나머지는 0이 됩니다.

이런 결과가 나오겠죠? 이러한 이진화 방법을 in-range 이진화 방법이라고 하고,

만약 역으로 low_th  high_th 이 값을 0으로, 나머지를 255로 이진화 하는 방법을 out-range 이진화 방법이라고 합니다. 이건 bin1 bin2 cv::bitwise_xor() 하면 되요.

in-range 이진화 방법을 한번에 할 수 있는 방법이 없을까요?

함수 원형은 아래와 같습니다.

 
src
입력 영상, 이진화할 영상
lowerb
낮은 threshold, lower boundary
upperb
높은 threshold, upper boundary
dst
결과 영상

 

소스는 아래와 같습니다. 간단하죠?

cv::inRange(src, low_th, high_th, dst);
 

두가지 방법을 좀 더 세밀하게 분석해 보겠습니다.

cv::threshold(src, bin1, low_th, max_val, cv::THRESH_BINARY)의 결과는

100 위치인 밝기 100이 0 값을 갖고 있습니다.

그리고 cv::threshold(src, bin2, high_th, max_val, cv::THRESH_BINARY_INV) cv::THRESH_BINARY를 역으로 취한 것으로 150이 255 값을 갖고 있습니다.

즉, cv::threshold( , , , cv::THRESH_BINARY)는 th 값 초과 하는 값부터 255로 변환합니다.

기존 강의의 표를 보시면 = 이 없죠?

하지만 inRange() 함수는 th 값 이상인 값부터 255로 변환하네요. 즉 inRange 함수의 255값이 되는 범위는 thL ≤ px ≤ thH 입니다. 위아래 경계 포함해서 white로 변환됩니다.

아참, 아직까지 opencv에서 outRange()란 함수는 제공하지 않는 것 같습니다.(참고하세요~!)

꿀팁 대 방출~!!

이번에도 총정리인데, 이렇게 끝나면 아쉽죠?

cv::inRange() 함수는 컬러 영상에서 특정 컬러값을 가지는 픽셀을 찾을 때 많이 사용됩니다.

아래와 같은 손 영상이 있으면

카메라와 조명 조건에 따라 약간씩은 달라질 수 있지만 색상 값은 매우 unique 하기 때문에 손과 같은 물체를 추출하는데 사용될 수 있습니다.

컬러 영상을 읽어오고 cv::cvtColor() 함수를 이용하여 HSV 영상으로 변경합니다.

그리고 low_th high_th를 설정하여 inRange() 함수를 사용하면,

손 영역을 아주 쉽게 추출할 수 있습니다. 이 결과로 gesture 인식 등에 사용할 수도 있습니다.

전체 소스는 아래와 같습니다.

#include <iostream>
#include "opencv2/opencv.hpp"

int main()
{
    cv::Mat src = cv::imread("hand.jpg", cv::IMREAD_UNCHANGED);

    cv::Mat hsv;
    cv::cvtColor(src, hsv, cv::COLOR_BGR2HSV);
    cv::Scalar low_th(0, 50, 100);
    cv::Scalar high_th(20, 200, 250);
    cv::Mat dst;

    cv::inRange(hsv, low_th, high_th, dst);

    return 0;
}
 

inRange()를 이용한 손 영역 추출 결과는 아래와 같습니다.

오늘 명심해야 할 사항은,



cv::threshold() 는 임계값 초과인 값을 255로 변환하는 반면에
cv::inRange()는 임계값을 포함하는 범위를 255로 변환한다!