Dilation, Erosion and Edge Detection

김성빈·2024년 5월 1일
0

Modern Computer Vision

목록 보기
13/117

Dilation, Erosion and Edge Detection

(확장, 침식 및 에지 탐지)

오늘 배울 내용은 아래 5가지 이다.

  1. Dilation
  2. Erosion
  3. Opening
  4. Closing
  5. Canny Edge Detection
  • Dilation – Adds pixels to the boundaries of objects in an image
    이미지의 물체 경계에 픽셀을 추가
  • Erosion – Removes pixels at the boundaries of objects in an image
    이미지 경계에 있는 픽셀 제거
  • Opening - Erosion followed by dilation
  • Closing - Dilation followed by erosion

이미지를 보면 original 과 비교해서 Erosion은 작아지고, Dilation은 더 두꺼워 지는것을 알 수 있다.

모폴로지 연산(1,2,3,4)

그럼 실제로 코드로 적용한것을 보자.

import cv2
import numpy as np

image = cv2.imread('images/opencv_inv.png', 0)
imshow('Original', image)

# Let's define our kernel size
kernel = np.ones((5,5), np.uint8)

# Now we erode
erosion = cv2.erode(image, kernel, iterations = 1)
imshow('Erosion', erosion)

# Dilate here
dilation = cv2.dilate(image, kernel, iterations = 1)
imshow('Dilation', dilation)

# Opening - Good for removing noise
opening = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
imshow('Opening',opening)

# Closing - Good for removing noise
closing = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)
imshow('Closing',closing)

위에 나온 설명처럼 Erosion은 얇아지고, Dilation은 굵어진다.

추가로 opening과 closing에 대한 이미지 결과인데.
위에 opening과 closing에 대해 다시 한번 보자.

- Opening - Erosion followed by dilation
- Closing - Dilation followed by erosion 

opening은 erosion한것을 dilation 하는것이고
closing은 dilation 한것을 erosion 한 것이다.

그럼 opening은 얇은 이미지가 두꺼워지고 closing은 두꺼운 이미지가 얇아진다는 뜻이다.

코드의 결과는 봤으니 이제 왜 그러한지 이유를 살펴보자.

처음 단계에는 kernel을 만드는데 이건 모폴로지 연산을 할때 필요하다.

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

모폴로지 연산이란?
영상 처리에서 형태학적인 변형을 가하는 기술
주로 바이너리 이미지(흑백 이미지)에서 전경(밝은 부분)과 배경(어두운 부분)을 기반으로 수행하며

주요 연산은 침식(Erosion), 팽창(Dilation), 열기(Opening), 그리고 닫기(Closing)가 있다.

여기서 사용되는 모폴로지 연산은 나중에 객체 감지, 세그멘테이션, 노이즈 제거 등 다양한 영상처리에 사용된다.

모폴로지 연산된것은 총 4가지로 위에서 말한 침식, 팽창, 열기, 닫기로 구성돼있다.

  1. erosion = cv2.erode(image, kernel, iterations = 1)
  2. dilation = cv2.dilate(image, kernel, iterations = 1)
  3. opening = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
  4. closing = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)

침식, 팽창은 이미지, 커널, 몇번 반복할건지에 대한 iterations를 인자로 갖고있고, 열기, 닫기는 이미지, Opne,Close에 대한 인자, 커널 이 세가지를 갖고있다.

  1. Canny Edge Detection(에지 감지)

Canny edge detection 을 살펴보기 전에 검출(detection)이 뭔지부터 알아보자.

사전적 정의는

"에지 검출은 이미지에서 대상의 경계를 찾는 기술"

이며 찾는 기술에는 알고리즘이 사용되며 그 알고리즘 중 높은 정확도를 갖는 것들중 하나가 Canny edge detection 이다.

그럼 바로 사용해보자.

아래는 Canny edge detection을 적용할 원본 이미지 이며 gray scale을 적용한 흑백 사진이다.

image = cv2.imread('images/londonxmas.jpeg',0)

imshow('image', image)

Canny edge detection 함수의 인자부터 살펴보면

cv2.Canny(image, threshold1, threshold2)

image: 에지를 찾을 입력 이미지입니다. 일반적으로 그레이스케일 이미지를 사용
threshold1: 에지를 검출하는 데 사용되는 첫 번째 임계값, 이 임계값보다 큰 그래디언트 값은 강한 에지로 간주
threshold2: 에지를 검출하는 데 사용되는 두 번째 임계값, 이 임계값은 약한 에지를 결정합니다.

image = cv2.imread('images/londonxmas.jpeg',0)

imshow('image', image)
# Canny Edge Detection은 그라데이션 값을 임계값으로 사용합니다
# 첫 번째 임계값 기울기
canny = cv2.Canny(image, 50, 120)
imshow('Canny 1', canny)

# 넓은 에지 임계값은 많은 에지를 예상합니다
canny = cv2.Canny(image, 10, 200)
imshow('Canny Wide', canny)

# 좁은 임계값, 더 적은 에지 예상
canny = cv2.Canny(image, 200, 240)
imshow('Canny Narrow', canny)

canny = cv2.Canny(image, 60, 110)
imshow('Canny 4', canny)

위의 코드 내용들의 threshold 값에 대한 설명을 글로 푼것과 사진을 같이 나열하겠다.

1. Canny 1: threshold1 = 50, threshold2=120

threshold1이 작기 때문에 더 많은 에지가 감지
에지가 감지되는 기준이 낮기 때문에 강한 에지와 약한 에지 모두 감지
하지만 threshold2가 threshold1보다 크기 때문에 약한 에지 중에서 threshold2보다 작은 값의 그래디언트는 제거
결과적으로 threshold1보다 크고 threshold2보다 작은 그래디언트 값이 강한 에지로 간주

2. Canny Wide: threshold1= 10, threshold2= 200

많은 수의 강한 에지만 남게 됨

threshold1이 매우 작기 때문에 많은 수의 에지가 감지
감지되는 모든 그래디언트 값은 강한 에지로 간주
그러나 threshold2가 threshold1보다 크기 때문에 약한 에지는 거의 모두 제거
결과적으로는 많은 수의 강한 에지만 남게 된다.

3. Canny Narrow: threshold1=200, threshold2=240

적은 수의 강한 에지만 남게 됨

threshold1이 매우 크기 때문에 적은 수의 강한 에지만 감지
따라서 강한 에지 중에서도 threshold2보다 큰 값의 그래디언트는 제거
결과적으로는 적은 수의 강한 에지만 남게 된다.

4. Canny 4: threshold1=60, threshold2=110

threshold1과 threshold2의 차이가 작기 때문에 중간 정도의 수의 에지가 감지
강한 에지 중에서도 threshold2보다 큰 값의 그래디언트는 제거
하지만 threshold1보다 크고 threshold2보다 작은 그래디언트 값은 강한 에지로 간주

결론은 가장 많은 경계를 남기는 코드는
Canny Wide 이며 가장 적은 경계를 남기는 것은 Canny Narrow이다.

경계를 많이 나타내는 것에 대한 인자는 threshold1의 영향이 더 크며 threshold2는 그 위의 값들을 제거 하는 의미로 최대 지점의 의미를 갖는다.

이렇게 수동으로 임계값을 적는 방법도 있지만,

StackOverflow에서 자동으로 임계값을 선택해주는 함수를 개발한 사람의 방식도 보여주겠다.

AutoCanny

코드는 아래로 간단하다.

먼저 이미지를 흐리게한다음 이미지 픽셀값의 중간값과 최댓값을 이용해서 임계값을 얻어서 함수로 만든 내용이다.

def autoCanny(image):
  blurred_img = cv2.blur(image, ksize=(5,5))
  med_val = np.median(image)
  lower = int(max(0, 0.66 * med_val))
  upper = int(min(255, 1.33 * med_val))
  edges = cv2.Canny(image=image, threshold1=lower, threshold2=upper)
  return edges

auto_canny = autoCanny(image)
imshow("auto canny", auto_canny)

높은 퀄리티의 경계 이미지가 나왔지만 필요하다면 직접 조정해서 최적의 이미지를 찾는 시간을 갖는것도 필요하다.

profile
감사합니다. https://www.youtube.com/channel/UCxlkiu9_aWijoD7BannNM7w

0개의 댓글