조금씩 꾸준히 완성을 향해

[OpenCV/Python] Edge 필터링 (Sobel, Laplacian) 본문

Python/OpenCV

[OpenCV/Python] Edge 필터링 (Sobel, Laplacian)

all_sound 2022. 11. 13. 13:31

Edge 추출

# 미분과 경사도

  • 함수 또는 데이터의 변화율
  • 함수의 순간 변화율

# sobel filter

  • 영상에서의 1차 미분 : 엣지 존재 여부 파악

▶ sobel 필터 후 나온 원본 gxo 값에 절대 값 + 루트 씌우기

import cv2
import numpy as np
import matplotlib.pyplot as plt
img = np.zeros(shape=(512, 512), dtype=np.uint8) +255

pt1 = 200, 200
pt2 = 300, 300
src = cv2.rectangle(img, pt1, pt2, (0,0,0), -1)

# x방향 y방향으로 sobel 필터 적용 후 이미지 밝기값들 확인해 보기
gx = cv2.Sobel(src, ddepth=-1, dx=1, dy=0, ksize=3)
gy = cv2.Sobel(src, ddepth=-1, dx=0, dy=1, ksize=3)
gxo = cv2.Sobel(src, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=3)
gyo = cv2.Sobel(src, ddepth=cv2.CV_32F, dx=0, dy=1, ksize=3)
# ddepth=cv2.CV_32F (원본 결과 보기: float32bit)
# ddepth=-1 (자동 계산: 마이너스 값은 0으로 변환)

print(' - ddepth=-1')
print(gx[195:205, 195:205])
print(gx[295:305, 295:305])
print(gy[195:205, 195:205])
print(gy[295:305, 295:305])

print(' - ddepth=cv2.CV_32F')
print(gxo[195:205, 195:205])
print(gxo[295:305, 295:305])
print(gyo[195:205, 195:205])
print(gyo[295:305, 295:305])

#절대값 쓰우기
gxo = np.abs(gxo)
gyo = np.abs(gyo)
print('- 32F after absolute')
print(gxo[195:205, 195:205])
print(gxo[295:305, 295:305])
print(gyo[195:205, 195:205])
print(gyo[295:305, 295:305])

#루트 씌우기
gxo = np.sqrt(gxo)
gyo = np.sqrt(gyo)
print('- 32F after root')
print(gxo[195:205, 195:205])
print(gxo[295:305, 295:305])
print(gyo[195:205, 195:205])
print(gyo[295:305, 295:305])
# 이미지 결과 보기
images = [src, gxo, gyo, gxo+gyo]
titles = ['original','gxo', 'gyo', 'gxo+gyo']
fig = plt.figure(figsize=(10, 10))
for i in range(4):
    ax = plt.subplot(2, 2, i+1)
    ax.imshow(images[i], 'gray')
    ax.set_title(titles[i])
    ax.axis('off')
plt.show()

  • normalize : 0~255로 스트레칭 적용
gxo = cv2.normalize(gxo, 0, 255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
gyo = cv2.normalize(gyo, 0, 255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
print('- 32F after stretching')
print(gxo[195:205, 195:205])
print(gxo[295:305, 295:305])
print(gyo[195:205, 195:205])
print(gyo[295:305, 295:305])

cv2.imshow('gxo', gxo)
cv2.imshow('gyo', gyo)
cv2.imshow('gxo+gyo', gxo+gyo)
cv2.waitKey()
cv2.destroyAllWindows()

 

  • magnitude => 넘파이(절대값, 루트) 대신 사용 가능
  • sobel + magnitude + nomalize
src = cv2.rectangle(img, pt1, pt2, (0,0,0), -1)

# cv2.CV_32F 원본 필수!
gxo = cv2.Sobel(src, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=3)
gyo = cv2.Sobel(src, ddepth=cv2.CV_32F, dx=0, dy=1, ksize=3)

mag = cv2.magnitude(gxo, gyo)
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(mag)
print('mag:', minVal, maxVal, minLoc, maxLoc)
# mag: 0.0 1081.8734130859375 (0, 0) (200, 200)

dstM = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(dstM)
print('mag:', minVal, maxVal, minLoc, maxLoc)
# mag: 0.0 255.0 (0, 0) (200, 200)

cv2.imshow('dstM', dstM)
cv2.waitKey()
cv2.destroyAllWindows()

# 소벨 적용해서 레나 엣지 확인

src = cv2.imread('./lena.jpg', cv2.IMREAD_GRAYSCALE)

gxo = cv2.Sobel(src, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=7)
gyo = cv2.Sobel(src, ddepth=cv2.CV_32F, dx=0, dy=1, ksize=7)

mag = cv2.magnitude(gxo, gyo)
dstM = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)

cv2.imshow('src', src)
cv2.imshow('dstM', dstM)
cv2.waitKey()
cv2.destroyAllWindows()

 

# Laplacian(라플라시안)

  • 영상에서의 2차 미분 : 밝기 변화 파악 가능
  • 잡음에 취약, 성능 개선 크지 않음 => 1차 미분을 주로 많이 사용
# 마스크가 하나여서 바로 src를 집어넣으면 됨
lap = cv2.Laplacian(src, cv2.CV_32F)
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(lap)
print('lap:', minVal, maxVal, minLoc, maxLoc)
print(lap[:10, :10])

# 절대값으로 변경
dst = cv2.convertScaleAbs(lap)
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(dst)
print('lap:', minVal, maxVal, minLoc, maxLoc)
print(dst[:10, :10])

# 스트레칭
dst = cv2.normalize(dst, None, 0, 255, cv2.NORM_MINMAX)
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(dst)
print('lap:', minVal, maxVal, minLoc, maxLoc)
print(dst[:10, :10])

cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

# 2차 미분 => 잡음 많음

 

  • 가우시안 필터 적용 후 라플라시안 결과 비교
lap = cv2.Laplacian(src, cv2.CV_32F)
dst = cv2.convertScaleAbs(lap)
dst = cv2.normalize(dst, None, 0, 255, cv2.NORM_MINMAX)

blur = cv2.GaussianBlur(src, ksize=(3,3), sigmaX=0.0)
lap2 = cv2.Laplacian(blur, cv2.CV_32F)
dst2 = cv2.convertScaleAbs(lap2)
dst2 = cv2.normalize(dst2, None, 0, 255, cv2.NORM_MINMAX)

cv2.imshow('dst', dst)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()