Learn Opencv ---- 使用Opencv完成图像修复

阅读原文


    想象一下这样的场景,你从你的老家找到一张相片,并想将这张照片扫描成电子档用于保存。但可惜但是,这张照片由于年代过于久远,出现了一些划痕。怎么办呢?我们虽然可以使用Photoshop来进行处理,但这对于能够写代码但我们来说,一点都不酷。

什么是图像修复

    图像修复是计算机视觉中但一类算法,它但目标是填充图像或视频中的区域。它使用二值掩模来识别区域,填充的像素通过从需要填充的区域的边界或者其传播区域来获取。图像修复最常见的任务是旧照片的修复和删除图像中不需要的小对象。

Opencv图像修复算法

    在这一部分,我们将简要讨论在Opencv中实现的两个图像修复算法。

基于Navier-Stokes的修复算法

    这个方法是2001年提出的,它所对应的论文是Navier-Stokes, Fluid Dynamics, and Image and Video Inpainting。有时候每个学科并不是完全分割开来的,例如,电子工程师们可能会将图像看成一个二维的信号,然后使用信号处理的方法来处理图像;而数学家们,可能会将图像看成连通图,进而用数学的方法来解决图像的问题。
    令人惊讶的是,流体动力学的理论也被应用到计算机视觉中。在下方的图像中,我们的目标是填充左边图像中的黑暗区域,得到一个看起来想右边的图像。
在这里插入图片描述
我们该如何填充这个黑色的区域呢?一个简单的约束是,有这么一条曲线,它的一头链接着A,另一头链接着B。而另一个约束是,链接A和B的曲线的右边区域应该是白色,左边的区域应该是蓝色。可以看到,上述的两个约束本质上是状态:

  1. 保留梯度(如边缘特征)。
  2. 继续在平滑区域传播颜色信息。

为了实现这样的约束,该方法的作者建立了一个偏微分方差(PDE)来更新区域内的图像强度。利用图像拉普拉斯算子估计图像平滑度信息,并沿等照度线(等强度等值线)传播。等照度估计的方法是:通过将图像梯度旋转90度来获得。

    作者表明,这些方程在形式上与二维不可压缩流体的Navier-Stokes方程密切相关。将问题简化为流体动力学问题的好处是,我们可以受益于成熟的理论分析和数值工具。

基于Fast Marching的修复算法

    这个方法是An Image Inpainting Technique Based on the Fast Marching Method这篇论文提出的。
    这个方法使用了与上述方法完全不同的技术方案解决了相同的问题。它不使用图像拉普拉斯算子作为平滑估计,作者使用加权平均在已知的图像领域的像素来进行修复。利用已知的领域像素和梯度来估计等待修复区域的像素。
    在该方法中,一旦需要修复的区域中的像素被修复,那么边界就需要进行更新。作者将图像缺失区域作为水平集,采用快速步进的方法对边界进行更新。

赞成者和反对者

    根据理论和论文,基于Navier-Stokes的修复应该是比较慢的,并且会产生比基于Fast Marching方法更加模糊的修复结果。但实际情况可能会有出入。
    此外,今年来深度学习但迅猛发展,使用深度学习来进行图像修复,取得了比传统方法更好但修复效果。

代码

让我们来看看代码吧。代码比较简单,因为Opencv都已经对修复算法进行来封装,我们只需要调用并做好交互就好啦。

import numpy as np
import cv2 as cv
import sys

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])

    # 鼠标触发函数
    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()):
                # 在图像和mask上绘制白色线条
                cv.line(dst, self.prev_pt, pt, color, 5)
            self.dirty = True
            self.prev_pt = pt
            self.show()


def main():

    print("Usage: python inpaint <image_path>")
    print("Keys: ")
    print("t - inpaint using FMM")
    print("n - inpaint using NS technique")
    print("r - reset the inpainting mask")
    print("ESC - exit")

    # 读取测试图像
    img = cv.imread('sample.jpeg', cv.IMREAD_COLOR)

    # 判读图像是否为None
    if img is None:
        print('Failed to load image file: {}'.format(args["image"]))
        return

    # 使用深度拷贝创建一个愿图像副本
    img_mask = img.copy()
    # 创建一个全黑的mask,用来显示人为绘制的图像缺失区域
    inpaintMask = np.zeros(img.shape[:2], np.uint8)
    # 使用Opencv创建能够绘制缺失区域的草图
    sketch = Sketcher('image', [img_mask, inpaintMask], lambda : ((255, 255, 255), 255))

    while True:
        ch = cv.waitKey()
        # 根据不同的按键触发不同的效果
        if ch == 27:
            break
        if ch == ord('t'):
            # FMM算法
            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'):
            # NS算法
            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
            # 清空mask图
            inpaintMask[:] = 0
            sketch.show()

    print('Completed')


if __name__ == '__main__':
    main()
    cv.destroyAllWindows()

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值