오늘은 이미지 그레디언트(gradient)를 이용해서 엣지 정보를 검출하는 것에 대해 정리하겠다. 디지털 이미지를 처리할 때 왜 엣지 정보가 중요한지는 이전 포스팅에서 다뤘었다. => https://bskyvision.com/132


사람의 시각시스템은 우리가 보는 장면 중에서 변화가 있는 부분을 중점적으로 처리한다. 이미지 내에서 변화가 큰 부분은 바로 엣지(가장자리)들이다. 그렇기 때문에 디지털 이미지를 처리할 때 엣지 정보를 중요하게 다뤄야하는 것이다. 



▶ 이미지 그레디언트


우선 그레디언트는 '기울기, 증감, 변화도'라는 뜻을 지닌다. 한마디로 변화하는 정도를 나타내는 단어이다. 이미지 내에서 픽셀값의 변화가 큰 부분은 일반적으로 엣지 및 코너 부분일 것이다. 반면 평탄(flat)한 부분은 변화가 적을 것이다. 그림1을 참고하자. 


그림1. 엣지, 코너, 평탄 부분 설명.


변화하는 정도는 수학적으로 미분과 관련이 있다. 이미지는 2차원이기 때문에 수직 방향과 수평 방향으로 미분해주면, 수직 방향으로 어떻게 변화하는지, 또한 수평 방향으로 어떻게 변화하는지를 알 수 있다. 


이미지 I의 그레디언트는 아래와 같이 계산한다. 


...(수식1: 이미지 그레디언트)


여기서 는 수평 방향으로의 변화,  는 수직 방향으로의 변화를 나타낸다. 따라서, 이 벡터는 (x, y)에서 수평 방향으로, 수직 방향으로 얼마나 변화하는지를 알려준다. 


그레디언트 벡터의 크기(magnitude)와 방향(direction)은 각각 다음과 같다.


...(수식2: 그레디언트 크기)


...(수식3: 그레디언트 방향)


그럼 와 를 어떻게 구할까? 여러 방법이 있지만 그중 가장 간단한 것은 1차 미분 엣지 검출을 위해 고안된 마스크로 이미지를 컨볼루션해주는 것이다. 마스크 종류에는 Roberts 마스크, Prewitt 마스크, Sobel 마스크 등이 있다. 그중 Prewitt 마스크와 Sobel 마스크는 그림2와 같다. 


그림2. Prewitt 마스크와 Sobel 마스크.


대각 엣지 검출 마스크를 사용하면 대각 엣지도 검출 가능하다. Prewitt 마스크와 Sobel 마스크는 상당히 유사하다. 차이가 있다면 Sobel 마스크는 그레디언트를 구하고자 하는 픽셀 위치에서 가까울수록 더 큰 가중치를 주는 형태이다. Sobel가 Prewitt보다 좀 더 나은 노이즈 억제력을 보인다고 한다[1]. 노이즈가 있는 상황에서 좀 더 엣지를 잘 도출해낸다는 뜻이다. Sobel 엣지에 대한 더 자세한 설명은 링크건 포스팅을 참고하자. => https://bskyvision.com/43



▶ 이미지 그레디언트 산출하기


Sobel 마스크를 이용해서 그레디언트 크기 이미지와 방향 이미지를 산출해보자. 매트랩에서 작성한 코드는 다음과 같다. 


clc, clear, close all


img = imread('DSC03771.JPG'); % 이미지 읽기

img = rgb2gray(img); % RGB 컬러 영상 그레이 영상으로 전환


Sobel_mask_h = [-1 -2 -1; 0 0 0; 1 2 1]; % 수평 엣지 검출용 Sobel 마스크

Sobel_mask_v = [-1 0 1; -2 0 2; -1 0 1]; % 수직 엣지 검출용 Sobel 마스크


img_h_edge = conv2(img, Sobel_mask_h, 'valid'); % 수평 엣지 검출

img_v_edge = conv2(img, Sobel_mask_v, 'valid'); % 수직 엣지 검출


figure, imshow(mat2gray(img_h_edge));

figure, imshow(mat2gray(img_v_edge));


Grad_mag = sqrt(img_h_edge.^2 + img_v_edge.^2); % 그레디언트 크기 이미지

Grad_direc = atan(img_v_edge./img_h_edge); % 그레디언트 방향 이미지


figure, imshow(mat2gray(Grad_mag));

figure, imshow(mat2gray(Grad_direc));


테스트 이미지와 결과 이미지들은 아래와 같다. 


그림3. 그레디언트 이미지.


먼저 그림3(b)를 보면, 수평 엣지들을 주로 도출되었음을 알 수 있다. 그림3(c)에서는 수직 엣지들이 도드라져있다. 그림3(d)는 수식2를 이용해서 만들어진 그레디언트 크기 이미지이다. 이미지 내의 엣지들이 대체적으로 잘 도출되었음을 확인할 수 있다. 그림3(e)는 수식3을 통해 계산된 그레디언트 방향 이미지이다. 



▶ Sobel 엣지와 Prewitt 엣지 비교


[1]에서 Sobel 마스크가 Prewitt 마스크보다 더 나은 노이즈 억제력이 있다고 언급했었다. 실제로 그런지 확인해보려고 한다. additive white Gaussian noise로 손상된 이미지에서 Sobel 엣지와 Prewitt 엣지를 도출해서 비교해보자. 매트랩 코드는 다음과 같이 작성했다.


clc, clear, close all


img = imread('DSC07466.JPG'); % 테스트 이미지 읽기

noise_img = imnoise(img, 'gaussian', 0, 0.01); % 노이즈 첨가


figure, imshow(noise_img);


noise_img = rgb2gray(noise_img); % 노이즈 첨가된 RGB 컬러 영상 그레이 영상으로 전환


sobel_mask_h = [-1 -2 -1; 0 0 0; 1 2 1]; % 수평 엣지 검출용 Sobel 마스크

sobel_mask_v = [-1 0 1; -2 0 2; -1 0 1]; % 수직 엣지 검출용 Sobel 마스크


sobel_h_edge = conv2(noise_img, sobel_mask_h, 'valid'); % 수평 엣지 검출

sobel_v_edge = conv2(noise_img, sobel_mask_v, 'valid'); % 수직 엣지 검출


sobel_edge = sqrt(sobel_h_edge.^2 + sobel_v_edge.^2); % sobel 엣지 이미지(그레디언트 크기)


prewitt_mask_h = [-1 -1 -1; 0 0 0; 1 1 1]; % 수평 엣지 검출용 Prewitt 마스크

prewitt_mask_v = [-1 0 1; -1 0 1; -1 0 1]; % 수직 엣지 검출용 Prewitt 마스크


prewitt_h_edge = conv2(noise_img, prewitt_mask_h, 'valid'); % 수평 엣지 검출

prewitt_v_edge = conv2(noise_img, prewitt_mask_v, 'valid'); % 수직 엣지 검출


prewitt_edge = sqrt(prewitt_h_edge.^2 + prewitt_v_edge.^2); % Prewitt 엣지 이미지(그레디언트 크기)



figure, imshow(mat2gray(sobel_edge))

figure, imshow(mat2gray(prewitt_edge))


노이즈 이미지와 노이즈 이미지의 Sobel 및 Prewitt 엣지 이미지들을 살펴보자. 


그림4. 노이즈 이미지와 Sobel 엣지 및 Prewitt 엣지 이미지.


화이트 가우시안 노이즈를 첨가하고 Sobel 엣지와 Prewitt 엣지를 도출했는데, 둘 중 어떤 것이 더 나은 성능을 보인다고 말하기 어려울 정도로 결과 영상에 큰 차이가 없다. 테스트 이미지를 바꿔보고, 노이즈의 강도를 조절하면서 체크해봤는데도 큰 차이를 느끼지 못했다. 그렇다고 이 결과로 [1]의 내용을 완전히 부정할 수는 없다. 



▶ 글을 마무리하며..


Roberts 연산자, Prewitt 연산자, Sobel 연산자는 1차 미분을 이용한 엣지 검출방법이다. 반면, LoG(Laplacian of Gaussian)은 2차 미분을 이용한 엣지 검출방법이다. 




<참고자료>

[1] Rafael C. Gonzalez, Richard E. Woods, "Digital Image Processing" (3판), Pearson

+ Recent posts

티스토리 툴바