(Python)从零开始,简单快速学机器仿人视觉Opencv---运用四:图像损痕修复

教程:

博主之前写了24节关于使用OpenCV的教程,欢迎大家阅读:
(Python)从零开始,简单快速学机器仿人视觉Opencv—第一节:OpenCV的图像读取显示及保存
(Python)从零开始,简单快速学机器仿人视觉Opencv—第二节:OpenCV的视频操作
(Python)从零开始,简单快速学机器仿人视觉Opencv—第三节:OpenCV中的绘图函数
(Python)从零开始,简单快速学机器仿人视觉Opencv—第四节:OpenCV处理鼠标事件
(Python)从零开始,简单快速学机器仿人视觉Opencv—第五节:OpenCV用滑动条做调色板
(Python)从零开始,简单快速学机器仿人视觉Opencv—第六节:OpenCV图像的一些基本操作
(Python)从零开始,简单快速学机器仿人视觉Opencv—第七节:图像上的算术运算
(Python)从零开始,简单快速学机器仿人视觉Opencv—函数讲解1:cv2.flip()函数
(Python)从零开始,简单快速学机器仿人视觉Opencv—第八节:程序性能检测及优化
(Python)从零开始,简单快速学机器仿人视觉Opencv—第九节:颜色空间转换
(Python)从零开始,简单快速学机器仿人视觉Opencv—第十节:五种常见的几何变换
(Python)从零开始,简单快速学机器仿人视觉Opencv—第十一节:图像阀值
(Python)从零开始,简单快速学机器仿人视觉Opencv—第十二节:几种常见的图像滤波、平滑
(Python)从零开始,简单快速学机器仿人视觉Opencv—第十三节:形态学转换
(Python)从零开始,简单快速学机器仿人视觉Opencv—第十四节:图像梯度
(Python)从零开始,简单快速学机器仿人视觉Opencv—第十五节:Canny边缘检测
(Python)从零开始,简单快速学机器仿人视觉Opencv—第十六节:图像金字塔
(Python)从零开始,简单快速学机器仿人视觉Opencv—第十七节:轮廓详解与运用
(Python)从零开始,简单快速学机器仿人视觉Opencv—第十八节:轮廓的性质
(Python)从零开始,简单快速学机器仿人视觉Opencv—第十九节:关于轮廓的函数
(Python)从零开始,简单快速学机器仿人视觉Opencv—第二十节:轮廓的层次结构
(Python)从零开始,简单快速学机器仿人视觉Opencv—运用一:快速截取图像中指定单个物体
(Python)从零开始,简单快速学机器仿人视觉Opencv—运用二:物体检测
(Python)从零开始,简单快速学机器仿人视觉Opencv—运用三:物体运动跟踪

主题

  在本节中我们将描述一种称为图像修复的区域填充算法。
  这种图片修复算法的作用是可以通过使用OpenCV模块来进行图片上异常划痕或斑点等噪线、噪点的修复,而且代码相对其他的图片修复算法而言要稍微简单一些。(最后效果类似于PhotoShop)
  图像修复算法是计算机仿人视觉中的一类基本算法,算法的主要目标是填充图像或视频内的区域,该区域主要使用二进制掩模来进行标识,填充通常根据需要我们来填充的区域周边的边界信息来完成。
  图像修复的最常见应用是用来恢复照片中产生的一些小的噪线等,当然图像修复还可以用于删除图像中的小的不需要的对象,这种时候只需要我们把不需要的对象看作一种特殊的噪线即可。
  在本节中,关于图片的修复,我们会简要的讨论在机器仿人视觉中较为常用的两种修复算法,分别是INPAINT_NS和INPAINT_TELEA。

INPAINT_NS

  这里我们依靠图片来进行这种算法的说明,原图如下图左边所示,现在假设我们图片破损了,破损区域如下图右边所示。
在这里插入图片描述
  那么我们现在的问题就是我们该如何填补这个黑色区域的问题。现在我们想要的一条约束黑线,他应该有两个这样的特征:
  (1)我们想要这样一条约束黑线,约束黑线的起点是从下边缘进入黑色区域,然后从上边沿处脱离黑色区域。
  (2)曲线的右边的区域应该为蓝色,而约束黑线的左边区域应该为白色。
通过以上简述的两个约束条件,我们可以得知的是,这种算法要求我们得到的约束黑线有以下两个特点:保留原有的边缘特征,以及一条能够继续在平滑区域中传播颜色信息的约束黑线。
  这个算法的创始人通过建立了一个偏微分方程来更新具有上述约束的区域内的图像强度,算法比较复杂,所以我们就不再在这里进行过多的描述了,这里给出对应的论文的网站

INPAINT_TELEA

  第二种算法与之前那种算法的区别在于:它不使用拉普拉斯算子作为平滑度的估计(前面那种算法需要使用拉普拉斯算子来进行平滑度的估计)。这种算法使用的是:依靠像素的已知图像邻域边上的加权平均值来对已经破损的图像进行补绘。这里的补绘指的是用我们已知的邻域像素和图像梯度来帮忙估计要修复的像素的颜色,通过这种估计可以来进行图像的修复。
  提出这种算法的论文如下所示,感兴趣的读者可以自行前往如下网址

cv2.inpaint函数

  在我们进行图像的修复的时候,我们需要使用到的函数为:cv2.inpaint,这个函数的语法如下所示:

dst = cv2.inpaint(src, inpaintMask, inpaintRadius, flags)
·src:我们要修复的图像。
·inpaintMask:二进制的掩码,这里指的是我们要修复的像素。
·inpaintRadius:表示我们要对图片进行修复的半径。
·flags:我们要选用的修复算法,这里我们使用的主要有上面说过的两种:
(1)cv2.INPAINT_NS2)cv2. INPAINT_TELEA
·dst:最后我们得到的结果图像。

INPAINT_NS修复

  首先我们先来进行cv2.INPAINT_NS的修复,我们的原图如代码下方图中最左边的所示,示例代码如下所示。

import numpy as np
import cv2
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()
        cv2.setMouseCallback(self.windowname, self.on_mouse)
    def show(self):
        cv2.imshow(self.windowname, self.dests[0])
        cv2.imshow(self.windowname + ": mask", self.dests[1])
    # 设置相对应的鼠标事件
    def on_mouse(self, event, x, y, flags, param):
        pt = (x, y)
        if event == cv2.EVENT_LBUTTONDOWN:
            self.prev_pt = pt
        elif event == cv2.EVENT_LBUTTONUP:
            self.prev_pt = None
        if self.prev_pt and flags & cv2.EVENT_FLAG_LBUTTON:
            for dst, color in zip(self.dests, self.colors_func()):
                cv2.line(dst, self.prev_pt, pt, color, 5)
            self.dirty = True
            self.prev_pt = pt
            self.show()
def main():
    # 读取照片
    img = cv2.imread("1.jpg")
    # 如果没有打开图片,直接返回
    if img is None:
        return
    # 创造一个原图的复制出来,方便后面显示
    img_mask = img.copy()
    # 创建一个黑色的掩膜
    inpaintMask = np.zeros(img.shape[:2], np.uint8)
    sketch = Sketcher('image', [img_mask, inpaintMask], lambda : ((255, 255, 255), 255))
    while True:
        ch = cv2.waitKey()
        if ch == ord('q'):
            break
        if ch == ord('n'):
            # 使用图像修复算法
            res = cv2.inpaint(src=img_mask, inpaintMask=inpaintMask, inpaintRadius=3, flags=cv2.INPAINT_NS)
            cv2.imshow('output', res)
        if ch == ord('r'):
            img_mask[:] = img
            inpaintMask[:] = 0
            sketch.show()
    print('Completed')
if __name__ == '__main__':
    main()
cv2.destroyAllWindows()

  运行上述代码,我们先左键拖动我们的鼠标,通过鼠标事件来在我们的图片上进行画图(将我们需要修复的部分用白色部分覆盖)我们鼠标拖动的白色部分其实也就是我们的掩膜,因此会显示在mask的窗口上,(每次进行算法前必须都提前准备好与我们的噪线相对应的掩膜)
  通过鼠标拖动将损坏区域完全覆盖以后,我们可以按下q键来进行画布的退出,也可以按下n键来进行NS算法的画面修补,如果需要再查看原图,也按下r键来查看;下图中间的部分表示的是已经被我们画出的白色掩膜包含的图片,下图右侧的图片表示的是通过掩膜经过NS修补后的图片。在这里插入图片描述
  可以看到的是,经过NS算法修复之后,原本图中上方的蓝色划线字样已经完全消失了。
  注意:如果在运行程序时将噪线完全覆盖后运行NS算法还有残留部分,那么可以增大在原图中残留部分周边的白色部分的面积,通过增大掩膜的方式增大计算,从而做到更好效果的NS修改算法效果。

INPAINT_TELEA修复

  在上面的例子中我们已经对有“划痕”的图片进行了INPAINT_NS算法的修复,下面我们来尝试INPAINT_TELEA的修复算法并将两种算法来进行一些简单的比较。我们还是采用上图来进行修复,示例代码如下所示。

import numpy as np
import cv2
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()
        cv2.setMouseCallback(self.windowname, self.on_mouse)
    def show(self):
        cv2.imshow(self.windowname, self.dests[0])
        cv2.imshow(self.windowname + ": mask", self.dests[1])
    # 设置相对应的鼠标事件
    def on_mouse(self, event, x, y, flags, param):
        pt = (x, y)
        if event == cv2.EVENT_LBUTTONDOWN:
            self.prev_pt = pt
        elif event == cv2.EVENT_LBUTTONUP:
            self.prev_pt = None
        if self.prev_pt and flags & cv2.EVENT_FLAG_LBUTTON:
            for dst, color in zip(self.dests, self.colors_func()):
                cv2.line(dst, self.prev_pt, pt, color, 5)
            self.dirty = True
            self.prev_pt = pt
            self.show()
def main():
    # 读取照片
    img = cv2.imread("1.jpg")
    # 如果没有打开图片,直接返回
    if img is None:
        return
    # 创造一个原图的复制出来,方便后面显示
    img_mask = img.copy()
    # 创建一个黑色的掩膜
    inpaintMask = np.zeros(img.shape[:2], np.uint8)
    sketch = Sketcher('image', [img_mask, inpaintMask], lambda : ((255, 255, 255), 255))
    while True:
        ch = cv2.waitKey()
        if ch == ord('q'):
            break
        if ch == ord('t'):
            res=cv2.inpaint(src=img_mask,inpaintMask=inpaintMask,inpaintRadius=3, flags=cv2.INPAINT_TELEA)
            cv2.imshow('output', res)
        if ch == ord('r'):
            img_mask[:] = img
            inpaintMask[:] = 0
            sketch.show()
    print('Completed')
if __name__ == '__main__':
    main()
cv2.destroyAllWindows()

  与前面一样的,这里我们可以使用q键来进行画布的退出,可以按下t键来进行TELEA算法的画面修补,按下r键可以查看我们的原图,运行上述代码后,与之前NS算法一样的操作之后,我们可以得到如下两张图所示的效果,其分别对应:掩膜图以及TELEA修复图。
在这里插入图片描述
  这里我们可以通过尝试分别在两块代码中加入以下代码显示各自修复的时间来进行比较两种修复算法的修复快慢。

Start=time.time()
#中间过程
......
print(time.time()-Start)

  这个时候我们可以发现的是,就上面两个例子而言NS算法所耗费的时间略比TELEA算法的时间要短一些,当然这也与我们选择的掩膜有关:可以看到的是我们在NS中画出的白色掩膜比起我们在TELEA中的掩膜面积要小一些,这个也对我们代码中NS算法最后得出较快的运行结果有关。
  感兴趣的读者可以自行对这两种算法进行时间上的比较,就理论上而言,TELEA算法在与NS达到相同的效果的前提下,其所消耗的时间应该要比NS算法要短一些,但我们在实际运用的过程中,我们往往会发现的是,NS算法更能节约一些时间,且在相同计算量下做得比TELEA算法更好。
  注意:TELEA算法因为基于的原理是快速匹配算法(Fast Marching Method based),所以我们常常也称其为FMM算法。

总结

  (前一段日子在赶着写书,被催稿(吐)导致的本系列咕咕咕了,之后可能还会咕,但是还是会尽量快的更新,谢谢大家支持啦!)

发布了44 篇原创文章 · 获赞 97 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_19408097/article/details/104223224
今日推荐