안녕하세요. 지난 포스팅의 디지털 영상 처리 - 디지털 영상 기초 2를 끝으로 이제부터는 실질적인 디지털 영상 처리 기법에 대해서 알아보도록 하겠습니다. 코드는 아래 링크 참조해주세요!
github.com/skawngus1111/DIP.git
1. 소개
가장 먼저, 공간 도메인(spatial domain)이란 개념을 알아야 합니다. 공간 도메인은 영상 그 자체에 바로 어떤 연산을 적용하는 도메인을 의미합니다. 아니? 그렇다면 영상에 연산을 바로 적용하지 않는 방법이 있을까요? 네, 그 방법은 나중에 설명드릴 주파수 공간(frequency domain)에서의 연산을 적용합니다. 따라서, 제가 따로 언급하지 않는 이상 앞으로 설명할 모든 영상 처리 기법은 공간 도메인에서 이루어진다는 점에 유의하시길 바랍니다. 또는, 특별히 제가 공간 도메인에서의 연산을 강조할 필요가 있다면 공간 도메인 처리(spatial domain processing)이라고 따로 언급하도록 하겠습니다.
앞서 말씀드렸다시피 공간 도메인 처리는 영상에 직접적으로 어떤 연산을 적용하는 것이라고 하였습니다. 지난 포스팅에서와 마찬가지로 영상을 $f(x, y)$라고 정의하도록 하겠습니다. 그렇다면 어떤 연산 $T [\cdot]$에 의해서 생성되는 새로운 영상은 $g(x, y)$라고 하면 두 영상은 아래의 관계를 가지게 됩니다.
$$g(x, y) = T [f(x, y)]$$
여기서 $f(x, y)$를 입력 영상(input image), $g(x, y)$를 출력 영상(output image), 그리고 $T [\cdot]$을 $(x, y)$의 이웃 픽셀들에 대해서 입력 영상 $f$에 적용되는 연산이라고 정의할 수 있습니다. 여기서 이웃 픽셀들에 대한 정의는 지난 포스팅의 $N_{8}((x, y))$로 정의하도록 하겠습니다. 이를 좀 더 간단하게 표현하기 위해서 $S^{3}_{x, y} = N_{8}((x, y))$라고 하도록 하죠. 여기서 $(x, y)$는 영상 안에 있는 임의의 픽셀 좌표를 의미하고 $S$ 윗 첨자의 $3$은 $(x, y)$의 이웃 픽셀이 $3 \times 3$의 크기를 가지고 있다는 것을 의미합니다. 이 모든 것을 하나의 그림으로 표현한 것이 아래 왼쪽에 나타나 있습니다.
저희는 왼쪽의 그림을 통해서 알 수 있는 것은 연산 $T [\cdot]$은 $g(x, y)$를 얻기 위해서 $S^{3}_{x, y}$를 원점부터 시작하여 대각선 방향의 마지막 픽셀에까지 하나하나 연산을 해야 한다는 점입니다. 그러므로, 좀 더 정확한 표현은 임의의 픽셀 $(x_{0}, y_{0})$에 대해서 $g(x_{0}, y_{0}) = T [S^{3}_{x_{0}, y_{0}}]$일 것입니다. 하지만 저희는 편의를 위해서 그냥 $g(x, y) = T [f(x, y)]$라고 쓰는 것이죠.
또한, 기본적으로 $S^{3}_{x, y}$는 전체 영상인 $f(x, y)$에 비해서 매우 작은 크기를 보여주고 있습니다.
하지만, 이 과정에서 하나의 이슈가 발생합니다. 만약, $(x_{0}, y_{0})$가 $f(x, y)$의 경계 지점, 즉 모서리나 꼭짓점에 위치하면 어떻게 연산을 해야 할까요? 애초에 경계 지점에서는 $N_{8}((x_{0}, y_{0}))$을 잡을 수가 없으니 말이죠. 이에 대해서는 2가지 해결책이 있습니다.
- $T$를 구현할 때 새로운 로직을 추가하여 만약 $(x_{0}, y_{0})$가 경계 지점에 있다면 영상이 정의된 영역만 연산을 할 수 있도록 외부 영역에 대한 연산을 스킵하는 방법
- 영상 주변에 적당한 크기의 아무런 의미 없는 값(0)을 둘러쌓는 방법(padding)
이 중에서 딥 러닝에서 자주 쓰는 방법은 두 번째, 패딩 기법입니다. 실제로 이러한 연산은 컨볼루션 연산이라고 부르는 데, 이 연산을 패딩 없이 지속적으로 계속 반복하다 보면 기존의 영상에 비해서 출력 영상이 너무 작아지는 현상이 존재합니다. 따라서 패딩 기법은 연산에 대한 로직을 간단하게 만들 뿐만 아니라 출력 영상이 너무 작아지는 것을 방지하는 방법이기 때문에 자줄 활용됩니다.
지금까지 언급한 것과 같이 특정 크기 $k$에 해당하는 이웃 픽셀 $S^{k}_{x, y}$을 이용한 연산을 영상 필터링(spatial filtering)이라고 부르면 이때 사용된 연산 $T [\cdot]$을 공간 마스크(spatial mask), 공간 커널(spatial kernel), 공간 윈도우(spatial window)라고 말합니다. 이때, $k = 1$이라면 이 필터링은 이웃에 대한 연산을 수행하지 않기 때문에 점 처리(point processing)이라고 합니다.
2. 변환 함수 정의
그렇다면, 이제 남은 것은 $T$를 정의하는 것입니다. 다시 한번 말씀드리지만 디지털 영상 처리는 "문제 지향적(problem-oriented)"이기 때문에 특정 문제를 풀기 위해서 다양한 방법을 적용합니다. 대표적인 변환 함수들에 대해서 알아보도록 하겠습니다. 이를 하나로 요약한 그림은 아래와 같습니다.
1). 영상 반전(image negative)
주어진 영상의 픽셀값이 $[0, L-1]$ 사이에서 정의되어있다고 가정하겠습니다. 이때, 영상 반전은 단순하게 최대 픽셀 값 $L-1$로부터 입력 영상을 뺀 것과 동일합니다. 이를 수식적으로 표현하면 아래와 같습니다.
$$s = T(r) = L - 1 - r$$
이를 통해서 위와 같은 영상을 얻을 수 있습니다. 왼쪽 영상에 비해서 오른쪽 영상에서 조직의 위치를 더 강조하고 있는 것을 볼 수 있습니다. 사실 이와 같은 변환은 조직 위치의 강조도 있지만 여러분들이 프린트를 생각해보시면 됩니다. 프린트할 때 검은색이 대부분이라면 프린터기는 기본적으로 검은색 색상을 대부분 사용해야 합니다. 그러면 잉크가 더 빨리 떨어지겠죠? 그다음으로는 검은색은 인쇄된 뒤 다른 색에 쉽게 침범할 수 있기 때문에 모니터로 보는 결과와 많이 달라집니다.
실제로 코드도 아주 단순해집니다.
function [output] = image_negative(input, L)
output = L - 1 - input;
end
먼저 위의 함수를 구현하면 됩니다. input은 입력 영상, L은 입력 영사이 가지는 최대 밝기를 의미합니다. 저는 예제로 uint8 타입의 영상을 사용했기 때문에 $L = 255$이 됩니다. 실제로 결과를 보도록 하겠습니다. 사진은 구글에서 제가 좋아하는 아인슈타인 사진을 이용하였습니다.
당연한 결과지만 왼쪽 영상에서 밝았던 영역은 검은색으로 변하고 어두웠던 영역은 밝은색으로 변하는 것을 볼 수 있습니다. 좋은 결과를 얻은 것 같군요.
2). 로그 변환(log transform)
먼저 상단의 로그 함수 그림부터 보도록 하겠습니다. 만약, 입력 영상의 밝기가 $[0, \frac{L}{4}]$라고 가정하겠습니다. 그러면 출력 영상의 밝기는 $[0, \frac {3L}{4}]$로 밝기 범위가 더 늘어난 것을 볼 수 있습니다. 다음으로 입력 영상의 밝기가 $[\frac {3L}{4}, L-1]$이라고 하면 출력 영상의 밝기의 범위는 더 줄어들게 됩니다. 이것은 어떤 의미를 지니는 걸까요? $[0, \frac {L}{4}]$는 대체적으로 어두운 영상 영역을 의미합니다. 하지만 로그 변환을 통해서 밝기 범위가 늘어남으로써 어두운 영상이 입력 영상에 비해서 비교적 밝아질 것으로 예상할 수 있습니다. 그에 반에 원래 밝은 영상은 로그의 성질로 인해서 큰 밝기 변화가 일어나지는 않다는 것을 볼 수 있습니다. 이를 수식적으로 아래와 같이 표현합니다.
$$s = c\log{\left(1 + r\right)}$$
여기서 $c$는 새롭게 추가된 파라미터로 이를 통해서 로그 변환의 정도를 조절합니다. 이 변환은 기본적으로 어두운 영상을 밝게 해주는 효과가 있기 때문에 실제로 자주 사용되는 변환 기법입니다. 예를 들어서 나중에 알아볼 푸리에 변환 스펙트럼의 경우에는 원본 영상이 너무 어두워서 로그 변환을 적절하게 적용하여 밝게 만들어줍니다. 아래의 예시입니다.
물론 이를 간단하게 코드로도 구현할 수 있습니다.
function [output] = image_log(input, c)
output = c * log(1 + double(input)/255);
end
기본적으로 매트랩의 로그 함수는 입력값으로 double/float만 받기 때문에 double로 캐스팅을 한 뒤 스케일링을 위해서 255로 나누어주었습니다.
위 그림은 $c = 1$로 했을 때의 결과입니다. 전체적으로 약간 어두워진듯한 느낌이 드는군요.
다음은 $c$를 1, 0.5, 2로 지정한 결과입니다. $c$가 작아질 수록 점점 어두워지고 $c$가 커질수록 점점 밝아지는 결과를 얻을 수 있습니다.
3). 감마 변환(Gamma transform)
감마 변환은 전체적으로 로그 변환가 거의 유사합니다. 아래에 정의된 감마값에 따라서 더 다양한 변환 결과를 얻을 수 있습니다.
$$s = cr^{\gamma}$$
애초에 상단의 그림을 보시면 $\gamma < 1$인 지점은 로그 변환과 마찬가지로 어두운 영상을 전체적으로 밝게 만들어주는 효과를 보여줍니다. 하지만 그와 반대로 $\gamma > 1$인 지점은 로그 변환과는 반대로 어두운 영상은 더 어둡고, 밝은 영상은 더 밝게 만들어주는 효과를 보여줍니다. 물론 $\gamma = 1$인 경우 그냥 원본 영상과 동일한 결과를 얻을 수 있습니다.
위와 같은 감마 변환은 기본적으로 모니터와 같은 영상 출력 장치에서 사용될 수 있습니다. 아래 그림과 같이 실제 영상을 모니터로 보면 밝기가 왜곡이 발생하는 데, 이를 보정하는 역할을 감마 변환이 맡아줍니다.
또한, 아래의 영상과 같이 객체 간 대비가 뚜렷하지 않은 경우에도 감마 변환을 적용하면 훨씬 깔끔한 영상을 얻을 수 있습니다. 상단의 오른쪽 영상은 $\gamma=3$, 하단의 왼쪽 영상은 $\gamma=4$, 하단의 오른쪽 영상은 $\gamma=5$로 정하였습니다.($c = 1$로 고정하였습니다.) 이 경우 원래 영상이 전체적으로 너무 밝아서 객체끼리 구별이 잘 가지 않기 때문에 저희는 $\gamma > 1$로 정하여 전체적으로 어둡게 만들어주는 과정이 필요합니다. 이 경우에는 $\gamma=4$일 때 전체적으로 잘 구별이 되는 것을 볼 수 있습니다.
코드 구현도 간단합니다!!
function [output] = image_gamma(input, gamma, c)
output = im2uint8(c*im2double(input).^(1/gamma));
end
다만 실질적 코드 구현에서는 $\gamma$를 그대로 사용하는 것이 아니라 $\frac {1}{\gamma}$를 사용한다는 점이 다릅니다. 그 결과 아래의 영상을 얻을 수 있습니다.
'image processing' 카테고리의 다른 글
디지털 영상 처리 - 히스토그램 평활화(histogram equalization) (14) | 2021.02.12 |
---|---|
디지털 영상 처리 - 영상 히스토그램(Image Histogram) (3) | 2021.02.02 |
디지털 영상 처리 - 디지털 영상 기초 2 (0) | 2021.01.14 |
디지털 영상 처리 - 디지털 영상 기초 1 (0) | 2021.01.08 |
디지털 영상 처리 - 소개 (0) | 2020.12.25 |