想想一下,我们有一张非常棒的相片,但是由于时间比较久远,没有电子版留底,而纸质版的又十分不便于保存。因此长采用扫描的方式获得电子版。但是非常不幸,扫描过程中落入了一根头发,或者是机器出现故 障,对相片造成了影响,这个时候就可以通过图像修复技术解决这个问题。
完整代码地址:https://github.com/YouthJourney/Computer-Vision-OpenCV/tree/master/image_restoration
1、Inpaint_ns:基于Navier-Stokes的图像修复
该方法在2001年提出,其神奇之处竟然是基于流体力学理论提出的方法。根据其作者提出,我们需要解决的问题可以抽象成在一个鞋子图片上有一个黑色的区域,通过填充黑色区域,使得最佳的恢复鞋子的样子。
对于如何填补这个黑色区域,可以抽象成存在一条曲线,使得由A到B将黑色区域分开,并且保证在曲线的一 侧是蓝色,另一侧是白色。这个曲线应具有如下的约束:
1. 保持边缘特征
2. 在平滑区域中保持颜色信息
通过构建一个偏微分方程来更新具有上诉约束的区域内的图像强度,同时利用拉普拉斯算子估计图像平滑度信息,并沿着等照度传播。
由于这些方程与Navier-Stokes方程(流体力学中的方程,感兴趣的小伙伴可以自行百度)相关且类似,因此可以通过流体力学中的方法进行求解。
本人对流体力学了解的不是很仔细,有需要了解的小伙伴可以阅读该论文:
http://www.math.ucla.edu/~bertozzi/papers/cvpr01.pdf
2、Inpaint_Telea:基于快速行进方法的图像修复。
该方法中没有使用拉普拉斯算子作为平滑度的估计,而是使用像素的已知图像邻域上的加权平均值来描述。同时利用邻域像素和梯度恢复填充区域像素的颜色。当像素被修复后,通过快速行进方法更新边界。关于细节部分,可参考论文:https://pdfs.semanticscholar.org/622d/5f432e515da69f8f220fb92b17c8426d0427.pdf
下面以亚历山大·加德纳拍摄的林肯总统有裂痕的图片为例做图像修复:
从左至右依次是输入图像、掩膜、INPAINT_TELEA的结果、INPAINT_NS的结果。
代码如下:
# -*- coding: UTF-8 -*-
# Author: LGD
# FileName: pic_restoration
# DateTime: 2020/10/18 17:11
# SoftWare: PyCharm
import numpy as np
import cv2 as cv
# OpenCV Utility Class for Mouse Handling
from pip._vendor.certifi.__main__ import args
class Sketcher:
def __init__(self, windowname, dests, colors_func):
self.prev_pt = None
self.windowname = windowname
self.dests = dests
self.colors_func = colors_func
self.dirty = False
self.show()
cv.setMouseCallback(self.windowname, self.on_mouse)
def show(self):
cv.imshow(self.windowname, self.dests[0])
cv.imshow(self.windowname + ": mask", self.dests[1])
# onMouse function for Mouse Handling
# 使用鼠标把掩膜画出来也就是第二幅图的白色线条
def on_mouse(self, event, x, y, flags, param):
pt = (x, y)
if event == cv.EVENT_LBUTTONDOWN:
self.prev_pt = pt
elif event == cv.EVENT_LBUTTONUP:
self.prev_pt = None
if self.prev_pt and flags & cv.EVENT_FLAG_LBUTTON:
for dst, color in zip(self.dests, self.colors_func()):
cv.line(dst, self.prev_pt, pt, color, 5)
self.dirty = True
self.prev_pt = pt
self.show()
def main():
print("Usage: python inpaint " )
print("Keys: ")
print("t - inpaint using FMM")
print("n - inpaint using NS technique")
print("r - reset the inpainting mask")
print("ESC - exit")
# Read image in color mode
# 读取输入图片
img = cv.imread("Lincoln.jpg", cv.IMREAD_COLOR)
# If image is not read properly, return error
if img is None:
print('Failed to load image file: {}'.format(args["image"]))
return
# Create a copy of original image
img_mask = img.copy()
# Create a black copy of original image
# Acts as a mask
inpaintMask = np.zeros(img.shape[:2], np.uint8)
# print(inpaintMask)
# Create sketch using OpenCV Utility Class: Sketcher
sketch = Sketcher('image', [img_mask, inpaintMask], lambda: ((255, 255, 255), 255))
while True:
ch = cv.waitKey()
if ch == 27:
break
if ch == ord('t'):
# Use Algorithm proposed by Alexendra Telea: Fast Marching Method (2004)
res = cv.inpaint(src=img_mask, inpaintMask=inpaintMask, inpaintRadius=3, flags=cv.INPAINT_TELEA)
cv.imshow('Inpaint Output using FMM', res)
if ch == ord('n'):
# Use Algorithm proposed by Bertalmio, Marcelo, Andrea L. Bertozzi, and Guillermo Sapiro:
# Navier-Stokes, Fluid Dynamics, and Image and Video Inpainting (2001)
res = cv.inpaint(src=img_mask, inpaintMask=inpaintMask, inpaintRadius=3, flags=cv.INPAINT_NS)
cv.imshow('Inpaint Output using NS Technique', res)
if ch == ord('r'):
img_mask[:] = img
inpaintMask[:] = 0
sketch.show()
print('Completed')
if __name__ == '__main__':
main()
cv.destroyAllWindows()