[OpenCV] 해리스(Harris) 코너 검출

[OpenCV] 해리스(Harris) 코너 검출

이번 글에서는 코너 검출에 대한 개념과 대해 알아보겠습니다.

특징 - feature

우선 영상에서 특징에 대해 알아보겠습니다. 특징(feature)은 영상에서 검출할 수 있는 유용한 정보를 말합니다. 유용한 정보라는 것은 예를들어 코너, 히스토그램, 에지, 직선 등등이 있습니다. 코너의 특징은 에지의 방향이 급격하게 변하는 부분으로 사각형에서 꼭지점, 바늘의 끝 부분 등 뾰족하게 튀어나온 부분을 의미합니다.

예를들어 아래 그림에서 빨간 부분이 코너가 될 수 있습니다. 

코너처럼 한 점의 형태로 표현할 수 있는 특징을 특징점(feature point)라고 합니다.

해리스 코너 검출

그중 해리스(Harris)가 개발한 코너 검출기에 대해 알아 보겠습니다.

해리스 코너 검출기에서 사용된 수식은 아래와 같습니다.

위 수식은 아래 그림처럼 특정 위치(x, y)에서 x, y축 방향만큼 이동하여 변화량을 계산하는 방법입니다.

영상 위 특정 위치 (x,y)에 윈도우를 설정하여 x축 방향으로, y축 방향으로 이동 시킵니다.

영상에서 픽셀값이 모두 균일하다면 변화량이 별로 없을 것이고, 직선이라면 픽셀 변화가 한쪽 방향으로, 코너라면 모든 방향으로 픽셀 변화가 생길 겁니다.

이렇게 픽셀 변화를 감지하여 코너를 알아낼 수 있습니다.

위의 영상 변화량 공식을 테일러 근사화를 하면 아래와 같이 수식이 변경되고

결론적으로 아래와 같이 됩니다. Det은 행렬식, Tr은 대각합입니다.

행렬 M은 다음과 같이 정의됩니다.

코드

OpenCV에서는 cornerHarris 함수를 지원합니다.

@param src 입력 영상
@param dst 해리스 코너 응답 함수값 저장할 행렬
@param blockSize 윈도우 크기
@param ksize 소벨 연산자를 위한 커널 크기
@param k 해리스 코너 검출 상수
@param borderType 가장자리 픽셀 확장 방식
 */
void cornerHarris( InputArray src, OutputArray dst, int blockSize, int ksize, double k, int borderType = BORDER_DEFAULT );

코드로 표현하면 아래와 같습니다.

Mat src = imread("building.jpg", IMREAD_COLOR);
if (src.empty())
{
	cerr << "image open error" << endl;
	return -1;
}

Mat src_gray;
cvtColor(src, src_gray, COLOR_BGR2GRAY);
if (src_gray.empty())
{
	cerr << "image convert error" << endl;
	return -1;
}

Mat harris;
cornerHarris(src_gray, harris, 3, 3, 0.04);

Mat harris_norm;
normalize(harris, harris_norm, 0, 255, NORM_MINMAX, CV_8U);

Mat dst;
cvtColor(src_gray, dst, COLOR_GRAY2BGR);


for (int iRow = 1 ; iRow < harris.rows - 1 ; iRow++)
{
	for (int iCol = 1 ; iCol < harris.cols - 1 ; iCol++)
	{
		if (harris_norm.at<uchar>(iRow, iCol) > 120)
		{
			if (harris.at<float>(iRow, iCol) > harris.at<float>(iRow-1, iCol) &&
				harris.at<float>(iRow, iCol) > harris.at<float>(iRow+1, iCol) &&
				harris.at<float>(iRow, iCol) > harris.at<float>(iRow, iCol-1) &&
				harris.at<float>(iRow, iCol) > harris.at<float>(iRow, iCol+1))
				circle(dst, Point(iCol, iRow), 5, Scalar(0,0,255), 2);
		}
	}
}

imshow("src", src);
imshow("dst", dst);

waitKey();
destroyAllWindows();

출력 결과 입니다.