< 9-2. Image Inpainting >
이번 장에서는,
- 오래된 사진에서의 작은 노이즈나, 스트로크를 제거하는 법인 “Inpainting”에 대해서 배워볼 것이다.
- OpenCV를 통해서 Inpainting을 구현해 볼 것이다.
Basics
집안을 찾아보면 검은 점이 찍인, 화질이 번져있는 사진을 찾아볼 수 있다. 이를 복원하기 위한 방법을 생각해 본적이 있는가? 일반적인 페인팅 도구로는 이를 간단하게 지울 수 없다. 왜냐하면 이는 단순히 아무 의미 없는 흰색 구조로 단순히 검은색을 대체할 뿐이기 때문이다. (그냥 0을 1로 바꾸는 것 밖에 의미가 없음) 이러한 상황에서, 이미지 인페인팅이라고 불리는 기술이 사용된다. 이 기법의 기본 아이디어는 간단하다 : 노이즈 부분을 주변 픽셀들로 대체하여, 주변부처럼 자연스레 보이도록 하는 것이다. 아래 이미지를 보면 이해가 될 것이다. (우측 사진은 번진 부분을 주변부 픽셀로 채운 사진이다.)
cv2.inpaint()로 구현할 수 있다.
첫 알고리즘은 “An Image Inpainting Technique Based on the Fast Marching Method” 의 저자인 Alexandru Telea의 논문에서 등장했다. Fast Matching Method에 기반한 방법이다. 이미지에서 칠해질 부분을 고려한 것이다. 알고리즘은 이 영역들 경계에서 시작하여 영역의 안쪽으로 들어가는데 서서히 경계선부터 칠해진다. 주변부의 픽셀을 조금 취해서 천천히 칠해나간다, 이 픽셀은 주변부의 픽셀들의 합을 정규화하여 가중치를 부여한 값으로 대체된다. 가중치를 정하는 것은 매우 중요한 부분이다. 경계 부근의 픽셀에 대해서 더 많은 가중치를 부여한다. 픽셀이 채워지면, Fast Marching Method 방법을 사용하여 다음 가까운 픽셀로 이동하게 된다. FMM은 알려진 픽셀 근처의 픽셀을 먼저 채워서 수동 휴리스틱(manual heuristic) 작업처럼 작동하도록 한다. 이 알고리즘은 cv2.INPAINT_TELEA 플래그를 써서 사용할 수 있다.
두 번째 알고리즘은 2001년 “Navier-Stokes, Fluid Dynamics, and Image and Video Inpainting” 의 저자인 Bertalmio, Marcelo, Andrea L. Bertozzi, and Guillermo Sapiro들에 의해 등장했다. 이 알고리즘은 유체 역학을 기반으로 편미분 방정식을 이용한다. 기본 원리는 휴리스틱하다. 이는 알고 있는 영역부터 모르는 영역(색X)까지의 모서리를 돌아다닌다. (모서리는 연속적인 부분이기에) 칠해야 할 영역의 경계에서 gradient vector들을 일치시키면서 등조건을 연속화한다. 색은 영역의 최소 분산을 줄이기 위해 채워진다. 이 알고리즘은 cv2.INPAINT_NS를 사용하여 구현할 수 있다.
Code
입력 이미지와 같은 크기의 마스크를 생성해야 한다.(0이 아닌 픽셀들이 칠해져야 할 영역과 일치해야 한다.) 나머지는 간단하다. 이미지는 검정색으로 그어져 있다. 페인트 도구로 스트로크에 대응하는 것을 생성했다.
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./images/apple1.jpg')
plt.imshow(img[:,:,::-1],'gray')
plt.axis('off')
plt.savefig('OrigApple.jpg')
noise = cv2.imread('./images/apple2.jpg',0)
noise = cv2.resize(noise,(img.shape[1],img.shape[0]))
print(noise.shape == img.shape)
plt.imshow(noise,'gray')
plt.axis('off')
plt.savefig('noiseGray.jpg')
dst = cv2.inpaint(img,noise,3,cv2.INPAINT_TELEA)
plt.imshow(dst[:,:,::-1])
plt.axis('off')
plt.savefig('ResApple.jpg')
결과를 보자! 두 번째 사진은 마스크로 지우고자 하는 이미지 영역을 나타낸다. 결과를 보면 사과부분이 지워지고 주변부 픽셀로 채워진 것을 볼 수 있다. 조금 많이 어색하지만, 이는 다른 방법, GAN같은 것으로 하면 더 잘 될 것으로 생각이 된다. 그래도 엄청 간단한 방법으로 나름 잘 지워진 것 같아서 신기하다... Inpainting 분야는 더 찾아보면 더 좋은 방법으로 더 좋은 효율/효과를 볼 수 있을 것 같다.