无意中看到inpaint()这个函数,它具有图像修复功能,这个应该很有趣,故查找了相关资料,整理好并呈现在这里。
void inpaint( const Mat& src, const Mat& inpaintMask,
Mat& dst, double inpaintRange, int flags );
第一个参数InputArray类型的src,为8位单通道或者三通道图像的输入图像(要修复的图像);
第二个参数inpaintMask为修复掩膜,为8位单通道图像,其中非零像素表示要修补的区域;
第三个参数为dst,该函数的输出结果就放在这里,它和src图像类型是一样的;
第四个参数是double类型的inpaintRadius,需要修复点的附近的圆形区域,该值为修复区域的半径;
最后一个参数是int型的flags,为修补方法的标识符,两种修饰方法见下表:
标识符 | 说明 |
INPAINT_NS | 基于Navier-Stokes方法 |
INPAINT_TEELEA | A了先倒入Telea方法 |
CV_INPAINT_NS - 基于Navier-Stokes的方法。
比较难懂,贴个链接:
http://wenku.baidu.com/link?url=aut0V0I8ngmDiQOl6uvjZqAukhfAYeiF38C2-EMS8NScb5VKB3ZMBTpicbO7hU_22yP7zcH2Lz9PDVQrzOYWIthrLWBqYNRAvHCuSZqD6qa
CV_INPAINT_TELEA - Alexandru Telea[Telea04]的方法。
Telea在2004年提出的基于快速行进的修复算法(后面简称FMM算法)。
论文题目:An Image Inpainting Technique Based on the Fast Marching Method (2004)
作者主页:http://www.cs.rug.nl/~alext/
论文下载: http://www.cs.rug.nl/~alext/PAPERS/index.html (编号36的那篇)
一、先说一下如何修复一个像素点的。
参考上图,Ω区域是待修复的区域;δΩ指Ω的边界);要修复Ω中的像素,就需要计算出新的像素值来代替原值。
现在假设p点是我们要修复的像素。以p为中心选取一个小邻域B(ε),该邻域中的点像素值都是已知的(只要已知的)。(这个ε就是opencv函数中参数 inpaintRadius)
显然,我们需要的是用邻域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 的贡献越大。
到底哪种方法更好呢?既然两种方法都收集到opencv库函数里,我认为它们难分仲伯。
#include
#include
using namespace cv;
using namespace std;
#define WINDOW_NAME1 "原始图"
#define WINDOW_NAME2 "修补方法:_TELEA"
#define WINDOW_NAME3 "修补方法:_NS"
Mat srcImage, srcImage1, inpaintMask;
Point previousPoint;//原来的点坐标
static void On_Mouse(int event, int x, int y, int flags, void*)
{
if (event == EVENT_LBUTTONDOWN)
previousPoint = Point(x, y);
else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
{
Point pt(x, y);
line(inpaintMask, previousPoint, pt, Scalar::all(255), 5, 8, 0);//单通道掩膜,需要修复的像素此时为白色
line(srcImage, previousPoint, pt, Scalar::all(255), 5, 8, 0);
previousPoint = pt;
imshow(WINDOW_NAME1, srcImage);
}
}
int main()
{
srcImage = imread("D:\\vvoo\\baboon.jpg", 1);//三通道彩色图像
if (!srcImage.data)
{
cout << "读取图片错误,请确定目录下是否有imread函数指定图片存在~!" << endl;
return false;
}
inpaintMask = Mat::zeros(srcImage.size(), CV_8UC1);
imshow(WINDOW_NAME1, srcImage);
//设置鼠标回调消息
setMouseCallback(WINDOW_NAME1, On_Mouse, 0);
while (1)
{
int key = waitKey(10);
switch (key)
{
case 'a':
{
Mat inpaintedImage;
inpaint(srcImage, inpaintMask, inpaintedImage, 3, INPAINT_TELEA);
imshow(WINDOW_NAME2, inpaintedImage);
}
break;
case 'b':
{
Mat inpaintedImage;
inpaint(srcImage, inpaintMask, inpaintedImage, 3, INPAINT_NS);
imshow(WINDOW_NAME3, inpaintedImage);
}
break;
}
if (key == 27)
break;
}
waitKey(0);
return 0;
}
1.http://blog.csdn.net/sunboyiris/article/details/23554351?utm_source=tuicool&utm_medium=referral
2.Opencv编程入门 作者:浅墨