WPF OpenCV 프로젝트 #33: Image Pyramid (Gaussian & Laplacian)

Image Pyramid (이미지 피라미드)를 주제로 Gaussian Pyramid (가우시안 피라미드)와 Laplacian Pylamid (라플라시안 피라미드)에 대해 OpenCvSharp에서 제공하는 함수들을 이용해서 순차적으로 간단히 개념을 정리하고, WPF OpenCV 프로젝트에 구현해 보겠습니다.
이전 포스팅(#32)에서는 Morphology (모폴로지: 형태학)를 주제로 Erosion (침식), Dilation (팽창), Opening (열림), Closing (닫힘), Gradiant (그레디언트), Top-Hat, Black-Hat 에 대해 OpenCvSharp에서 제공하는 함수들을 이용해서 한꺼번에 순차적으로 간단히 개념을 정리하고, WPF OpenCV 프로젝트에 구현하였습니다. 영상 Filter 관련 포스팅은 이번 포스팅(#33)이 마지막 내용이 됩니다. 그동안 진행한 Filter 관련 내용을 차례대로 기술해보면, Convolution, Average Blur, Gaussian Bluring, Median Buluring, Bilateral filter, Basic Differential, Roberts, Prewitt, Sobel, Scharr, Laplacian, Canny, Erode, Dilate, Opening, Closing, Gradient, Top-Hat, Black-Hat 을 모두 총 망라하여 다뤘더군요. 후~

아무튼 이번 Image Pyramid (이미지 피라미드)가 제가 Filter 관련해서 다루는 포스팅의 마지막입니다. 이제 본격적으로 정리해 보도록 하죠.

Theory

Image Pyramid

Image Pyramid (이미지 피라미드)는 하나의 원본 이미지로 부터 다양한 해상도의 이미지 세트를 생성하는 방법입니다. 조금 더 풀어서 설명하면, 영상의 크기를 단계적으로 축소 또는 확대 해서, 가장 아래층에는 해상도가 높은 원본 이미지를 두고, 위로 올라갈수록 해상도를 낮춰 쌓아 올립니다. 그 모양이 Pyramid (피라미드)와 같다고 해서 붙여진 이름이죠. 이렇게 하는 이유는 영상을 분석할 때 먼저 작은 이미지 (해상도가 낮은 이미지)로 겁나 빠르게 훑어 보고 다음 단계 크기의 영상으로 분석하는 식으로 정확도를 높이는 것이 효율적이며, 영상의 크기에 따라 분석하는 내용이 다를 수 있기 때문이기도 합니다.

이번 포스팅의 마지막에 기술할 테지만, Image Pyramid영상 인식이나, 영상에서 객체 추적 등과 같이 영상 분석이 필요할 때 내부적으로 활용되는 경우가 대부분입니다. 포스팅의 끝부분에 다시 한번 추가 적으로 언급하겠습니다. Image Pyramid 에는 Gaussian PyramidLaplacian Pyramid 가 있는데, 차례대로 정리해 보도록 하죠.

Gaussian Pyramid (가우시안 피라미드)

앞선 포스팅에서 다루었던, Gaussian Filter (가우시안 필터)를 적용한 후 Image Pyramid (이미지 피라미드)를 구현하는 것을 Gaussian Pyramid 라고 합니다. Gaussian Pyramid (가우시안 피라미드)의 한 단계를 생성하는 과정은 크게 두 단계로 나눠집니다.

첫 번째: Gaussian Bluring (가우시안 블러링) 으로 이미지에 5 x 5 크기의 Gaussian KernelConvolution 해서, 고주파 성분(노이즈 및 세밀한 엣지)를 제거하여 Smooting 하게 만듭니다. 이것은 Down Sampling (다운 샘플링) 시 발생하는 Aliasing (계단 현상) 을 방지하기 위해서 입니다. 가장 많이 사용되는 5 x 5 크기의 Gaussian Kernel 은 아래의 행렬을 사용합니다. (그냥 알아 만 두세요 ^^)

W=1256[1464141624164624362464162416414641]W=\frac{1}{256}\begin{bmatrix}1&4&6&4&1\\4&16&24&16&4\\6&24&36&24&6\\4&16&24&16&4\\1&4&6&4&1\end{bmatrix}


두 번째: Down Sampling (다운 샘플링) 또는 Sub Samplig 이라고도 하는데요. 이 단계에서는 첫 번째 단계에 블러링 처리된 이미지에서 모든 짝수 번째 행과 열을 제거하여 이미지의 크기를 가로/세로 각각 1/2 (전체 면적 1/4)로 줄여 버립니다.
이것을 수식으로 표현하면 아래와 같이 굳이 표현할 수 있답니다.
l번째 층의 이미지 Gl은 이전 층 G{l-1}로 부터 다음과 같이 유도 된다고 하니, 진짜 ~ 참고만 하세요.

Gl=Reduce(Gl1)G_l=\text{Reduce}(G_{l-1})


OpenCV 라이브러리에서는 Gaussian Pyramid 처리를 위해 두 가지의 함수 [Cv2.PyDown(), Cv2.PyUp()] 를 제공합니다.

먼저 이미지의 축소를 위해 Cv2.PyDown() 함수는 아래와 같은 원형을 가지고 있습니다.

void Cv2.PyrDown
(
InputArray src,
OutputArray dst,
Size dstSize = default,
BorderTypes borderType = BorderTypes.Reflect101
);

이 함수의 동작은 Gaussian Filtering 처리 후 가로/세로를 1/2 줄입니다. 주요 파라미터를 살펴보죠.
dstSize : 출력 이미지의 크기를 지정할 수 있지만, 기본적으로 (src.Cols+1)/2, (src.Rows+1)/2로 자동 계산됩니다.
borderType: 이미지 가장자리 픽셀을 처리하는 방식입니다.
(피라미드는 Reflect101을 권장합니다. OpenCVSharp 에서는 현재 Reflect101 외의 Type은 지원되지 않는 것으로 보입니다.)

이미지의 확대를 위해 Cv2.PyUp() 함수는 아래와 같은 원형을 가집니다. 사실 함수 이름만 다를 뿐 파라미터는 동일합니다.

void Cv2.PyrUp
(
InputArray src,
OutputArray dst,
Size dstSize = default,
BorderTypes borderType = BorderTypes.Reflect101
);

이 함수의 동작은 이미지의 가로/세로를 2배로 늘리 후 Gaussian Filtering 을 적용해서 부드럽게 만듭니다. 이미지를 확대한다고 해서 손실된 정보가 돌아오지는 않습니다. 그냥 약간 뿌옇게 보입니다.

Laplacian Pyramid (라플라시안 피라미드)

Laplacian Pyramid (라플라시안 피라미드)는 Gaussian Pyramid (가우시안 피라미드) 처리 과정에서 손실된 정보를 저장하는 피라미드입니다. 이게 무슨 말이냐면, 영상을 Cv2.PyUp() 함수를 이용해 4배로 확대할 때 없던 행과 열을 생성해서 Gaussian Filter 를 적용하기 때문에 원본 영상 만큼 완벽하지 못합니다. 그래서 Cv2.PyDown() 함수로 한 단계 작아진 영상을 Cv2.PyUp() 함수로 확대 해도 원본 영상을 완벽하게 복원할 수가 없습니다. 원본 영상에서 Cv2.PyUp() 함수로 확대한 영상을 빼서 나온 그 값 만큼이 바로 원본 영상과 확대 영상의 차이가 되는데요, 이 놈을 보관해 두었다가 확대 영상에 더하게 되면 원본 영상을 아주 완벽하게 복원할 수 있습니다. 이렇게 원본 영상과 OpenCv에서 제공하는 Image Pyramid 함수를 적용한 영상의 차이를 단계 별로 보관해 두는 것을 Laplacian Pyramid 라고 하는 겁니다.

(어렵지 않죠?) 아무튼 Laplacian Pyramid 는 아래와 같이 l 번째 중 Ll 은 아래와 같이 계산됩니다.

Ll=GlExpand(Gl+1)L_l=G_l-\text{Expand}(G_{l+1})


수식에서 Expand 부분은 이미지를 다시 2배로 확대하고 블러링 하는 과정입니다. Laplacian Pyramid 는 이미지의 Edge (Edge) 또는 Texture 같은 세밀한 부분만 남게 되므로, 마치 흑백 선화처럼 보입니다. Laplacian Pyramid 와 Gaussian 처리된 이미지가 있다면, 원본 이미지를 완벽하게 복원할 수 있습니다.​

나름 간단하게 정리한다는 것이 조금 길어 졌네요. 위에 언급한 정도만 알아도 충분한 개념이긴 한데, 마지막으로 두 개의 Image Pyramid 를 아래의 표와 같이 간략히 정리하고 WPF OpenCV 프로젝트에 구현하도록 하겠습니다.

구분Gaussian PyramidLaplacian Pyramid
개념이미지의 크기를 줄여나가는 해상도 축소축소 과정에서 버려진 디테일(차이) 저장
시각적 형태원본이 점점 작고 뿌옇게 변함이미지의 윤곽선과 세밀한 특징만 나타남
용도멀티 스케일 분석, 템플릿 매칭이미지 압축, 이미지 블렌딩(합성), 복원

구현 요약

위 에서 언급 Image Pyramid 들에 대해 간략히 (?) 정리했으니까 이제 구현을 해야겠죠. 늘 그래왔던 것 처럼 WPF OpenCV 프로젝트에 구현할 내용을 아래와 같이 요약하고 단계별로 구현 해 보도록 하죠.

AlgorithmParameters.cs: ImagePyramidParams 클래스와 ImagePyramidType 열거형을 추가하여 각 알고리즘 필터 (GaussianDown, GaussianUp, Laplacian)를 정의합니다.
MainViewModel.cs: 알고리즘 목록에 “Image Pyramid”를 추가하고, 선택 시 파라미터 객체를 생성하도록 연결하도록 하죠.
MainWindow.xaml: “Image Pyramid” 선택 시 나타날 UI(콤보박스)를 DataTemplate에 추가하고, 선택할 수 있도록 구현 합니다.
OpenCVService.cs: Gaussian Pyramid, Laplacian Pyramid 에 대한 영상 처리를 위해 OpenCvSharp 함수 이용해서 실제로 수행하는 로직을 구현합니다.

Step 1: ImagePyramidParams (Model)

AlgorithmParameter.cs 파일에 먼저 ImagePyramidType 열거형 데이터를 추가합니다. ImagePyramidParams 클래스에는 적용할 Filter 함수들의 파라미터를 아래의 코드와 같이 정의해서 UI와 연결 되도록 하죠.

Step 2: MainWindow.xaml (View)

MainWindow.xaml 파일에는 ImagePyramidParams Property 속성을 UI에 연결하고, 사용자가 “Image Pyramid” 선택 시 나타날 UI(콤보박스)를 아래의 코드와 같이 DataTemplate에 추가해서, 선택된 필터를 설정합니다.

Step 3: MainViewModel (ViewModel)

MainViewModel.cs 파일의 MainViewModel() 생성자 함수의 알고리즘 목록에 Image Pyramid 를 추가하고, 사용자가 Image Pyramid 를 선택했을 때, CreateParametersForAlgorithm() 함수에서 ImagePyramidParams 객체의 파라미터 객체를 생성해 연결 하도록 아래 코드로 업데이트 합니다.

그리고, UI의 상태 바에 이미지 로딩 시 파일 명과 해상도를 표시하게 끔 변수와 일부 함수도 수정하였으니, 아래 코드를 참고해 주세요.

Step 4: OpenCVService (model)

OpenCVService.cs 파일에서는 ProcessImageAsync() 함수에 OpenCvSharp에서 제공하는 함수 중Cv2.PyUp 함수와 Cv2.PyDown() 함수를 이용해서 Gaussian PyramidLaplacian Pyramid 에 대한 영상 처리 하는 코드를 아래와 같이 추가하면 됩니다.

실행 및 확인

정상적으로 빌드가 완료되었다면, 에러 없이 잘 실행될 거라 믿어 의심치 않습니다. ^^;

Image Pyramid 검증을 위해서는 고주파 성분 (세밀한 선, 복잡한 패턴) 과 저주파 성분 (부드러운 그라데이션)이 모두 포함된 이미지가 검증에 가장 좋은 이미지입니다. 특히 Gaussian Pyramid 의 경우, 축소 시 Aliasing (계단 현상)이 발생하는지 확인 하기 위해서는 동심원 패턴의 이미지를, Laplacian Pyramid 의 경우에는 Edge 정보를 확인하기 위해 선명한 텍스트 및 기학적 도형이 포함된 이미지가 좋습니다. 여기서는 제가 가지고 있는 ChessBoard 이미지만을 가지고 테스트 하기 때문에 언급한 특징의 이미지가 있다면 로딩하여 각자 알아서 테스트 해보시기 바랍니다.

먼저 원본 이미지를 불러옵니다. 원본 이미지는 960 x 720 크기를 가집니다.

Image Pyramid : Image loading
원본 이미지 (로딩)

위 원본 이미지를 Image Pyramid Gaussian Down 알고리즘을 적용한 이미지는 아래와 같습니다.
적용 후 이미지는 480 x 360 크기를 가지게 됩니다.

Image Pyramid : Gaussian Down
Image Pyramid (GaussianDown)

다음은 원본 이미지를 Image PyramidGaussian Up 알고리즘을 적용한 이미지 입니다.
적용 후 이미지는 1920 x 1440 크기를 가지게 됩니다.

Image Pyramid : Gaussian Up
Image Pyramid (Gaussian Up)

마지막으로 원본 이미지를 Image PyramidLaplacian 알고리즘을 적용한 이미지는 아래와 같습니다.

Image Pyramid : Laplacian
Image Pyramid (Laplacian)

위와 같이 Laplacian Pylamid 를 적용하면, ChessBoard 이미지의 Edge (경계선) 부분이 선명하게 추출되는지 확인할 수 있습니다.

마지막으로 Image Pyramid 를 적용하는 이유를 간략히 기술하고 이번 포스팅을 마무리 할게요.

영상 처리에서 단순히 화면을 확대/축소하는 것 외에 Image Pyramid 알고리즘을 쓰는 이유영상 처리 컴퓨터가 이미지 내부의 물체를 다양한 크기로 찾아 내야 하기 때문 입니다.

아직 포스팅 하진 않았지만, 객체 검출(Ojbect Detection)의 경우를 보죠. 이미지에서 얼굴을 찾는다고 가정하면, 원본 이미지에 얼굴이 매우 클 수도 있고, 아주 멀리 있어 작을 수도 있습니다. 고정된 크기의 Window (얼굴을 찾는 영역 또는 ROI)를 가지고 이미지를 훑는다면 image pyramid 를 만들어 사용해서 window의 크기를 바꾸는 대신 다양한 크기의 이미지를 훑음으로써 모든 사이즈의 얼굴을 찾을 수 있겠죠.

다른 예로, Image Blending (이미지 블랜딩) 이라고 두 이미지를 자연스럽게 합치는 영상 처리 방법이 있습니다. 무슨 영화였는지 기억은 나지 않지만, 사람 얼굴이 늑대 인간의 얼굴로 변해 가는 영상을 본 적이 있습니다. 이처럼 두 이미지가 자연스럽게 합칠 때 사용합니다. Laplacian Pylamid 를 이용해 각 주파수 대역 (거친 윤곽선 ~ 세밀한 질감) 별로 따로 합치면 경계선이 생기지 않고 아주 부드럽게 합성 시킬 수 있습니다. 그리고 이미지에서 별로 중요하지 않은 정보를 골라내어 데이터 용량을 줄일 때에도 유용합니다.

이것으로 Image Filter (영상 필터) 와 관련된 포스팅은 이것으로 마무리 하겠습니다.
다음 포스팅 부터는 Image Segmentation (영상 분할) 에 대해 한땀 한땀, 차근 차근 다뤄 보도록 하겠습니다.

참고 자료

[Post #32] 모폴로지: [WPF OpenCV Project #32] – 노이즈 제거와 형태 보정
[Post #30] 가우시안 블러: [WPF OpenCV Project #30] – 피라미드 전처리의 핵심
PyrDown: OpenCV Docs – 이미지 축소 함수
PyrUp: OpenCV Docs – 이미지 확대 함수
Image Pyramids: OpenCV Python Tutorial – 피라미드의 이론과 활용 예제

댓글 남기기