[OpenCV] 라플라스 연산자 (Laplace Operator)
이미지 처리에서 sharpening이라고 하는 즉 선명도를 증가시키는 방법이 몇 가지 있습니다. 가장 대표적인 방법으로 다음의 4가지를 뽑을 수 있고, 이 포스팅에선 라플라스 연산자에 대해서 보도록 하겠습니다.
- Laplace operator
- Difference of Gaussians (DoG)
- Unsharp Filter
2020/10/27 - [분류 전체보기] - [OpenCV] 이미지 경계선 강화: Unsharped 필터 - 파이썬 코드 포함
2020/10/27 - [OpenCV] - [OpenCV] 이미지 경계선 강화: DoG - 파이썬 코드 포함
Laplace operator
라플라스 연산자는 주로 edge를 감지하거나 동작을 잡아내는데 사용된다. 이것은 공간에 대한 2차 미분으로 정의된다. 여기에서 f(x,y)는 2차원 이미지의 픽셀 값의 함수이다.
L=∇2f(x,y)=[∂2∂x2+∂2∂y2]f(x,y)
아래 왼쪽 그림에서 보이는 바와 같이 edge에서는 대개 픽셀 값이 많이 변하게 된다. 이것에 대한 1차 미분으로 가장 급격하게 변하는 부분을 잡아 낼 수 있다.

2차 미분을 하면 아래와 같이 변곡점에서 0이 되는 지점을 찾아낸다. 이것을 바로 픽셀값이 급격히 바뀌는 즉 이미지 내의 edge로 간주하고, edge 주변으로 픽셀 값이 역시 크게 변하는 것을 알 수 있다.

실제 이미지 처리에서는 불연속적으로 근사된 라플라스 필터를 이미지에 합성곱으로 적용한다. 여기에 사용되는 라플라스 필터는 이미지보다 크기가 훨씬 작다. 주로 3x3의 행렬이 쓰인다. 다음의 세 종류가 보통이다.

아래의 코드는 파이썬으로 이미지를 읽고 라플라스와 가우시안 필터를 적용해 본 것이다. 각 필터링에 두가지 방법, OpenCV이용한 것과 직접 짠 코드, 를 사용하여 결과를 비교하였다.
import os | |
import cv2 | |
import numpy as np | |
import matplotlib.pyplot as plt | |
class LaplaceOperator: | |
def __init__(self): | |
path=os.getcwd() | |
file = ''.join(path+"/wdg1.gif") | |
gif = cv2.VideoCapture(file) | |
ret, self.originalImage = gif.read() | |
def Laplacian_by_OpenCV(self): | |
ddepth = cv2.CV_16S | |
kernel_size = 3 | |
self.src_gray = cv2.cvtColor(self.originalImage,cv2.COLOR_BGR2GRAY) | |
self.src_blur = cv2.GaussianBlur(self.src_gray,(3,3),0) | |
self.dst = cv2.Laplacian(self.src_blur, ddepth, ksize = kernel_size) | |
self.output = cv2.convertScaleAbs(self.dst) | |
def replicate_boundary(self,r_curr, c_curr,row,col): | |
r_temp = r_curr | |
c_temp = c_curr | |
if r_temp<0: | |
r_temp +=1 | |
elif r_temp>=row: | |
r_temp -=1 | |
if c_temp<0: | |
c_temp +=1 | |
elif c_temp>=col: | |
c_temp -=1 | |
return r_temp, c_temp | |
# edge detecting filter | |
def Laplacian_from_scratch(self): | |
self.kernel = np.array([[0,-1,0],[-1,4,-1],[0,-1,0]],dtype=np.float32) | |
row = len(self.dst) | |
col = len(self.dst[0]) | |
self.mat_filtered = np.zeros((row,col)) | |
for r in range(row): | |
for c in range(col): | |
for i in range(3): | |
for j in range(3): | |
r_temp, c_temp = self.replicate_boundary(r+i-1,c+j-1,row,col) | |
self.mat_filtered[r,c] += self.src_blur[r_temp, c_temp]*self.kernel[i,j] | |
def Gaussian_from_scratch(self): | |
self.gkernel = np.array([[1,2,1],[2,4,2],[1,2,1]],dtype=np.float32)/16 | |
row = len(self.dst) | |
col = len(self.dst[0]) | |
self.gfiltered = np.zeros((row,col)) | |
for r in range(row): | |
for c in range(col): | |
for i in range(3): | |
for j in range(3): | |
r_temp, c_temp = self.replicate_boundary(r+i-1,c+j-1,row,col) | |
self.gfiltered[r,c] += self.src_gray[r_temp,c_temp]*self.gkernel[i,j] | |
def showImage(self, imgArray, title): | |
plt.imshow(imgArray,cmap="gray") | |
plt.title(title) | |
다음과 같이 실행해 볼 수 있으며 아래 세 그림으로 결과를 볼 수 있다.


위의 오른쪽 그림에서 edge는 뚜렷해진 것을 확인할 수 있지만, 전체적으로 노이즈가 증가했다. 이것은 라플라스 필터링이 노이즈에 민감하게 작용하기 때문이다. 이것은 보완하는 다른 알고리즘은 Unsharp filter를 사용하는 것이고, 이것은 이어지는 포스팅에서 살펴보도록 하겠습니다.
마지막으로 직접 짠 코드로 적용한 Laplacian 와 Gaussian filter결과 입니다. 아래와 같이 실행하여 결과를 확인할 수 있습니다.


참고 링크 및 이미지 출처
'Programming > Computer Vision' 카테고리의 다른 글
[OpenCV] 이미지 이진화(Image binarization)를 이용한 image segmentation (Python) (0) | 2021.06.28 |
---|---|
[OpenCV] K-Means를 이용한 Image Segmentation(이미지 분할) (0) | 2020.11.05 |
[OpenCV] 이미지 경계선 강화: DoG - 파이썬 코드 포함 (0) | 2020.11.04 |
[OpenCV] 이미지 경계선 강화: Unsharp 필터 - 파이썬 코드 포함 (0) | 2020.11.04 |
[OpenCV] 이미지 노출 융합 (exposure fusion) 이란? (1) | 2020.11.01 |
댓글