※ 회선

화소값 각각에 대해 여러가지 연산을 수행하는 화소 기반 처리가 아닌 마스크라 불리는 규정된 영역을 기반으로 연산을 수행하는 것을 공간영역 기반 처리 또는 마스크 기반 처리라고도 한다. 마스크 기반 처리는 마스크 내의 원소값과 공간 영역에 있는 입력 영상의 화소값들을 대응되게 곱하여 출력화소값을 계산한다. 이러한 처리를 모든 입력 화소값에 대해 이동하면서 수행하는 것을 회선이라고 한다. 이 때 입력 영상에 곱해지는 이 마스크를 커널, 윈도우, 필터 등이라고 부른다.


1. 블러링

사진을 뽀샾할 때 적용되는 영상 처리 기술이 블러링이다. 영상을 보통 밝게 수정하면서 약간 흐리게 만들어서 얼굴의 잡티나 기타 좋지 않은 부분들을 가리려는 편집기술이다. 블러링(스무딩)은 영상에서 화소값이 급격하게 변하는 부분들을 감소시켜 점진적으로 변하게 함으로써 영상이 전체적으로 부드러운 느낌이 나게 하는 기술이다.

블러링은 회선을 이용한 필터링으로 구현 가능하다. 마스크를 모든 원소의 값을 같게 구성하고 전체 원소의 합을 1로 해 입력 영상의 밝기가 유지되도록 하면 블러링이 적용된다.

 위 그림은 3*3의 모든 마스크 요소 값이 1/9인 마스크를 적용한 예이다. 보다시피 (2,3)의 픽셀 값이 200으로 주위의 픽셀 값들과 차이가 많이 났다. 마스크 회선 연산 후를 보면 주위 픽셀과 차이가 거의 없는 것을 볼 수 있다. 이와 같은 연산을 모든 픽셀에 대해서 하게 되면 영상이 흐려지는 현상도 발생한다.


2. 샤프닝

블러링은 이웃 화소의 차이를 감소시켜 부드럽게 만드는 것이라면 샤프닝은 출력화소에서 이웃 화소끼리 차이를 크게 해서 날카로운 느낌이 나게 만드는 것이다. 이렇게 해서 영상의 세세한 부분을 강조할 수 있고 경계 부분에서 명암대비가 증가하는 효과를 낼 수 있다.

샤프닝은 마스크의 중심 위치의 요소가 주변 요소와의 값 차이를 크게 만들면 된다. 또한 마스크 원소의 전체 합이 1이 되어야 입력 영상의 밝기 손신이 없어진다. 다음은 샤프닝 마스크의 예이다.


해당 포스트는 "OpenCV로 배우는 영상 처리 및 응용", "C++ API OpenCV 프로그래밍" 책의 내용을 요약한 것이다.



※ 경계값 채우기

이웃을 정의하는 필터 W(x,y)의 크기가 3*3이면, 첫 번째와 마지막 행과 열에서는 이웃 중 일부가 영상 및 행렬을 벗어나게 된다. 따라서 경계에서 벗어나는 이웃을 처리하는 방법이 있어야 한다. 일반적으로는 0으로 채우거나 경계에 있는 값을 확장 복사하여 사용한다.


- void copyMakeBorder(InputArray src, OutputArray dst, int top, int bottom, int left, int right, int borderType, const Scalar& value = Scalar())

: 영상 src를 dst에 복사하되 top, bottom, left, right에 명시된 만큼 각 부분을 확장한다. 확장된 행렬 요소 안에는 borderType에 따라서 value 값이 들어가거나 경계에 있는 값이 복사된다. 만약 borderType=BORDER_CONSTANT 라면 value 값으로 채워 넣는다. 다른 borderType 들은 src의 상하좌우의 경계 값을 특정 규칙에 따라 복사하여 채운다. 


- int borderInterpolate(int p, int len, int borderType)

: 이 함수는 copyMakeBorder 과 달리 결과행렬에 경계값 채운 결과가 저장되지 않는다. borderType에 따라 p위치에 대한 원본 영상의 좌표를 계산한다. len은 행렬의 열 또는 열의 길이다. 다음의 예를 보자.  

int border = 1; Mat B; copyMakeBorder(A, B, border, border, border, border, BORDER_REPLICATE); cout << "B = " << B << endl;


for (int y = -border; y<A.rows + border; y++) { for (int x = -border; x<A.cols + border; x++) { float val = A.at<uchar>(borderInterpolate(y, A.rows, BORDER_REPLICATE), borderInterpolate(x, A.cols, BORDER_REPLICATE)); cout << val << ", "; } cout << endl; }

위 두 소스의 결과로 출력되는 값은 서로 같다.

+ Recent posts