在opencv中实现修复有两种算法,这里只介绍Telea的算法,即基于快速行进(FMM)的修复算法。首先看c++接口中,函数的定义。

void cv::inpaint( const Mat& src, const Mat& mask, Mat& dst,  double inpaintRange, int flags )
//src:要修复的图像;
//mask:修复模板,必须是单通道图像;
//dst:目标图像;
//inpaintRange:选取邻域半径;
//flags:要使用的方法,可以是CV INPAINT NS或CV INPAINT TELEA(本文介绍的方法)。
其实c++接口实现的inpaint方法,只是调用了一下c接口中的cvinpaint。
1,cvInpaint( const vArr*_input_img,const CvArr* _inpaint_mask,CvArr* _output_img, double inpaintRange, int flags )
首先,FMM算法基于的思想是,先处理待修复区域边缘上的像素点,然后层层向内推进,直到修复完所有的像素点。
下面以灰度图为例,我们只需要计算出像素新的灰度值即可。对于彩色图像,分别用同样的方法处理各个通道即可。
一、先说一下如何修复一个像素点的。
参考上图,Ω区域是待修复的区域;δΩ指Ω的边界);要修复Ω中的像素,就需要计算出新的像素值来代替原值。
现在假设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 的贡献越大。
把红色像素区域作为掩膜,类似可以定义想操作的区域和像素:
#include "opencv2/core/core.hpp"
 #include "opencv2/highgui/highgui.hpp"
 #include "opencv2/imgproc/imgproc.hpp"
 #include "opencv2/photo/photo.hpp"
  
 #include <iostream>
 using namespace std;
 using namespace cv;
  
 //该方法可能产生误检点,但在可容忍的错范围内
 Mat GetRedComponet(Mat srcImg)
 {
     //如果直接对srcImg处理会改变main()函数中的实参
     Mat dstImg = srcImg.clone();
     Mat_<Vec3b>::iterator it = dstImg.begin<Vec3b>();
     Mat_<Vec3b>::iterator itend = dstImg.end<Vec3b>();
     for(; it != itend; it++)
     {
         if((*it)[2] > 190)//对红色分量做阈值处理
         {
             (*it)[0] = 0;
             (*it)[1] = 0;
             //(*it)[2] = 255;//红色分量保持不变
         }
  
         else
         {
             (*it)[0] = 0;
             (*it)[1] = 0;
             (*it)[2] = 0;
         }
     }
     return dstImg;
 }
  
 void Inpainting(Mat oriImg, Mat maskImg)
 {
     Mat grayMaskImg;
     Mat element = getStructuringElement(MORPH_RECT, Size(7, 7));
     dilate(maskImg, maskImg, element);//膨胀后结果作为修复掩膜
     //将彩色图转换为单通道灰度图,最后一个参数为通道数
     cvtColor(maskImg, grayMaskImg, CV_BGR2GRAY, 1); 
     //修复图像的掩膜必须为8位单通道图像
     Mat inpaintedImage;
     inpaint(oriImg, grayMaskImg, inpaintedImage, 3, INPAINT_TELEA);
     imshow("原图", oriImg);
     imshow("图像复原结果图", inpaintedImage);
     waitKey(0);
 }
  
 int main(int argc, char* argv[])
 {
     Mat srcImg;
     srcImg = imread("D:/openCV/data/naturalImage/data/opencv.jpg", 1);
     Mat imgComponet = GetRedComponet(srcImg);
     Inpainting(srcImg, imgComponet);
  
     return 0;
 }鼠标确定掩膜的代码:
 //---------------------------------【头文件、命名空间包含部分】----------------------------  
 //      描述:包含程序所使用的头文件和命名空间  
 //------------------------------------------------------------------------------------------------  
 #include "opencv2/highgui/highgui.hpp"  
 #include "opencv2/imgproc/imgproc.hpp"  
 #include "opencv2/photo/photo.hpp"  
 #include <iostream>  
 using namespace cv;  
 using namespace std;  
   
   
 //-----------------------------------【宏定义部分】--------------------------------------------   
 //  描述:定义一些辅助宏   
 //----------------------------------------------------------------------------------------------  
 #define WINDOW_NAME0 "【原始图参考】"        //为窗口标题定义的宏   
 #define WINDOW_NAME1 "【原始图】"        //为窗口标题定义的宏   
 #define WINDOW_NAME2 "【修补后的效果图】"        //为窗口标题定义的宏   
   
   
 //-----------------------------------【全局变量声明部分】--------------------------------------  
 //          描述:全局变量声明  
 //-----------------------------------------------------------------------------------------------  
 Mat srcImage0,srcImage1, inpaintMask;  
 Point previousPoint(-1,-1);//原来的点坐标  
   
   
 //-----------------------------------【ShowHelpText( )函数】----------------------------------  
 //          描述:输出一些帮助信息  
 //----------------------------------------------------------------------------------------------  
 static void ShowHelpText( )  
 {  
   
     //输出一些帮助信息  
     printf("\n\n\n\t欢迎来到【图像修复】示例程序~\n");   
     printf(  "\n\t请在进行图像修复操作之前,在【原始图】窗口中进行适量的绘制"   
         "\n\n\t按键操作说明: \n\n"   
         "\t\t【鼠标左键】-在图像上绘制白色线条\n\n"  
         "\t\t键盘按键【ESC】- 退出程序\n\n"   
         "\t\t键盘按键【1】或【SPACE】-进行图像修复操作 \n\n"   );    
 }  
   
   
 //-----------------------------------【On_Mouse( )函数】--------------------------------  
 //          描述:响应鼠标消息的回调函数  
 //----------------------------------------------------------------------------------------------  
 static void On_Mouse( int event, int x, int y, int flags, void* )  
 {  
     //鼠标左键弹起消息  
     if( event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON) )  
         previousPoint = Point(-1,-1);  
     //鼠标左键按下消息  
     else if( event == CV_EVENT_LBUTTONDOWN )  
         previousPoint = Point(x,y);  
     //鼠标按下并移动,进行绘制  
     else if( event == CV_EVENT_MOUSEMOVE && (flags & CV_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);  
     }  
 }  
   
   
 //--------------------------------------【main( )函数】-----------------------------------------  
 //          描述:控制台应用程序的入口函数,我们的程序从这里开始执行  
 //-----------------------------------------------------------------------------------------------  
 int main( int argc, char** argv )  
 {  
     //改变console字体颜色  
     system("color 2F");   
   
     //显示帮助文字  
     ShowHelpText();  
   
     //载入原始图并进行掩膜的初始化  
     Mat srcImage = imread("1.jpg", -1);  
     if(!srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; }   
     srcImage0 = srcImage.clone();  
     srcImage1 = srcImage.clone();  
     inpaintMask = Mat::zeros(srcImage1.size(), CV_8U);  
   
     //显示原始图参考  
     imshow(WINDOW_NAME0, srcImage0);  
     //显示原始图  
     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, CV_INPAINT_TELEA);  
             imshow(WINDOW_NAME2, inpaintedImage);  
         }  
     }  
   
     return 0;  
 }