(docs.opencv.org 참조)

 

형태학적 변환 Morphological Transformation은 이미지 형태를 기반으로 한 몇 가지 단순한 연산이며, 보통 이진 Binary 이미지에서 수행됩니다. 한 단계 더 나아가 그레이 Gray 이미지에서도 가능합니다. 동작을 위해서는 원본 이미지와 변환의 특성을 결정하는 마스크 또는 커널을 활용합니다. 기본적인 형태학 연산자는 침식 Erosion과 팽창 Dilation입니다. 그리고 열림 Opening, 닫힘Closing, 기울기 Gradient 등과 같은 변형 연산도 수행할 수 있습니다. 현 블로그에서는 이진 이미지 기준으로 기본 변환을 설명합니다.

 

영상처리에서 형태학적 변환 연산은 관심영역 또는 물체의 크기를 재정의 하거나 잡음 Noise 제거에 빈번히 활용됩니다.

 

1.  침식 Erosion 연산

침식 Erosion은 흰색 영역을 전경이라고 했을 때 흰색 영역의 가장자리를 기준으로 감소시키는 역할을 합니다. 기본 동작은 원본 이미지 기준으로 커널 간 컨볼루션 Convolution 수행이며, 원본 이미지의 픽셀(1 또는 0)은 커널 아래의 모든 픽셀이 1일 때만 1로 간주되고, 그렇지 않으면 침식됩니다(0으로 만듭니다). 따라서 커널의 크기에 따라 경계 근처의 픽셀들이 모두 침식될 수 있으며, 전경 객체의 두께나 크기가 감소하거나 이미지에서 단순히 흰색 영역이 감소합니다. 작은 흰색 잡음을 제거하거나, 연결된 두 개의 객체를 분리하는 등에 유용합니다. Python 예에서 커널 kenel은 모든 element가 1인 5X5 매트릭스이며, Input_Img와 결과 이미지는 이진 Binary 이미지 입니다.

 

Python Example)

kernel = np.ones((5,5),np.uint8)

Erosion_Img = cv.erode(Input_Img, kernel, iterations = 1)

 

 

2. 팽창 Dilation 연산

팽창 연산은 침식과 반대의 효과를 내며, 커널 아래의 적어도 하나의 픽셀이 '1'이면 픽셀 요소는 '1'입니다. 따라서 이미지에서 흰색 영역이 커지거나 전경 물체의 크기가 커집니다. 침식은 배경에 잡음을 제거하는 효과가 있다면 팽창은 전경 내에 잡음 제거에 효과가 있습니다.

 

Dilation_Img = cv.dilate(Input_Img, kernel, iterations = 1)

 

 

3. 열림 Opening 연산

앞에 설명한 침식과 팽창이 연속 연산을 통해 또 다른 효과를 낼 수 있습니다. 열림 Opening 연산은 침식 연산 후에 팽창 연산을 의미합니다. 그림에서와 같이 침식 효과를 통해 배경 잡음을 제거하고 팽창을 통해 전경의 크기를 보전합니다.

 

Opening_Img = cv.morphologyEx(Input_mg, cv.MORPH_OPEN, kernel)

 

 

4. 닫힘 Closing 연산

닫힘 연산은 팽창 연산 침식 연산을 수행합니다. 그림에서 같이 팽창의 효과로 전경의 잡음을 제거하고 침식을 통해 전경의 크기를 보전합니다.

 

Closing_Img = cv.morphologyEx(Input_Img, cv.MORPH_CLOSE, kernel)

 

 

5. Morphological Gradient

그림에서 같이 팽창 연산 결과와 침식 연산 결과의 차(뺄셈) 연산을 통해 관심 영역인 전경의 에지 Edge를 추출할 수 있습니다.

 

Gradient_Img = cv.morphologyEx(Input_Img, cv.MORPH_GRADIENT, kernel)

 

 

6. 커널 Structuring Element

보통 커널의 형태는 직사각형 모양입니다. 그러나 경우에 따라 타원형 또는 원형 커널이 필요할 수 있습니다. 이를 위해 OpenCV에는 cv.getStructuringElement()라는 함수가 있으며, 커널의 모양과 크기를 전달하면 원하는 커널을 얻을 수 있습니다. 그림은 Python 활용 예입니다.

 

(docs.opencv.org 참조)

 

히스토그램은 이미지의 픽셀 크기별 분포를 그래픽으로 나타낼 수 있습니다. 히스토그램의 X축은 픽셀의 크기(Gray Image: 0~255)를 나타내며 Y축은 영상의 픽셀 수를 나타냅니다. 픽셀 수가 많을수록 특정 밝기 레벨의 피크가 더 높습니다. 히스토그램 균등화(평활화) Histogram Equalization(HE)는 이미지의 대조를 향상시키는데 사용되는 이미지 처리 기술입니다. 이는 가장 빈번한 픽셀값을 효과적으로 펼침으로써, 이미지의 밝기 범위를 확장함으로써 개선 효과를 얻을 수 있습니다. 이를 통해 낮은 국소 대조도의 영역이 더 높은 대조도를 얻을 수 있습니다.

그림에서처럼 픽셀값이 특정 범위의 값으로만 제한되는 이미지를 생각해 있습니다. 예를 들어, 밝은 이미지는 모든 픽셀이 높은 값으로 제한됩니다. 그러나 좋은 이미지는 이미지의 모든 영역에서 픽셀값을 가질 것입니다. 따라서 히스토그램을 끝까지 늘려야 이미지의 대비를 향상시킵니다. 원리를 활용한 이미지 개선 방법이 히스토그램 평활화 기술입니다칼라 히스토그램 평활화는 이미지의 색상 균형에 극적인 변화를 초래하기 때문에 이미지의 빨간색, 녹색 및 파란색 성분에 별도로 적용할 수 없습니다. 그러나 이미지를 HSL/HSV 색 공간과 같은 다른 색 공간으로 먼저 변환 후, 이미지의 색상 및 채도를 변경하지 않고 휘도값에 적용할 수 있습니다.

 

Adaptive Histogram Equalization

적응적 히스토그램 평활화(Adaptive Histogram Equalization) 그대로 적응적, 국소적으로 이미지 밝기를 변환한다는 점에서 일반적인 히스토그램 평활화는 다르며, 로컬 대비를 개선하고 영상의 영역에서 에지의 정의를 강화하는데 적합합니다.

적응적 히스토그램 평활화의 한 방법으로 CLAHE(Contrast Limited Adaptive HE)가 있습니다. CLAHE의 경우, 이미지 대비 제한 과정은 국소적으로 영역을 분할하여 적용하며 일반적인 히스토그램 균등화가 야기할 수 있는 노이즈의 과증폭을 방지하기 위해 개발되었습니다. 아래 첫번째 이미지는 원본 이미지이며, 두번째 이미지는 일반적 히스토그램 균등화(Histogram Equalization) 결과, 세번째는 CLAHE 결과를 나타냅니다. 일반 히스토그램 균등화에서 발생할 수 있는 밝기 포화 Saturation를 CLAHE를 통해 개선할 수 있습니다. 

 

OpenCV에서 createCLAHE 함수로 제공 됩니다. 아래는 Python Code 예입니다.

 

import cv2 as cv

img = cv.imread('input_img.jpg', cv.IMREAD_GRAYSCALE)

clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))

cl1 = clahe.apply(img)

cv.imwrite('output_img.jpg',cl1)

(docs.opencv.org 참조)

 

이미지 개선의 가장 기본처리 방식으로 이미지 히스토그램을 활용합니다. 이미지 히스토그램 Image Histogram 이란 말 그대로 픽셀값의 분포를 나타냅니다. 이를 활용한 이미지 개선 방법 중 하나가 이미지 평활화 또는 균등화 Image Equalization이며 OpenCV 함수 EqualizeHist로 활용해 볼 수 있습니다.

 

 

히스토그램 평활화란 픽셀값 범위를 확장하여 이미지 대비를 개선하는 방법입니다. 그림에서처럼 원본 이미지의 픽셀값의 분포는 중간에 모여 있으며 이를 픽셀의 최소(0) 및 최대(255) 범위로 평활화 하게 되면 대비가 개선된 이미지를 얻을 수 있습니다. 조금 더 세부적으로 원본 이미지의 히스토그램의 누적분포함수(CDF)를 활용하여 픽셀값에 재맵핑 Remapping을 통해 균등화 할 수 있습니다. 수식은 생략하도록 하겠습니다.

 

활용 예)

(C/C++) equalizeHist( Input_Image, Result_Image);

(Python) Result_Image = cv.equalizeHist(Input_Image)

 

파라미터 Input_Image는 8-bit gray image로 결과 또한 8-bit gray 입니다. 

 

영상처리 알고리즘 Algorithm을 설계할 때 전처리 과정에 필수적으로 사용되는 것이 영상에 잡음 Noise을 제거하기 위한 함수 입니다. 몇 가지 명칭으로 사용되며 이미지 블러링 Image Blurring  또는 이미지 스무딩 Image Smoothing 등에  명칭으로 사용이 되는데 유사한 의미로 볼 수 있습니다. 신호처리에서 표현하는 저역통과 필터 Lowpass Filter로도 볼 수 있습니다. 평균 필터 Mean Filter, Averaging Filter에 명칭으로도 쓰이며 변형된 필터인 가우시안 필터 Gaussian Filter, 중간값 필터 Median Filter로도 응용될 수 있습니다. 원리로 보면 저역통과라는 단어에서 느껴지는 것처럼 고주파 신호를 제거하고 저주파 신호만 받아들이겠다는 의미이기도 합니다.

 

OpenCV에는 위에서 처럼 Smooth라는 함수로 구현이 되어 있습니다. Src는 원본영상, 그리고 Dst는 결과영상을 나타내고 기본적으로 8 bits 영상을 사용합니다. Size1Size2는 각 Aperture withheight를 나타내며, 다시 말해 적용하고자 하는 마스크 mask의 크기입니다. 예를 들어 3x3, 5x5, 7x7 등에 마스크 크기를 설정하면 되고, Smooth Type 중 가우시안 마스크 CV_GAUSSIANCV_BILATERAL을 이용 시에는 Sigma를 활용할 수도 있습니다. 마스크에 형태 Type CV_BLUR는 평균 필터, CV_MEDIAN은 중간값 필터를 의미합니다. 참고로 CV_BILATERALGaussian filter의 응용으로 Non-Linear한 특성과 경계선 보존 Edge-Preserving 특징을 가지는 필터로 영상처리기술에서 추가로 설명하도록 하겠습니다.

 

사용 예)

cvSmooth( Input_Image, Result_Image, CV_BLUR, 3, 3, 0, 0)

 

3x3 마스크 크기의 평균 필터를 적용하고자 할 때 위 예처럼 사용하면 됩니다. Smooth 함수는 알고리즘 개발에 전처리 과정에도 많이 사용되지만 그 자체 원리를 이용하여 디지털 카메라, 스마트폰 등에서 사진 블러효과 Blur Effect 로도 응용되고 실생활에서 자주 접할 수 있는 기능이기도 합니다.

윤곽선 검출 Edge Detection은 물체에 경계선을 추출하기 위한 기술 입니다. 우리가 알고자 하는 물체의 모양, 크기, 위치 등 정보를 확인하고자 할 때 사용되는 기술로 알고리즘의 전처리 과정에서 이용됩니다. 요즘 기술개발이 활발히 진행되고 있는 Machine Learning의 한 분야인 딥러닝 Deep Learning에서 영상의 특징점 Feature 을 정의하는데도 사용되는 전처리 기술 입니다.

Edge Detection 기술이 사용 된지는 오래됐으며 현재까지도 폭넓게 사용되고 있고, 수학적 접근방식에 따라 다양한 방법들이 존재합니다. 현 이야기에서는 OpenCV에서 제공되는 윤곽선 검출 도구 중 Sobel Edge Detection Canny Edge Detection 기술 사용법에 대한 설명입니다. Edge Detection에 앞에 붙어 있는 Sobel Canny는 개발자에 이름이며 WIKIPEDIA에 의하면Sobel Filter 1968년과 Canny Edge Detector 1986년에 개발되었습니다. 정말 오래된 기술 입니다만 현재도 상당히 많이 사용되고 있고, 이러한 윤곽선 검출 기술에 기본 원리는 미분 연산자 Differentiation Operator에 의한 밝기 변화 탐색 입니다. 다시 말해, 물체의 가장자리에서 밝기 변화가 크므로 Pixel 값에 차이를 통해 윤곽선 유무를 판단할 수 있다는 의미이기도 합니다.

위에서처럼 OpenCV에서 제공되는 Edge Detection 함수인 Sobel Canny를 볼 수 있습니다. 먼저 cvSobel 함수를 살펴보면 앞에서 설명한 기본 함수들에서처럼 입력영상인 “src”와 결과영상을 담기위한 “dst”가 보입니다. “aperture_size” Sobel Mask의 크기를 나타내고 “3”이면 3X3을 의미합니다. “xorder”“yorder” Sobel Mask 연산 시 결과값의 위치를 나타내는데 3X3의 경우 중심인 xorder = 1 yorder = 1 이 되며, Mask의 시작점은 (0, 0)이니 2가 아닌 1이 됩니다.

 

Sobel 사용 예)

cvSobel( Input_Image, Result_Image, 1, 1, 3)

cvConvertScaleAbs(Result_Image, Convert_Result_Image, 1, 0)

 

cvSobel 함수 사용은 위에 예처럼 사용하면 되는데 cvConvertScaleAbs()라는 함수가 더 있죠? cvSobel 함수 사용 시 주의할 점이 있습니다. 앞에서 언급했듯이 Edge Detection의 원리가 Pixel의 차이를 이용하기 때문에 결과에서는 마이너스 “-“ 값이 존재하고 Result_Image Depth는 보통 Input_Image Depthunsigned 8-bit 이니 signed 16-bit 으로 구성되어야 합니다. 따라서 최총 결과를 확인하기 위해서는 절대값을 이용하여 다시 unsigned 8-bit 으로 변환해 줘야 하고, 이때 사용되는 함수가 cvConvertScaleAbs() 입니다.

 

cvCanny 함수의 경우 입력영상인 “image”와 결과영상인 “edges”가 보이고 “apertureSize = 3” Sobel Mask   3X3을 의미합니다. 여기서 cvSobel 함수와 다르게 “threshold1”“threshold2”를 입력하게 되어 있는데, “threshold1” 이하는 외곽선이 아닌 영역으로 “threshold2” 이상은 외곽선인 영역, 그리고 “threshold1”“threshold2”의 사이는 Canny Procedure에 의해 외곽선 유무를 판단하게 됩니다.

 

Canny 사용 예)

cvCanny( Input_Image, Result_Image, 100, 200, 3 )

 

cvCanny 사용 예에서 threshold1threshold2 100 2000 ~ 255의 범위를 갖는 8-bit 영상 기준으로 설정된 값을 의미하고, Result_Image Input_Image와 동일한 속성으로 설정하면 됩니다. 앞에 설명과 그림 결과와 같이 8-bit Input_Image 기준으로 cvSobel 함수의 최종 Result_Image 8-bit 그레이 Gray 영상이며, cvCanny 함수의 경우 8-bit 바이너리 Binary 영상이니 이것만 주의해서 활용하면 됩니다. Sobel Canny의 수학적 또는 알고리즘 접근에 대해서는 영상처리기술에서 추가적으로 다뤄보도록 하겠습니다.

+ Recent posts