Programing/OpenCV

#OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝 - 12

CouqueD'asse 2022. 6. 22. 15:37

9장 에지 검출과 응용

 

9.1.1 미분과 그래디언트

에지(edge)한쪽 방향으로 픽셀 값이 급격하게 바뀌는 부분

1차원 이산함수에서 미분 근사화 방법

1. 전진 차분 : L(x + 1) - L(x) = 자기 자신 바로 앞에 있는 픽셀에서 자기 자신 픽셀 값을 뺀 형태

2. 후진 차분 : L(x) - L(x - 1) = 자기 자신 픽셀에서 바로 뒤에 있는 픽셀 값을 뺀 형태

3. 중앙 차분 : (L(x + 1) - L(x - 1)) / 2 = 자기 자신을 제외하고 바로 앞과 뒤에 있는 픽셀 값을 이용

그래디언트(gradient) : x축 방향 미분과 y축 방향 미분을 한꺼번에 벡터로 표현한 것

그래디언트 벡터의 방향 : 변화 정도가 가장 큰 방향을 나타냄

그래디언트 벡터의 크기 : 변화율 세기를 나타냄

2차원 영상에서 에지를 찾는 기본적인 방법은 그래디언트 크기가 특정 값보다 큰 위치를 찾는 것

임계값(threshold) : 에지 여부를 판단하기 위해 기준이 되는 값

 

9.1.2 마스크 기반 에지 검출

실제 영상에서 미분을 구할 때에는 잡음의 영향을 줄일 수 있도록 좀 더 큰 크기의 마스크를 이용

소벨 필터 마스크 : 가장 널리 사용되고 있는 미분 마스크

장점 : 구현이 간단하고 빠르게 동작

단점 : 그래디언트 크기만을 기준으로 에지 픽셀을 검출하기 때문에 임계값에 민감하고 에지 픽셀이 두껍게 표현됨

void sobel_edge()
{
	Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}

	Mat dx, dy;
	Sobel(src, dx, CV_32FC1, 1, 0);
	Sobel(src, dy, CV_32FC1, 0, 1); // x축 1차 편미분, y축 1차 편미분을 각각 구하여 dx와 dy 행렬에 저장

	Mat fmag, mag;
	magnitude(dx, dy, fmag); // 그래디언트 크기를 계산하여 fmag에 저장
	fmag.convertTo(mag, CV_8UC1); // fmag를 그레이스케일 형식으로 변화하여 mag에 저장

	Mat edge = mag > 150; // 그래디언트 크기 임계값을 150으로 설정하여 에지를 판별

	imshow("src", src);
	imshow("mag", mag);
	imshow("edge", edge);

	waitKey();
	destroyAllWindows();
}

 

9.1.3 캐니 에지 검출기

좋은 에지 검출기의 조건 (캐니의 논문)

1. 정확한 검출 : 에지를 검출하지 못하거나 또는 에지가 아닌데 에지로 검출하는 확률을 최소화

2. 정확한 위치 : 실제 에지의 중심을 찾아야 함

3. 단일 에지 : 하나의 에지는 하나의 점으로 표현

그래디언트 크기와 방향을 모두 고려한 좀 더 정확한 에지 위치를 찾을 수 있음

수행과정

가우시안 필터링 -> 그래디언트 계산 -> 비최대 억제 -> 이중 임계값을 이용한 히스테리스 에지 트래킹

가우시안 필터링 : 영상에서 포함된 잡음을 제거하기 위함

그래디언트 계산 : 가로 세로 방향으로 각각 소벨 필터 마스크를 수행한 후, 그래디언트 크기와 방향을 모두 계산

비최대 억제 : 에지가 두껍게 표현되는 현상 방지

이중 임계값을 이용한 히스테리스 에지 트래킹 : 에지 픽셀이 대체로 상호 연결되어 있다는 점을 이용

Ex) 약한 에지 픽셀이 강한 에지 픽셀과 연결되어 있다면 에지로 판단 반면 강한 에지와 연결되어 있지 않는 약한 에지 픽셀은 에지가 아니라고 판단

void canny_edge()
{
	Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}

	Mat dst1, dst2;
	Canny(src, dst1, 50, 100);
	Canny(src, dst2, 50, 150);

	imshow("src", src);
	imshow("dst1", dst1);
	imshow("dst2", dst2);

	waitKey();
	destroyAllWindows();
}