一年好景君须记,最是橙黄橘绿时。这篇文章主要讲述[OpenCV实战]34 使用OpenCV进行图像修复相关的知识,希望能为你提供帮助。
本文将描述一类称为图像修复的区域填充算法。想象一下找一张旧的家庭照片。你扫描它,它看起来很棒,除了一些划痕。当然,你可以在photoshop中加载照片并修复划痕。除此之外可以编写10行代码以使用OpenCV中的修复算法来解决问题。
1 什么是图像修复图像修复是计算机视觉中的一类算法,其目标是填充图像或视频内的区域。该区域使用二进制掩模进行标识,填充通常根据需要填充的区域边界信息来完成。图像修复的最常见应用是恢复旧的扫描照片。它还用于删除图像中的小的不需要的对象。
在本节中,我们将简要讨论在OpenCV中实现的两种修复算法。
1.1 INPAINT_NS : Navier-Stokes based Inpainting该方法于2001年发表在题为Navier-Stokes, Fluid Dynamics, and Image and Video Inpainting的论文。论文见:
有时我觉得计算机视觉领域是一个来自其他领域的移民领域,如电子工程,计算机科学,物理和数学。他们将自己的想法带到现场,以非常有趣和独特的方式解决同样的问题。电气工程师可以将图像看作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。论文见:
该方法实现使用不同的技术解决了相同的约束。作者不使用图像拉普拉斯算子作为平滑度的估计,而是使用像素的已知图像邻域上的加权平均值来补绘。已知的邻域像素和梯度用于估计要修复的像素的颜色。
1.3 方法比较与函数实现根据理论和论文,基于Navier-Stokes的修复应该更慢,并且倾向于产生比fast marching method的方法更模糊的结果。在实践中,我们没有发现这种情况。INPAINT_NS在我们的测试中产生了更好的结果,速度也略高于INPAINT_TELEA。
在OpenCV中,使用函数inpaint实现了修复算法。函数接口如下:
C++:
void inpaint( const Mat&
amp;
src, const Mat&
amp;
inpaintMask, Mat&
amp;
dst, double inpaintRange, int flags );
python:
dst = cv2.inpaint(src, inpaintMask, inpaintRadius, flags)
Src:源图像
inpaintMask:二进制掩码,指示要修复的像素。
【[OpenCV实战]34 使用OpenCV进行图像修复】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 代码所有代码见:
两种算法修复效果都还不错,但是都需要事先准备修复模板的掩模mask,也就是inpaintMask 这个参数。例子里面用鼠标在图片上划线,划线的结果就是mask,而真正应用的时候需要事先设计好这个mask。例子程序中在划线确定mask后,不同按键有不同效果。按t选择INPAINT_TELEA处理,按n选择INPAINT_NS处理,按r查看原图,按ESC退出。
具体代码如下:
C++:
#include "pch.h"
#include &
lt;
opencv2/opencv.hpp&
gt;
#include &
lt;
opencv2/photo.hpp&
gt;
#include &
lt;
iostream&
gt;
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 &
amp;
EVENT_FLAG_LBUTTON))
prevPt = Point(-1, -1);
else if (event == EVENT_LBUTTONDOWN)
prevPt = Point(x, y);
else if (event == EVENT_MOUSEMOVE &
amp;
&
amp;
(flags &
amp;
EVENT_FLAG_LBUTTON))Point pt(x, y);
if (prevPt.x &
lt;
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 &
lt;
&
lt;
"Failed to load image: " &
lt;
&
lt;
filename &
lt;
&
lt;
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 = Noneif self.prev_pt and flags &
amp;
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 &
lt;
image_path&
gt;
")
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=https://www.songbingjia.com/android/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()
推荐阅读
- Intellij官方中文语言包,它来了
- 一次实战挖掘软件逻辑漏洞
- vim的简单使用
- 跨越时空的对白——async&await分析
- C语言_Linux基本命令与C语言基础
- NAT实验
- #私藏项目实操分享#愚公系列2022年05月 Python教学课程 75-DRF框架之排序
- 路由基础之DHCP接口全局中继地址池的配置
- Nacos源码系列—服务端那些事儿