728x90

이미지 처리에서 영역을 분할하는 방법들은 다양합니다. OpenCV에서 제공하는 기능 중 floodFill도 영역을 분할 하는 방법들 중에 하나 입니다. 응용에서는 물체 추적에 전처리나 배경 제거, 물체의 영역 채우기 등에 활용됩니다.

 

아래는 파이썬에서 활용하는 함수 형태와 C/C++에서 사용하는 함수 예 입니다. C/C++에서 활용하는 함수는 두 가지 형태인데 mask 유무가 차이 입니다. 

 

(Python) retval, image, mask, rect = cv2.floodFill(image, mask, seedPoint, newVal,

                                                                                 loDiff=None, upDiff=None, flags=None)

 

(C/C++) int floodFill(InputOutputArray image, Point seedPoint, Scalar newVal,

                                  Rect* rect = 0, Scalar loDiff = Scalar(), Scalar upDiff = Scalar(), int flags = 4);

 

(C/C++) int floodFill(InputOutputArray image, InputOutputArray mask, Point seedPoint, 

                    Scalar newVal, Rect* rect = 0, Scalar loDiff = Scalar(), Scalar upDiff = Scalar(), int flags = 4);

 

파라미터 설명은 아래와 같으며 실무 적용에서는 mask 활용할 때와 아닐 때만 유의하면 됩니다. 

 

1) image: 입력 이미지이자 처리 대상 이미지이며 그레이 이미지와 칼라 이미지 적용 가능합니다.

2) mask: 어떤 부분을 채울 때 채우기 작업을 제한하는 역할을 합니다. 예를 들어 영역의 에지를 추출한 Binary 이미지라면 “0”인 부분만 특정 값으로 채울 수 있습니다. 8-bit 그레이 이미지로 정의해야 하며, 원본 이미지보다 가로와 세로가 2 픽셀씩 커야 합니다. 추가로 마스크의 테두리는 “1”로 채우며, 동일한 마스크를 여러번 사용해도 채워진 영역이 겹치지 않도록 할 수 있습니다.

3) seedPoint: 값을 변경하는 시작 좌표(x, y) 입니다.

4) newVal: 변경할 픽셀값 또는 색상 입니다. 칼라라면 (255, 10, 10) 형태가 됩니다.

5) loDiff: 변경할 픽셀값의 하한값 입니다.

6) upDiff: 변경할 픽셀값의 상한값 입니다.

7) flag: 변경 처리 조건을 지정합니다.

 

flag에는 아래 두 가지가 있으며, 실제 적용 시에는 픽셀 연결 방식인 4방향 또는 8방향과 조합해서 사용할 수 있습니다.

 

(flag)

1) FLOODFILL_FIXED_RANGE: 정의된 상한 및 하한 픽셀값 비교

2) FLOODFILL_MASK_ONLY: 입력 이미지는 변화 없으며 mask를 업데이트

(connectivity) 상하좌우 4방향과 대각 포함한 8방향으로 비교

 

아래 예는 mask를 사용하지 않는 파이썬 및 C/C++ 코드 입니다. “flag” 부분을 살펴보면 “flags=4 | cv2.FLOODFILL_FIXED_RANGE”과 “4 | FLOODFILL_FIXED_RANGE”로 정의 되었으며, 4방향 + 정의된 상한 및 하한 픽셀값 비교 로 동일한 의미로 적용되었음을 알 수 있습니다. 

 

import cv2
import numpy as np

# 이미지 로드 및 복사
img = cv2.imread('image.jpg')
im_floodfill = img.copy()

# 마스크 생성
h, w = img.shape[:2]
mask = np.zeros((h+2, w+2), np.uint8)

# 위치 및 변환값
seed_point = (125, 125)
new_val = (255, 0, 0)

cv2.floodFill(im_floodfill, mask, seed_point, new_val,
              loDiff=(5, 5, 5), upDiff=(5, 5, 5),
              flags=4 | cv2.FLOODFILL_FIXED_RANGE)

cv2.imshow("Filled Image", im_floodfill)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

#include <opencv2/opencv.hpp>
using namespace cv;

int main() {
    
    // 이미지 로드
    Mat img = imread("image.jpg");
   
    // 마스크 생성
    Mat mask = Mat::zeros(img.rows + 2, img.cols + 2, CV_8UC1);

    // 위치 및 변환값
    Point seedPoint(125, 125);
    Scalar fillColor(255, 0, 0);  

   floodFill(img, mask, seedPoint, fillColor,
              0,              // rect 포인터 (필요 없으면 0)
              Scalar(5,5,5),  // loDiff
              Scalar(5,5,5),  // upDiff
              4 | FLOODFILL_FIXED_RANGE);  // flags

    // 결과 출력
    imshow("Flood Filled", img);
    waitKey(0);
    return 0;
}
728x90
728x90

이미지에서 우리가 원하는 영역을 찾거나 추출할 때 필요한 방법이 이진화 Binarization 입니다. 물체 탐색, 물체 추적, 이미지 분할 등 알고리즘을 구현할 때 필수적으로 사용해야하는 기법이 이진화 기술들입니다. 이진화 방법에는 다양한 기술들이 있으며, OpenCV 에서 제공하는 기술들 중 하나인 적응적 이진화 기술 Adaptive Thresholding Method에 대해 알아보겠습니다.

 

이진화 방법은 크게 전역적 처리 Global Processing과 지역적 처리 방법 Local Processing 방법으로 구분할 수 있습니다. OpenCV에서 제공하는 “adaptiveThreshold” 방법은 지역적 처리 방법과 적응적 방법이 합쳐진 이진화 방법입니다. Python에서는 아래와 같이 “cv2.adaptiveThreshold” 명으로 적용해 볼 수 있으며, 입력 변수는 아래와 같습니다.

 

[Python] dst = cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)

(입력 변수 설명)

- src: 입력 이미지 (Gray Scale Image)

- maxValue: 임계값 Threshold을 초과한 픽셀에 적용할 값 (일반적으로 255)

- adaptiveMethod: 1) cv2.ADAPTIVE_THRESH_MEAN_C, 2) cv2.ADAPTIVE_THRESH_GAUSSIAN_C

- thresholdType: 1) cv2.THRESH_BINARY, 2) cv2.THRESH_BINARY_INV

- blockSize: 픽셀 임계값을 계산할 영역 크기 (홀수: 3, 5, 7 ...)

- C: 계산된 임계값에서 빼는 상수 (일반적으로 0 또는 2~10)

 

여기서 “cv2.ADAPTIVE_THRESH_MEAN_C”는 “blockSize”가 3이라면 3x3 크기 영역 안에 평균 값에서 “C”를 뺀 값을 임계값으로 사용하게 됩니다. “cv2.ADAPTIVE_THRESH_GAUSSIAN_C”는 3x3 크기 영역 안에 가우시안 가중 평균값에서 “C”를 뺀 값을 임계값으로 사용합니다. 앞에서 언급했듯이 “blockSize”는 3은 3x3, 5는 5x5의 블럭을 의미하며 블럭 크기가 작아질 수록 잡음 Noise에 민감해지고 너무 크면 지역적 처리의 이점이 줄어들 수 있으니 설정 시 고민이 필요할 수 있습니다.

 

C/C++ 코드를 사용한다면 아래와 같이 사용할 수 있습니다. 파이썬 함수와 다르게 결과 이미지는 입력 변수와 함께 설정해야 합니다. (dst = 결과 이미지)

[C/C++] adaptiveThreshold(src, dst, maxValue, adaptiveMethod, thresholdType, blockSize, C)

 

지역적이면서 적응적 이진화 방법은 이미지에 조명이 균일하지 않거나 배경이 복잡하여 명암이 불균일한 경우 유용할 수 있으며, 문서 이미지에서 문자 추출 시에도 잘 활용될 수 있습니다. 아래는 파이썬과 C/C++에서 “adaptiveThreshold”를 활용한 예이니 참고해서 보면 좋을 듯 합니다.

 

import cv2

# 그레이 이미지 읽기
img = cv2.imread('grayimage.jpg', cv2.IMREAD_GRAYSCALE)

# 가우시안 적응형 이진화
thresh = cv2.adaptiveThreshold(
    img,
    255,
    cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
    cv2.THRESH_BINARY,
    11,
    2
)

cv2.imshow("Adaptive Threshold", thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

#include <opencv2/opencv.hpp>
using namespace cv;

int main() {
    // 이미지 불러오기 (그레이스케일)
    Mat src = imread("grayimage.jpg", IMREAD_GRAYSCALE);
    Mat dst;
    // 적응형 이진화 적용
    adaptiveThreshold(
        src,              // 입력 이미지
        dst,              // 출력 이미지
        255,              // 최대값
        ADAPTIVE_THRESH_GAUSSIAN_C,  // 적응형 방법
        THRESH_BINARY,    // 임계값 타입
        7,               // 블록 크기 (홀수)
        2                 // C 상수
    );

    // 결과 출력
    imshow("Original", src);
    imshow("Adaptive Threshold", dst);
    waitKey(0);
    return 0;
}
728x90
728x90

영상처리 기술은 이미지를 활용하는 다양한 산업분야에서 핵심적인 역할을 하고 있습니다. 현재 많은 분야에서 확장되고 있는 AI분야 머신러닝과 딥러닝에서도 부분적으로 영상기술 이론이 주요하게 사용되고 있습니다. 영상처리 기술을 활용하는 산업 분야를 간략히 요약하면 아래와 같습니다.

 

산업 자동화

  • 제조 공정에서 불량품 검출, 로봇 비전 등에 활용되며, 고속 카메라와 영상분석을 통해 사람의 개입 없이 품질을 관리할 수 있습니다.

의료 영상

  • MRI, CT, X-ray 등에서 병변을 자동으로 식별하거나, 조직을 정량적으로 분석하며, 진단 정확도를 높여 줍니다.

보안 및 감시

  • CCTV 영상에서 이상행동 탐지, 얼굴 인식, 번호판 인식 등에 사용되며, 범죄 예방 및 수사에 핵심적인 역할을 합니다.

자율주행

  • 카메라로 주변 물체를 인식하고, 차선, 신호, 보행자 등을 분석하며, 딥러닝을 활용한 실시간 처리에 핵심적인 역할을 합니다.

엔터테이먼트 및 미디어

  • 영상 편집, 필터 적용, 실시간 합성, 가상현실(VR), 증강현실(AR) 등에서 쓰이며, 사용자 경험 UI/UX에도 활용됩니다.

 

몇 년 사이에 AI기술이 확장 및 확대 되면서 컴퓨터 비전과 영상처리 라이브러리들이 다양화 되었습니다. 특히 파이썬 Python 프로그래밍 활용이 확대 되면서 파이썬 관련 영상처리 라이브러리가 많아진 것을 볼 수 있습니다. 아래는 대표적인 라이브러 설명이며, OpenCV의 경우 참 오래된 라이브러이기도 하고 꾸준히 사용되 되었습니다. AI 성장과 함께 현재는 영상처리 활용에 핵심 라이브러리의 하나이기도 합니다.

 

1) OpenCV (Open Source Computer Vision)

  • 가장 널리 사용되는 오픈소스 라이브러리. 영상 입출력, 필터, 물체 인식 등 광범위한 기능을 제공하며 C/C++과 파이썬에서 활용할 수 있습니다.

2) MATLAB

  • 영상처리 전용 툴박스 제공, 연구 및 교육용으로 활용도 높으며 최근 실무에서도 활용도가 높아지고 있습니다. MATLAB 자체가 유료이므로 라이브러리도 유료로 사용할 수 있습니다.

3) Pillow(PIL)

  • 파이썬 기반 영상 처리 라이브러리이며, 단순한 이미지 처리에 적합합니다.

4) Scikit Image

  • 파이썬 과학 연산 라이브러리 기반 영상처리 라이브러리이며, 연구 및 분석 목적에 적합합니다.

5) PyTorch

  • 파이썬 머신러닝, 딥러닝 기반 영상처리에 적합하며 분류, 객체탐지, 세크멘테이션에 활용 됩니다.

6) MediaPipe

  • 구글에서 개발한 실시간 영상처리 프레임워크이며 얼굴, 손, 포즌 인식에 이용됩니다. C/C++ 및 파이썬을 통해 활용할 수 있습니다.

 

이미지 처리는 단순 필터 적용을 넘어서 AI와 융합되어 인공지능 기술의 시각지능의 핵심이 되고 있습니다. 특히 영상에서의 의미 있는 정보 추출과 자동화가 가능해져 다양한 기술 분야에 확대 적용되고 있습니다. 

 

영상처리 라이브러리 중 MediaPipe에 대해 조금 더 알아 보겠습니다. MediaPipe는 머신러닝 기반의 멀티모달 파이프라인을 쉽게 만들고 실행할 수 있게 해주는 프레임워크입니다. 실시간 영상에서 정교한 분석을 저지연으로 처리할 수 있도록 설계되어, 모바일, 데스크탑, 웹 등 다양한 환경에서 실행 가능합니다.

 

MediaPipe의 주요 특징으로는 CPU에서도 실시간 처리가 가능할 정도로 경량화 된 모델 구조이며, 각 기능이 독립된 컴포넌트로 구성되어 있습니다. 크로스 플랫폼으로 사용 가능하며, 사전 학습된 모델도 제공 됩니다. 기능으로는 얼굴 감지 및 인식, 손 제스처 인식, 포즈 예측, 신체 모션 분석, 3D 객체 인식 등이 있습니다. 아래는 설치 방법 입니다.

 

설치 : pip install mediapipe opencv-python

파이썬 활용 시: import mediapipe as mp

 

추가로 OpenCV는 .NET 프레임워크 환경 C# 등에서도 활용 가능합니다. OpenCvSharp은 C++ OpenCV의 .NET 바인딩 라이브러리입니다. 쉽게 이야기 하면 C++ 코드의 함수를 C#에서 호출하는 방식으로 볼 수 있습니다. 아래 첫번째 예는 OpenCvSharp을 이용한 이미지 읽기이며, 두번째는 웹캠 영상 로드 예 입니다.

 

// 이미지 읽기
using OpenCvSharp;

Mat image = Cv2.ImRead("lena.jpg");
Cv2.ImShow("lena", image);
Cv2.WaitKey(0);

 

// 웹캠 이미지 로드
using OpenCvSharp;

using var capture = new VideoCapture(0);
using var window = new Window("WebCam");

var frame = new Mat();
while (true)
{
    capture.Read(frame);
    if (frame.Empty()) break;

    window.ShowImage(frame);
    if (Cv2.WaitKey(1) == 27) break; // esc 종료
}
728x90
728x90

영상처리 기술에서 보간법은 이미지 확대나 회전, 좌표 변환 기술 Affine Transform 등 알고리즘 구현 시 빈번히 사용됩니다. 머신 러닝 Machine Learning 및 딥러닝 Deep Learning 등 특히 CNN 계열에 Coarse Map을 Dense Map으로 Up-Sampling시 활용되는 기술이기도 합니다. 이미지를 두 배로 확대할 때 보간법은 새로운 픽셀 Pixel 값을 그럴듯하게 예측하는 기술로 볼 수 있습니다. 이론적인 부분은 영상처리 기술에서 다루도록 하겠습니다.

 

기본적인 보간법 세가지는 아래와 같습니다.

 

1. 최근접 이웃 보간법 Nearest Neighbor Interpolation

- 소수점 좌표에서 가장 가까운 정수 좌표 픽셀 사용, 수행속도가 가장 빠르지만 계단 현상 및 깨짐 현상 발생

 

2. 양선형 보간법 Bilinear Interpolation

- 주변 4개 픽셀을 사용해 선형 평균을 이용, 최근접 보다 자연스러운 결과를 보이고 일반적으로 많이 활용되는 기술

 

3. 양큐빅 보간법 Bicubic Interpolation

- 주변 16개 픽셀을 활용하며 앞에 두 기술보다 자연스러우며, Super Resolution 기술들에서 비교 결과로 많이 사용, 고화질 이미지 편집에 사용되는 기술이며 처리속도는 느림

 

이미지 회전 시 보간법이 사용되는 이유는 회전은 수학적으로 기존 픽셀 좌표를 회전 행렬로 이동시키는 과정입니다. 회전 시 생성되는 좌표는 보통 소수점 좌표가 되며, 새로운 평면에 매칭 시 좌표는 소수점이 아닌 정수이기때문에 빈공간이 생기게 됩니다. 그 빈공간을 채워주기 위해 보간법이 활용됩니다. 예를 들어 좌표 (5, 5)를 30도 회전하면 (5.3, 3.9) 되므로 실제 좌표 매칭을 위해서는 주변 픽셀 (5, 3), (6, 3), (5, 4), (6, 4)를 이용하여 추정해야 합니다.

 

OpenCV에서 이미지 회전 및 확대 시 보간법을 지정할 수 있습니다. 아래는 파이썬 활용시 CV 상수 입니다. 첫번째 예제는 회전시 파이썬 코드이며, 이전 블로그에서 설명한 “cv2.warpAffine” 함수를 사용합니다. 두번째 예제는 “cv2.resize” 함수를 활용한 보간법 사용 예입니다. 

 

1. 최근접 이웃 보간법 - cv2.INTER_NEAREST

2. 양선형 보간법 - cv2.INTER_LINEAR

3. 양큐빅 보간법 - cv2.INTER_CUBIC

4. Lanczos 보간법 - cv2.INTER_LANCZOS4

 

# 이미지 회전 예제
import cv2
import numpy as np

# 이미지 로드
img = cv2.imread('lena.jpg')

# 회전 중심, 각도, 스케일
center = (img.shape[1] // 2, img.shape[0] // 2)
angle = 50
scale = 1.0

# 회전 행렬 생성
M = cv2.getRotationMatrix2D(center, angle, scale)

# 양큐빅 보간법 사용예
rotated = cv2.warpAffine(img, M, (img.shape[1], img.shape[0]), flags=cv2.INTER_CUBIC)

# 결과 보기
cv2.imshow('Rotated Image', rotat_result)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

# 이미지 확대 예제
img = cv2.imread('lena.jpg')

# 2배 확대, 선형 보간법
resized = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR)

# 결과 보기
cv2.imshow('Resized Image', resiz_result)
cv2.waitKey(0)
cv2.destroyAllWindows()
728x90
728x90

칼라 분석 처리 목적에 따라 RGB 공간에서 다른 공간으로 변환할 있습니다. OpenCV에서 cvtColor 함수를 사용할 있으며 RGB 공간에서 변환할 경우에는 채널의 순서를 명시적으로 지정해야 합니다. OpenCV 기본 형식은 RGB라고 하는 경우가 많지만 실제로는 BGR입니다. 24비트 이미지의 번째 바이트는 8비트 Blue 성분이고 번째 바이트는 Green, 번째 바이트는 Red 됩니다.

 

아래는 C언어와 Python 활용 따른 예이며 src는 원본, dst는 결과입니다. cvtColor 함수를 활용하면 간단히 색 공간변환을 할 수 있습니다.

 

(C언어): cvtColor(src, dst, cv::COLOR_RGB2GRAY);

(Python): dst = cv.cvtColor(src, cv.COLOR_RGB2GRAY)

 

Luv 같은 비선형 변환은 입력 RGB 영상을 적절한 범위로 정규화하여 처리해야 합니다. 예를 들어 8비트 영상에서 직접 변환된 32비트 부동소수점 영상이 있으면 함수에서 가정하는 0.1 아니라 0.255 범위를 갖게 됩니다. 따라서 cvtColor 호출하기 전에 아래 예와 같이 먼저 영상을 축소해야 합니다.

 

src *= 1./255;

cvtColor(src, dst, COLOR_BGR2Luv);

 

아래는 RGB 공간에서 수식적 변환 관계(Gray, HSV, HLS, Lab)이며  cvtColor의 변환 코드 선택으로 간편히 변환할 수 있습니다. cv::COLOR_RGB2HSV (또는 cv::COLOR_BGR2HSV) 등.

RGB to GRAY 변환

RGB to Gray: Y = 0.299⋅R+0.587⋅G+0.114⋅B

 

RGB to HSV 변환 (Hue, Saturation, Value)

R, G, B 부동 소수점 형식으로 변환하여 [0~1] 범위에 맞게 스케일링 계산 적용합니다.

 

RGB to HLS 변환 (Hue, Luminance, Saturation)

R, G, B 부동 소수점 형식으로 변환하여 [0~1] 범위에 맞게 스케일링  계산 적용합니다.

 

RGB to Lab 변환

R, G, B 부동 소수점 형식으로 변환하여 [0~1] 범위에 맞게 스케일링  계산 적용합니다.

(docs.opencv.org 참조)

728x90
728x90

(docs.opencv.org 참조)

영상처리의 가장 기본이 되는 연산 중에는 픽셀 Pixel 연산과 마스크 Mask 연산이 있습니다. 현 블로그에서는 픽셀 간 산술 연산 Arithmetic Operation과 그 응용인 이미지 혼합 Image Blending을 간략히 다루겠습니다. 아래 예에서는 파이썬 코드로 설명하지만 당연히 C언어 함수로도 제공됩니다.

 

이미지 덧셈 Image Addition

덧셈 함수는 cv.add(input1, input2)로 사용할 수 있으며, input1과 input2는 모두 깊이 depth와 유형이 동일해야 하거나 input2는 스칼라 scalar 일 수 있습니다. 아래 예에서 같이 Numpy 연산으로도 이미지 덧셈 연산을 할 수 있습니다. 다만 결과에서 같이 OpenCV 연산은 포화 Saturation 연산이며 Numpy는 modulo 연산이라는 점에 주의해야 합니다.

 

CV 연산과 Numpy 연산 예)

x = np.uint8([250])

y = np.uint8([10])

z1 = cv.add(x,y) # 250+10 = 260 => 255

z2 = x+y                # 250+10 = 260 % 256 = 4

 

이미지 뺄셈, 곱셈, 나눗셈도 각 cv.substract, cv.multiply, cv.divide로 제공되며 형식은 덧셈 연산과 동일합니다.

 

이미지 혼합 Image Blending

이미지 간 혼합감이나 투명감을 주기 위해 이미지 별 다른 가중치를 부여할 수 있습니다. 단순 연산에 응용으로 Result = (1−α)Image1 + αImage2 처럼 가중치를 다르게 적용하여 두 개의 이미지를 혼합 할 있습니다. OpenCV에서는 cv.addWeighted 함수로 제공되며 두 개의 가중치 α, β와 하나의 offset γ을 활용하여 처리할 수 있습니다. 두 개의 가중치의 합은 항상 1이 되어야 합니다. 그림에서는 로봇에 0.7의 가중치를 OpenCV 로고 이미지에 0.3의 가중치로 혼합한 결과 입니다.

 

result =α⋅img1+β⋅img2+γ

result = cv.addWeighted(img1, α, img2, β, γ)

 

 

이미지 논리 연산 Logical Operations

논리 연산자에는 AND, OR, XOR, NOT 가 있습니다. 논리 연산은 이진 Binary 이미지에 적용되며 픽셀 단위로 수행됩니다. 이진 이미지 처리 시 마스킹, 특정 처리, 형태 분석 등 활용될 수 있습니다. 그림에서 같이 AND의 경우 두 입력이 모두 1일 때 1이 되며, OR 연산은 두 입력 모두 또는 하나가 1일 때 1이 됩니다.

 

 

NOT 연산 cv. bitwise_not() 함수를 이용하며 Result = -Input과 같이 입력에 대한 반전과 같습니다. AND 연산은 아래와 같이 활용할 수 있으며 OR 및 XOR도 각 cv.bitwise_or(), cv.bitwise_xor()로 테스트 해 볼 수 있습니다. Input1과 Input2는 모두 동일한 배열이거나 또는 Input1은 배열, Input2는 Scalar가 될 수 있습니다.

                    

AND 연산 파이썬 예)

Result = cv.bitwise_and(Input1, Input2)

728x90
728x90

(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 활용 예입니다.

 

728x90
728x90

(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 입니다. 

 

728x90
728x90

영상처리 알고리즘 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 로도 응용되고 실생활에서 자주 접할 수 있는 기능이기도 합니다.

728x90
728x90

윤곽선 검출 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의 수학적 또는 알고리즘 접근에 대해서는 영상처리기술에서 추가적으로 다뤄보도록 하겠습니다.

728x90

+ Recent posts