2013년 1월 26일 토요일

OpenCV #7-1 Example (캐니 연산자로 영상 외곽선 감지)

OpenCV #7 선, 외곽선, 컴포넌트 추출

 - 영상의 내용을 기반으로 분석하기 전 영상을 구성하는 화소 집단에서의 특징을 추출할 필요가 있음.
 - 기본적인 영상 요소 : 선, 외곽선, 덩어리 등.
 - 7장은 영상에서 중요한 특징의 일부를 추출하는 방법을 알려줌.

 - 6장 : 영상의 에지를 감지하는 방법을 설명.
 - 기울기 크기로 경계값을 적용함을 설명, 영상의 주요 에지가 있는 이진 맵을 얻음.
 - 에지는 영상 요소를 기술하기 때문에 중요한 시각 정보를 가짐. (객체 인식에 사용)

 - 그러나 간단한 이진 에지 맵은 두 가지 주요 결점을 가짐.
 - 첫째, 에지는 불필요한 두께를 감지한다. 객체의 정확한 지역화에 제한이 걸려 수행하지 못함을 의미.
 - 둘째, 영상의 모든 중요한 에지를 감지하기 위해 충분히 낮은 값과, 너무 낮은 의미 없는 에지를 포함하지 않는 높은 경계값을 찾기 어렵다.
 - 이러한 문제점을 캐니(Canny) 알고리즘으로 해결

 - 캐니 알고리즘은 cv::Canny 함수로 구현

  • Example
#include <iostream>

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

int main()
{
cv::Mat image= cv::imread("road.jpg", 0);
cv::namedWindow("Original Image");
cv::imshow("Original Image",image);

// 케니 알고리즘 적용
cv::Mat contours;
cv::Canny(image, // 그레이레벨 영상
contours, // 결과 외곽선
125, // 낮은 경계값
350); // 높은 경계값

// 넌제로 화소로 외곽선을 표현하므로 흑백 값을 반전
cv::Mat contoursInv; // 반전 영상
cv::threshold(contours, contoursInv, 128, 255, cv::THRESH_BINARY_INV);
// 밝기 값이 128보다 작으면 255가 되도록 설정

cv::namedWindow("Canny Contours");
cv::imshow("Canny Contours", contoursInv);

cv::waitKey(0);

return 0;
}

  • Result

  • 예제 분석
 - 캐니 연산자는 소벨 연산자에 기반을 두지만 다른 기울기 연산자 사용 가능.
 - 점이 외곽선에 속하는지 조사하기 위한 낮고 높은 경계값인 다른 두 경계값을 사용 하는 것이 포인트.

 - 소스
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

int main()
{
cv::Mat image= cv::imread("road.jpg", 0);

cv::Mat sobelX, sobelY;
cv::Sobel(image, sobelX, CV_8U, 1, 0, 3, 0.4, 128); // 수평 필터 호출
cv::Sobel(image, sobelY, CV_8U, 0, 1, 3, 0.4, 128); // 수직 필터 호출
// 0값은 128인 그레이레벨에 해당
// 음수 값은 어두운 화소로 표현, 양수 값은 밝은 값을 표현

// 소벨 커널은 양수와 음수 값을 가짐
// 소벨 필터의 결과는 일반적으로 16비트 부호 있는 정수영상(CV_16S)에서 계산
// 소벨 놈 계산 (수직과 수평 두 결과의 소벨 필터 놈을 얻기 위해 조합)
cv::Sobel(image, sobelX, CV_16S, 1, 0);
cv::Sobel(image, sobelY, CV_16S, 0, 1);
cv::Mat sobel;
sobel = abs(sobelX)+abs(sobelY); // L1 놈 계산

// 소벨 놈은 0인 값을 흰색으로, 높은 값을 어두운 회색 음영으로 할당한 영상을 얻기 위해
// converTo 메소드의 부가적인 재스케일링 파라미터를 사용
double sobmin, sobmax;
cv::minMaxLoc(sobel, &sobmin, &sobmax); // 소벨 최댓값 찾기
// sobelImage = -alpha*sobel + 255 // 8비트 영상 변환
cv::Mat sobelImage;
sobel.convertTo(sobelImage, CV_8U, -255./sobmax, 255);

// 영상의 경계값을 처리
cv::Mat sobelThresholded;
cv::threshold(sobelImage, sobelThresholded, 225, 255, cv::THRESH_BINARY);
// 소벨 놈에 대한 경계값 적용(낮은 경계값)

cv::namedWindow("Sobel (low threshold)");
cv::imshow("Sobel (low threshold)",sobelThresholded);

cv::threshold(sobelImage, sobelThresholded, 128, 255, cv::THRESH_BINARY);
// 소벨 놈에 대한 경계값 적용(높은 경계값)

cv::namedWindow("Sobel (high threshold)");
cv::imshow("Sobel (high threshold)",sobelThresholded);

cv::waitKey(0);

return 0;
}


 - 결과


 - 낮은 경계값은 중요한 영상 외곽선에 속하는 것으로 간주하는 모든 에지 화소를 포함하는 방법으로 선택해야 한다.
 - 예제에서 지정한 낮은 경계값을 사용하고, 소벨 연산자 결과에 적용하면 다음 같은 이진 맵을 얻는다.
 - 에지를 매우 잘 정의 하고 있지만, 허용 경계값을 사용했기 때문에 이상적으로 필요한 것보다 더 많은 에지를 감지.
 - 두 번째 경계값의 역할은 모든 중요한 외곽선에 속하는 에지를 정의. (영외로 간주되는 모든 에지는 제외)

 - 소벨 에지맵에서 높은 경계값의 결과는 다음과 같다.
 - 깨진 에지가 들어간 영상을 갖고 있지만, 눈에 보이는 것들은 확실히 장면 내의 중요한 외곽선에 속해 있음.
 - 캐니 알고리즘은 외곽선의 '최적' 맵을 생산하기 위한 두 가지 에지 맵을 조합한다.

 - 에지의 연속적인 경로가 존재하는 낮은 경계값 에지맵의 에지 점만 유지하고, 높은 경계값 에지 맵에 속한 에지에 대한 에지 점을 연결하게 처리.
 - 즉, 낮은 경계값 맵에서 모든 고립된 에지 점이 연결된 부분을 제거하는 동안 경계값 맵의 모든 에지 점을 유지.
 - 적절한 경계값을 지정했기 때문에 얻게 되는 좋은 품질의 외곽선을 갖기 위한 좋은 타협으로 구성한 해결책이다.
 - 이 전략은 이중 경계화라고 하며, 이진 맵을 얻기 위해 두 경계값을 사용함에 기반을 두며, 경계화 작업으로 이진 맵을 반드시 얻어야 하는 어떤 상황에서 사용할 수 있음.

 - 더불어 캐니 알고리즘은 이진 맵의 품질을 개선하는 추가 전략으로 사용.
 - 이전 이중 경계화 애플리케이션에서 기울기 크기가 에지 방향에서 최대가 아닌 모든 에지 점을 제거. (기울기 방향이 항상 에지에 수직임을 상기)
 - 즉, 이 방향으로 기울기의 지역 최대치는 외곽선의 최대 강도에 있는 점에 대응. ( 캐니 외곽선 맵에서 얇은 에지를 얻은 이유)

  • 참고문헌 : OpenCV 2 Computer Vision Application Programming Cookbook