目录

1 什么是图像修复

1.1 INPAINT_NS : Navier-Stokes based Inpainting

1.2 INPAINT_TELEA : Fast Marching Method based

1.3 方法比较与函数实现

2 结果与代码

2.1 结果

2.2 代码

3 参考


本文将描述一类称为图像修复的区域填充算法。想象一下找一张旧的家庭照片。你扫描它,它看起来很棒,除了一些划痕。当然,你可以在photoshop中加载照片并修复划痕。除此之外可以编写10行代码以使用OpenCV中的修复算法来解决问题。

1 什么是图像修复

图像修复是计算机视觉中的一类算法,其目标是填充图像或视频内的区域。该区域使用二进制掩模进行标识,填充通常根据需要填充的区域边界信息来完成。图像修复的最常见应用是恢复旧的扫描照片。它还用于删除图像中的小的不需要的对象。

在本节中,我们将简要讨论在OpenCV中实现的两种修复算法。

1.1 INPAINT_NS : Navier-Stokes based Inpainting

该方法于2001年发表在题为Navier-Stokes, Fluid Dynamics, and Image and Video Inpainting的论文。论文见:

http://www.math.ucla.edu/~bertozzi/papers/cvpr01.pdf

有时我觉得计算机视觉领域是一个来自其他领域的移民领域,如电子工程,计算机科学,物理和数学。他们将自己的想法带到现场,以非常有趣和独特的方式解决同样的问题。电气工程师可以将图像看作2D信号,并应用信号处理理论来解决计算机视觉问题。另一方面,数学家可以将图像看作连通图并使用图论解决计算机视觉问题。因此,为流体动力学开发的理论也可以用于计算机视觉,这并不奇怪。在下图中,我们的目标是填充暗区并获得一个看起来像右边的图像。

我们如何填补这个黑色区域?我们想要的一个约束是边缘进入点A应该连接边缘离开点B。我们可能想要的另一个约束是连接A和B的曲线右边的区域应该是白色,而左边的区域应该是蓝色的。

以上两个约束基本上要求:保留渐变(即边缘特征)和继续在平滑区域中传播颜色信息。

作者建立了一个偏微分方程(PDE)来更新具有上述约束的区域内的图像强度。

1.2 INPAINT_TELEA : Fast Marching Method based

该方法基于论文An Image Inpainting Technique Based on the Fast Marching Method。论文作者Alexandru Telea。论文见:

https://pdfs.semanticscholar.org/622d/5f432e515da69f8f220fb92b17c8426d0427.pdf

该方法实现使用不同的技术解决了相同的约束。作者不使用图像拉普拉斯算子作为平滑度的估计,而是使用像素的已知图像邻域上的加权平均值来补绘。已知的邻域像素和梯度用于估计要修复的像素的颜色。

1.3 方法比较与函数实现

根据理论和论文,基于Navier-Stokes的修复应该更慢,并且倾向于产生比fast marching method的方法更模糊的结果。在实践中,我们没有发现这种情况。INPAINT_NS在我们的测试中产生了更好的结果,速度也略高于INPAINT_TELEA。

在OpenCV中,使用函数inpaint实现了修复算法。函数接口如下:

C++:

void inpaint( const Mat& src, const Mat& inpaintMask, Mat& dst, double inpaintRange, int flags );

Python:

dst = cv2.inpaint(src, inpaintMask, inpaintRadius, flags)

Src:源图像

inpaintMask:二进制掩码,指示要修复的像素。

Dst:结果图像

inpaintRadius:表示修复的半径

flags : 修复算法,主要有INPAINT_NS (Navier-Stokes based method) or INPAINT_TELEA (Fast marching based method)

2 结果与代码

2.1 结果

让我们来看看对林肯总统的历史形象进行修复的结果。这张照片背后有一段引人入胜的历史,我从维基百科借来的:

1865年2月5日星期日,在华盛顿特区的加德纳画廊,亚历山大·加德纳拍摄了几张总统的多镜头照片。在本届会议结束之前,加德纳要求总统最后一个姿势。他把相机拉得更近,拍了一张林肯头部,肩膀和胸部的照片。神秘的玻璃板破裂。加德纳小心翼翼地将它带到了他的黑暗房间,并且能够制作一张印刷品,但在林肯的脸上有一个不祥的裂缝。在这个印刷品完全破碎并被弃用。但这种印刷品,即O-118,至今仍然存在。多年来,许多人将这一裂缝与10周后等待林肯的刺客子弹的象征性预言联系在一起。

修复结果:上图左边的第一个图像是输入图像,第二个图像是掩模,第三个图像是INPAINT_TELEA的结果,第四个图像是INPAINT_NS的结果。

让我们来看一个更复杂的例子。我们已经在一个花园的图像上草草写了很多,但是结果仍然非常引人注目。结果如下:

上图中,左:带有潦草文字的原始图像。中:使用INPAINT_TELEA方法修复,右:使用INPAINT_NS。

2.2 代码

所有代码见:

https://github.com/luohenyueji/OpenCV-Practical-Exercise

两种算法修复效果都还不错,但是都需要事先准备修复模板的掩模mask,也就是inpaintMask 这个参数。例子里面用鼠标在图片上划线,划线的结果就是mask,而真正应用的时候需要事先设计好这个mask。例子程序中在划线确定mask后,不同按键有不同效果。按t选择INPAINT_TELEA处理,按n选择INPAINT_NS处理,按r查看原图,按ESC退出。

具体代码如下:

C++:

#include "pch.h"
#include <opencv2/opencv.hpp>
#include <opencv2/photo.hpp> #include <iostream> using namespace cv;
using namespace std; // Declare Mat objects for original image and mask for inpainting
Mat img, inpaintMask;
// Mat object for result output
Mat res;
Point prevPt(-1, -1); // onMouse function for Mouse Handling
// Used to draw regions required to inpaint
// 调用鼠标事件
static void onMouse(int event, int x, int y, int flags, void*)
{
if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
prevPt = Point(-1, -1);
else if (event == EVENT_LBUTTONDOWN)
prevPt = Point(x, y);
else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
{
Point pt(x, y);
if (prevPt.x < 0)
prevPt = pt;
line(inpaintMask, prevPt, pt, Scalar::all(255), 5, 8, 0);
line(img, prevPt, pt, Scalar::all(255), 5, 8, 0);
prevPt = pt;
imshow("image", img);
imshow("image: mask", inpaintMask);
}
} int main()
{
string filename = "./image/flower-garden.jpg";
// Read image in color mode 读图
img = imread(filename);
Mat img_mask;
// Return error if image not read properly
if (img.empty())
{
cout << "Failed to load image: " << filename << endl;
return 0;
} namedWindow("image"); // Create a copy for the original image 复制原图像
img_mask = img.clone();
// Initialize mask (black image)
inpaintMask = Mat::zeros(img_mask.size(), CV_8U); // Show the original image
imshow("image", img);
//调用鼠标在图像上画圈
setMouseCallback("image", onMouse, NULL); for (;;)
{
char c = (char)waitKey();
//按t选择INPAINT_TELEA处理
if (c == 't')
{
// Use Algorithm proposed by Alexendra Telea
inpaint(img, inpaintMask, res, 3, INPAINT_TELEA);
imshow("Inpaint Output using FMM", res);
}
//按n选择INPAINT_NS处理
if (c == 'n')
{
// Use Algorithm proposed by Bertalmio et. al.
inpaint(img, inpaintMask, res, 3, INPAINT_NS);
imshow("Inpaint Output using NS Technique", res);
}
//按r查看原图
if (c == 'r')
{
inpaintMask = Scalar::all(0);
img_mask.copyTo(img);
imshow("image", inpaintMask);
}
//按ESC退出
if (c == 27)
{
break;
}
}
return 0;
}

Python:

import numpy as np
import cv2 as cv
# OpenCV Utility Class for Mouse Handling
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 <image_path>")
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("./image/Lincoln.jpg") # If image is not read properly, return error
if img is None:
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)
# 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)
# Reference: https://pdfs.semanticscholar.org/622d/5f432e515da69f8f220fb92b17c8426d0427.pdf
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()

3 参考

https://www.learnopencv.com/image-inpainting-with-opencv-c-python/

[OpenCV实战]34 使用OpenCV进行图像修复的更多相关文章

  1. OpenCV探索之路(十):图像修复技术

    在实际应用中,我们的图像常常会被噪声腐蚀,这些噪声或是镜头上的灰尘或水滴,或是旧照片的划痕,或者是图像遭到人为的涂画(比如马赛克)或者图像的部分本身已经损坏.如果我们想让这些受到破坏的额图片尽可能恢复 ...

  2. [OpenCV实战]44 使用OpenCV进行图像超分放大

    图像超分辨率(Image Super Resolution)是指从低分辨率图像或图像序列得到高分辨率图像.图像超分辨率是计算机视觉领域中一个非常重要的研究问题,广泛应用于医学图像分析.生物识别.视频监 ...

  3. [OpenCV实战]45 基于OpenCV实现图像哈希算法

    目前有许多算法来衡量两幅图像的相似性,本文主要介绍在工程领域最常用的图像相似性算法评价算法:图像哈希算法(img hash).图像哈希算法通过获取图像的哈希值并比较两幅图像的哈希值的汉明距离来衡量两幅 ...

  4. [OpenCV实战]20 使用OpenCV实现基于增强相关系数最大化的图像对齐

    目录 1 背景 1.1 彩色摄影的一个简短而不完整的历史 1.2 OpenCV中的运动模型 2 使用增强相关系数最大化(ECC)的图像对齐 2.1 findTransformECC在OpenCV中的示 ...

  5. [OpenCV实战]46 在OpenCV下应用图像强度变换实现图像对比度均衡

    本文主要介绍基于图像强度变换算法来实现图像对比度均衡.通过图像对比度均衡能够抑制图像中的无效信息,使图像转换为更符合计算机或人处理分析的形式,以提高图像的视觉价值和使用价值.本文主要通过OpenCV ...

  6. [OpenCV实战]50 用OpenCV制作低成本立体相机

    本文主要讲述利用OpenCV制作低成本立体相机以及如何使用OpenCV创建3D视频,准确来说是模仿双目立体相机,我们通常说立体相机一般是指双目立体相机,就是带两个摄像头的那种(目就是指眼睛,双目就是两 ...

  7. [OpenCV实战]48 基于OpenCV实现图像质量评价

    本文主要介绍基于OpenCV contrib中的quality模块实现图像质量评价.图像质量评估Image Quality Analysis简称IQA,主要通过数学度量方法来评价图像质量的好坏. 本文 ...

  8. [OpenCV实战]47 基于OpenCV实现视觉显著性检测

    人类具有一种视觉注意机制,即当面对一个场景时,会选择性地忽略不感兴趣的区域,聚焦于感兴趣的区域.这些感兴趣的区域称为显著性区域.视觉显著性检测(Visual Saliency Detection,VS ...

  9. [OpenCV实战]19 使用OpenCV实现基于特征的图像对齐

    目录 1 背景 1.1 什么是图像对齐或图像对准? 1.2 图像对齐的应用 1.3 图像对齐基础理论 1.4 如何找到对应点 2 OpenCV的图像对齐 2.1 基于特征的图像对齐的步骤 2.2 代码 ...

随机推荐

  1. [Mysql] 两段提交

    事务提交 Mysql 默认开启自动提交事务 两段提交 把一个事务分成两个阶段来提交,就是把redolog拆分成了prepare和commit两段 MySQL想要准备事务的时候会先写redolog.bi ...

  2. 后端框架的学习----mybatis框架(9、多对一处理和一对多处理)

    9.多对一处理和一对多处理 #多对一 <!--按照结果集嵌套查询--> <select id="getAllStudent1" resultMap="S ...

  3. C++ Undefined Behavior 详细列表

    Undefined Behavior,即未定义的行为,指程序不可预测的执行效果,一般由错误的代码实现引起.出于效率.兼容性等多方面原因,语言标准不便于定义错误程序的明确行为,而是将其统称为" ...

  4. DevOps|从特拉斯辞职风波到研发效能中的不靠谱人干的荒唐事

    今天发生了一件大事特拉斯辞任英国首相,我想借着这件事情说下我看到的一件研发效能的荒唐事,这其中的关联也许就是「都用了不靠谱的人」. 两件事情 今儿一早就听到,2022年10月20日英国第78任首相伊丽 ...

  5. 靶机: easy_cloudantivirus

    靶机: easy_cloudantivirus 准备 下载靶机(Target):https://www.vulnhub.com/entry/boredhackerblog-cloud-av,453/ ...

  6. Python 嵌入式打包 (图文)

    Python嵌入式打包过程 目录 Python嵌入式打包过程 下载嵌入式包 解压和配置 安装pip和其他依赖 启动项目 python嵌入式打包:将python环境与项目代码打包到同一个文件夹中,在其他 ...

  7. Scrapy 如何传递 get请求的params

    我们都知道 在requests中可以使用 requests.get(url,params)的方式传值 那么在scrapy中如何传值呢 直接看代码 from urllib.parse import ur ...

  8. 又拍云之 Keepalived 高可用部署

    在聊 Keepalived 之前,我们需要先简单了解一下 VRRP.VRRP(Virtual Router Redundancy Protocol)即虚拟路由冗余协议,是专门为了解决静态路由的高可用而 ...

  9. java学习之SpringMVC拦截器开发

    0x00前言 springmvc的拦截器类似于Selvet的Filter,但是所属的操作又不一样 Spring MVC 提供了 Interceptor 拦截器机制,用于请求的预处理和后处理,也就是增强 ...

  10. Java安全之动态加载字节码

    Java字节码 简单说,Java字节码就是.class后缀的文件,里面存放Java虚拟机执行的指令. 由于Java是一门跨平台的编译型语言,所以可以适用于不同平台,不同CPU的计算机,开发者只需要将自 ...