一、图像修复简介
图像修复是图像复原中的一个重要内容,其目的是利用图像现有的信息来恢复丢失的信息。可用于旧照片中丢失信息的恢复,视频文字去除以及视频错误隐藏等。简言之,图像修复就是对图像上信息缺损区域进行信息填充的过程,其目的就是为了对有信息缺损的图像进行复原,并且使得观察者无法察觉到图像曾经缺损或者已经修复
图像修复技术简单来说,就是利用那些被破坏区域的边缘,即是边缘的颜色和结构,繁殖和混合到损坏的图像中,来进行修复图像
目前存在两大类图像修复技术:一类是用于修复小尺度缺损的数字图像修补(inpainting)技术。即,利用待修补区域的边缘信息,同时采用一种由粗到精的方法来估计等照度线的方向,并采用传播机制将信息传播到待修补的区域内,以便达到较好的修补效果;另外一类是用于填充图像大块丢失信息的图像补全技术。目前,这一技术分为以下两种方法:一种是基于图像分解的修复方法,其主要思想是将图像分解为结构部分和纹理部分。其中,结构部分用inpainting的技术来修复,而纹理部分则采用纹理合成的方法来填充。另一种方法是用基于块的纹理合成技术来填充丢失的信息,其主要思想是:首先从待修补区域的边界上选取一个像素点,同时以该点为中心,根据图像的纹理特征,选择大小合适的纹理块,然后在待修补区域的周围寻找与之最相近的纹理匹配块来替代该纹理块。近几年来,利用纹理合成来修复大块丢失信息的图像合成技术得到了相当的研究,也取得了一定的成果。需要提醒的是,图像修复技术是一种对视觉感知过程的学习和理解。它是一个不确定问题,没有唯一解的存在,解的合理性取决于视觉系统的接受程度。换言之,为了达到较好的视觉效果,我们必须让修复效果更加符合视觉感知的特性,使得图像看起来浑然一体,没有修改过的痕迹。
二、原理介绍opencv提供了2中方法,这里主要说的是INPAINT_TELEA
参考文献为Alexandru Telea于2004年发表于Journal of GraphicTools上An ImageInpainting Technique Based On the Fast Marching Method”也称为FMM算法
如何修复一个像素点的?
参考上图,Ω区域是待修复的区域;δΩ指Ω的边界);要修复Ω中的像素,就需要计算出新的像素值来代替原值。
现在假设p点是我们要修复的像素。以p为中心选取一个小邻域B(ε),该邻域中的点像素值都是已知的(只要已知的)。(这个ε就是opencv函数中参数 inpaintRadius)
现在假设p点是我们要修复的像素。以p为中心选取一个小邻域B(ε),该邻域中的点像素值都是已知的(只要已知的)。(这个ε就是opencv函数中参数 inpaintRadius)
q为 Bε(p)中的一点,由q点计算P的灰度值公式如下
显然,我们需要的是用邻域Bε(p)中的所有点计算p点的新灰度值。显然,各个像素点所起的作用应该是不同的,也就引入了权值函数来决定哪些像素的值对新像素值影响更大,哪些比较小。采用下面的公式(公式2):
这里的w(p, q)就是权值函数,是用来限定邻域中各像素的贡献大小的。
w(p, q) = dir(p, q) ·dst(p, q) · lev(p, q)
其中,d0和 T0分别为距离参数和水平集参数,一般都取为 1。方向因子 dir(p,q)保证了越靠近法线方向 N = ?T的像素点对 p点的贡献最大;几何距离因子 dst(p,q)保证了离 p点越近的像素点对p点贡献越大;水平集距离因子lev(p,q)保证了离经过点 p的待修复区域的轮廓线越近的已知像素点对点 p的贡献越大。
三、图像修复应用
void inpaint(InputArray src, InputArray inpaintMask, OutputArray dst, double inpaintRadius, int flags)
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/photo/photo.hpp"
#include
using namespace cv;
using namespace std;
#define WINDOW_NAME1 "【原始图】"
#define WINDOW_NAME2 "【修补后】"
Mat srcImage1, inpaintMask;
// 原来的点坐标
Point previousPoint(-1, -1);
static void On_Mouse(int event, int x, int y, int flags, void *)
{
if(event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
previousPoint = Point(-1, -1);
else if(event == EVENT_LBUTTONDOWN)
previousPoint = Point(x, y);
else if(event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
{
Point pt(x, y);
if(previousPoint.x < 0)
previousPoint = pt;
line(inpaintMask, previousPoint, pt, Scalar::all(255), 5, 8, 0);
line(srcImage1, previousPoint, pt, Scalar::all(255), 5, 8, 0);
previousPoint = pt;
imshow(WINDOW_NAME1, srcImage1);
}
}
int main(int argc, char ** argv)
{
Mat srcImage = imread("lena.jpg", -1);
if(!srcImage.data)
{
printf("读取照片错误,请确定目录下是否有imread函数指定的图片存在!\n");
return false;
}
srcImage1 = srcImage.clone();
inpaintMask = Mat::zeros(srcImage1.size(), CV_8U);
// 显示原图
imshow(WINDOW_NAME1, srcImage1);
// 设置消息回调消息
setMouseCallback(WINDOW_NAME1, On_Mouse, 0);
while(1)
{
char c = (char)waitKey();
// 按键为ESC则退出
if(c == 27)
break;
// 按键为‘2’则恢复原来图像
if(c == '2')
{
inpaintMask = Scalar::all(0);
srcImage.copyTo(srcImage1);
imshow(WINDOW_NAME1, srcImage1);
}
// 按键为1或者空格,则进行修补操作
if(c == '1' || c == ' ')
{
Mat inpaintedImage;
inpaint(srcImage1, inpaintMask, inpaintedImage, 3, INPAINT_TELEA);
imshow(WINDOW_NAME2, inpaintedImage);
imshow("yanmo", inpaintMask);
}
}
return 0;
}
四、通道分离与合并
C++: void split(InputArray m,OutputArrayOfArrays mv);
C++: void merge(InputArrayOfArrays mv,OutputArray dst)
通道的分离合并也是经常用的,因为比较简单所以就穿插在里面了,不单独作为来说,看看就好Mat srcImage;
Mat imageROI;
vector channels;
srcImage= cv::imread("dota.jpg");
// 把一个3通道图像转换成3个单通道图像
split(srcImage,channels);//分离色彩通道
imageROI=channels.at(0);
addWeighted(imageROI(Rect(385,250,logoImage.cols,logoImage.rows)),1.0,
logoImage,0.5,0.,imageROI(Rect(385,250,logoImage.cols,logoImage.rows)));
merge(channels,srcImage4);
namedWindow("sample");
imshow("sample",srcImage);
图像修复,基于偏微分方程的主要有三个:经典的PDE模型有BSCB模型,TV模型和CDD模型,用了BSCB修了一下,感觉一般,也可能是设置不合理吧,读着可以自己找这个三种方法的原理和实现,这里就不多说了,
图像识别算法交流 QQ群:145076161,欢迎图像识别与图像算法,共同学习与交流