美文网首页
【06】OpenCV图像修复inpaint函数

【06】OpenCV图像修复inpaint函数

作者: 豚大叔的小屋 | 来源:发表于2023-02-28 19:22 被阅读0次

1.inpaint函数讲解
在OpenCV的“photo.hpp”中定义了一个inpaint函数,可以用来实现图像的修复和复原功能.
该算法使用区域邻域恢复图像中的选定区域。该功能可用于去除扫描照片上的灰尘和划痕,或去除静止图像或视频中不需要的物体。
inpaint函数的原型如下:
void inpaint
(
InputArray src,
InputArray inpaintMask,
OutputArray dst,
double inpaintRadius,
int flags
);

第一个参数src,输入的单通道或三通道图像;
第二个参数inpaintMask,图像的掩码,单通道图像,大小跟原图像一致,inpaintMask图像上除了需要修复的部分之外其他部分的像素值全部为0;inpaintMask表示修复模板
第三个参数dst,输出的经过修复的图像;
第四个参数inpaintRadius,修复算法邻域半径,用于计算当前像素点差值;
第五个参数flags,修复算法,有两种:INPAINT_NS 和I NPAINT_TELEA;

图片.png

函数实现关键是图像掩码的确定,可以通过阈值筛选或者手工选定,按照这个思路,用三种方法生成掩码,对比图像修复的效果。

图片.png

2.全区域阈值处理+Mask膨胀处理
由于是图像全区域做阈值处理获得的掩码,图像上部分区域也被当做掩码对待,导致部分图像受损。

include <imgproc\imgproc.hpp>

include <highgui\highgui.hpp>

include <photo\photo.hpp>

using namespace cv;
//全区域阈值处理+Mask膨胀处理
int main()
{
Mat imageSource = imread("Test.jpg");
if (!imageSource.data)
{
return -1;
}
imshow("原图", imageSource);
Mat imageGray;
//转换为灰度图
cvtColor(imageSource, imageGray, CV_RGB2GRAY, 0);
Mat imageMask = Mat(imageSource.size(), CV_8UC1, Scalar::all(0));
//通过阈值处理生成Mask
threshold(imageGray, imageMask, 240, 255, CV_THRESH_BINARY);
Mat Kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
//对Mask膨胀处理,增加Mask面积
dilate(imageMask, imageMask, Kernel);
//图像修复
inpaint(imageSource, imageMask, imageSource, 5, INPAINT_TELEA);
imshow("Mask", imageMask);
imshow("修复后", imageSource);
waitKey();
}
3.方法二、鼠标框选区域+阈值处理+Mask膨胀处理
选定区域之外的图像不受修复影响,没有额外的损伤

include <imgproc/imgproc.hpp>

include <highgui/highgui.hpp>

include <core/core.hpp>

include <photo/photo.hpp>

using namespace cv;
Point ptL, ptR; //鼠标画出矩形框的起点和终点
Mat imageSource, imageSourceCopy;
Mat ROI; //原图需要修复区域的ROI
//鼠标回调函数
void OnMouse(int event, int x, int y, int flag, void *ustg);
//鼠标圈定区域阈值处理+Mask膨胀处理
int main()
{
imageSource = imread("Test.jpg");
if (!imageSource.data)
{
return -1;
}
imshow("原图", imageSource);
setMouseCallback("原图", OnMouse);
waitKey();
}
void OnMouse(int event, int x, int y, int flag, void *ustg)
{
if (event == CV_EVENT_LBUTTONDOWN)
{
ptL = Point(x, y);
ptR = Point(x, y);
}
if (flag == CV_EVENT_FLAG_LBUTTON)
{
ptR = Point(x, y);
imageSourceCopy = imageSource.clone();
rectangle(imageSourceCopy, ptL, ptR, Scalar(255, 0, 0));
imshow("原图", imageSourceCopy);
}
if (event == CV_EVENT_LBUTTONUP)
{
if (ptL != ptR)
{
ROI = imageSource(Rect(ptL, ptR));
imshow("ROI", ROI);
waitKey();
}
}
//单击鼠标右键开始图像修复
if (event == CV_EVENT_RBUTTONDOWN)
{
imageSourceCopy = ROI.clone();
Mat imageGray;
cvtColor(ROI, imageGray, CV_RGB2GRAY); //转换为灰度图
Mat imageMask = Mat(ROI.size(), CV_8UC1, Scalar::all(0));
//通过阈值处理生成Mask
threshold(imageGray, imageMask, 235, 255, CV_THRESH_BINARY);
Mat Kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imageMask, imageMask, Kernel); //对Mask膨胀处理
inpaint(ROI, imageMask, ROI, 9, INPAINT_TELEA); //图像修复
imshow("Mask", imageMask);
imshow("修复后", imageSource);
}
}
4.方法三、鼠标划定整个区域作为修复对象
这个方法选定一个矩形区域,把整个矩形区域作为要修复的对象,该方法适用于图像结构比较简单,特别是纯色图像,并且选定区域面积占比不大的情况,效果较好。

include <imgproc/imgproc.hpp>

include <highgui/highgui.hpp>

include <core/core.hpp>

include <photo/photo.hpp>

using namespace cv;
Point ptL, ptR; //鼠标画出矩形框的起点和终点
Mat imageSource, imageSourceCopy;
Mat ROI; //原图需要修复区域的ROI
//鼠标回调函数
void OnMouse(int event, int x, int y, int flag, void ustg);
//鼠标圈定区域
int main()
{
imageSource = imread("Test.jpg");
if (!imageSource.data)
{
return -1;
}
imshow("原图", imageSource);
setMouseCallback("原图", OnMouse);
waitKey();
}
void OnMouse(int event, int x, int y, int flag, void ustg)
{
if (event == CV_EVENT_LBUTTONDOWN)
{
ptL = Point(x, y);
ptR = Point(x, y);
}
if (flag == CV_EVENT_FLAG_LBUTTON)
{
ptR = Point(x, y);
imageSourceCopy = imageSource.clone();
rectangle(imageSourceCopy, ptL, ptR, Scalar(255, 0, 0));
imshow("原图", imageSourceCopy);
}
if (event == CV_EVENT_LBUTTONUP)
{
if (ptL != ptR)
{
ROI = imageSource(Rect(ptL, ptR));
imshow("ROI", ROI);
waitKey();
}
}
//单击鼠标右键开始图像修复
if (event == CV_EVENT_RBUTTONDOWN)
{
imageSourceCopy = Mat(imageSource.size(), CV_8UC1, Scalar::all(0));
Mat imageMask = imageSourceCopy(Rect(ptL, ptR));
//生成一个跟ROI大小一样的值全为1的区域
Mat imageMaskCopy = Mat(imageMask.size(), CV_8UC1, Scalar::all(1));
imageMaskCopy.copyTo(imageMask);
inpaint(imageSource, imageSourceCopy, imageSource, 9, INPAINT_TELEA); //图像修复
imshow("Mask", imageSourceCopy);
imshow("修复后", imageSource);
}
}
5.源码
源码路径:opencv\modules\photo\src\inpaint.cpp
源码:
static void
icvInpaint( const CvArr
_input_img, const CvArr
_inpaint_mask, CvArr* _output_img,
double inpaintRange, int flags )
{
cv::Ptr<CvMat> mask, band, f, t, out;
cv::Ptr<CvPriorityQueueFloat> Heap, Out;
cv::Ptr<IplConvKernel> el_cross, el_range;

CvMat input_hdr, mask_hdr, output_hdr;
CvMat* input_img, *inpaint_mask, *output_img;
int range=cvRound(inpaintRange);
int erows, ecols;

input_img = cvGetMat( _input_img, &input_hdr );
inpaint_mask = cvGetMat( _inpaint_mask, &mask_hdr );
output_img = cvGetMat( _output_img, &output_hdr );

if( !CV_ARE_SIZES_EQ(input_img,output_img) || !CV_ARE_SIZES_EQ(input_img,inpaint_mask))
    CV_Error( CV_StsUnmatchedSizes, "All the input and output images must have the same size" );

if( (CV_MAT_TYPE(input_img->type) != CV_8U &&
     CV_MAT_TYPE(input_img->type) != CV_16U &&
     CV_MAT_TYPE(input_img->type) != CV_32F &&
    CV_MAT_TYPE(input_img->type) != CV_8UC3) ||
    !CV_ARE_TYPES_EQ(input_img,output_img) )
    CV_Error( CV_StsUnsupportedFormat,
    "8-bit, 16-bit unsigned or 32-bit float 1-channel and 8-bit 3-channel input/output images are supported" );

if( CV_MAT_TYPE(inpaint_mask->type) != CV_8UC1 )
    CV_Error( CV_StsUnsupportedFormat, "The mask must be 8-bit 1-channel image" );

range = MAX(range,1);
range = MIN(range,100);

ecols = input_img->cols + 2;
erows = input_img->rows + 2;

f.reset(cvCreateMat(erows, ecols, CV_8UC1));
t.reset(cvCreateMat(erows, ecols, CV_32FC1));
band.reset(cvCreateMat(erows, ecols, CV_8UC1));
mask.reset(cvCreateMat(erows, ecols, CV_8UC1));
el_cross.reset(cvCreateStructuringElementEx(3,3,1,1,CV_SHAPE_CROSS,NULL));

cvCopy( input_img, output_img );
cvSet(mask,cvScalar(KNOWN,0,0,0));
COPY_MASK_BORDER1_C1(inpaint_mask,mask,uchar);
SET_BORDER1_C1(mask,uchar,0);
cvSet(f,cvScalar(KNOWN,0,0,0));
cvSet(t,cvScalar(1.0e6f,0,0,0));
cvDilate(mask,band,el_cross,1);   // image with narrow band
Heap=cv::makePtr<CvPriorityQueueFloat>();
if (!Heap->Init(band))
    return;
cvSub(band,mask,band,NULL);
SET_BORDER1_C1(band,uchar,0);
if (!Heap->Add(band))
    return;
cvSet(f,cvScalar(BAND,0,0,0),band);
cvSet(f,cvScalar(INSIDE,0,0,0),mask);
cvSet(t,cvScalar(0,0,0,0),band);

if( flags == cv::INPAINT_TELEA )
{
    out.reset(cvCreateMat(erows, ecols, CV_8UC1));
    el_range.reset(cvCreateStructuringElementEx(2*range+1,2*range+1,
        range,range,CV_SHAPE_RECT,NULL));
    cvDilate(mask,out,el_range,1);
    cvSub(out,mask,out,NULL);
    Out=cv::makePtr<CvPriorityQueueFloat>();
    if (!Out->Init(out))
        return;
    if (!Out->Add(band))
        return;
    cvSub(out,band,out,NULL);
    SET_BORDER1_C1(out,uchar,0);
    icvCalcFMM(out,t,Out,true);
    switch(CV_MAT_DEPTH(output_img->type))
    {
        case CV_8U:
            icvTeleaInpaintFMM<uchar>(mask,t,output_img,range,Heap);
            break;
        case CV_16U:
            icvTeleaInpaintFMM<ushort>(mask,t,output_img,range,Heap);
            break;
        case CV_32F:
            icvTeleaInpaintFMM<float>(mask,t,output_img,range,Heap);
            break;
        default:
            CV_Error( cv::Error::StsBadArg, "Unsupportedformat of the input image" );
    }
}
else if (flags == cv::INPAINT_NS) {
    switch(CV_MAT_DEPTH(output_img->type))
    {
        case CV_8U:
            icvNSInpaintFMM<uchar>(mask,t,output_img,range,Heap);
            break;
        case CV_16U:
            icvNSInpaintFMM<ushort>(mask,t,output_img,range,Heap);
            break;
        case CV_32F:
            icvNSInpaintFMM<float>(mask,t,output_img,range,Heap);
            break;
        default:
            CV_Error( cv::Error::StsBadArg, "Unsupported format of the input image" );
    }
} else {
    CV_Error( cv::Error::StsBadArg, "The flags argument must be one of CV_INPAINT_TELEA or CV_INPAINT_NS" );
}

}

void cv::inpaint( InputArray _src, InputArray _mask, OutputArray _dst,
double inpaintRange, int flags )
{
CV_INSTRUMENT_REGION();

Mat src = _src.getMat(), mask = _mask.getMat();
_dst.create( src.size(), src.type() );
Mat dst = _dst.getMat();
CvMat c_src = cvMat(src), c_mask = cvMat(mask), c_dst = cvMat(dst);
icvInpaint( &c_src, &c_mask, &c_dst, inpaintRange, flags );

}

相关文章

  • 【06】OpenCV图像修复inpaint函数

    1.inpaint函数讲解在OpenCV的“photo.hpp”中定义了一个inpaint函数,可以用来实现图像的...

  • Inpaint软件一键智能去水印

    Inpaint软件是一款图像编辑处理软件,这款软件可以用于修复图像、去除水印、优化图质等。本文中,软发网将着重介绍...

  • demo01

    图像处理基本操作 1. 读取图像 opencv提供了imread函数来读取图像,该函数有两个参数,filepath...

  • OpenCV5-图像处理组件

    OpenCV开发->图像组件->imgproc组件(image process)高等数学(算法->高斯函数)图像处...

  • OpenCV 之ios 轮廓矩

    OpenCV 之ios 轮廓矩 目标 在这节教程中您将学到: 使用OpenCV函数 moments 计算图像所有的...

  • OpenCV 之ios 给图像添加边界

    OpenCV 之ios 给图像添加边界 目标 本文档尝试解答如下问题: 如何使用OpenCV函数 copyMak...

  • OpenCV 之ios直方图计算

    OpenCV 之ios直方图计算 目标 本文档尝试解答如下问题: 如何使用OpenCV函数 split 将图像分割...

  • 图像重映射remap函数

    借助opencv中的remap函数可以实现图像特效。 效果图如下:

  • OpenCV 之ios 在图像中寻找轮廓

    OpenCV 之ios 在图像中寻找轮廓 目标 在这个教程中你将学到如何: 使用OpenCV函数 findCont...

  • OpenCV 之ios 图像金字塔

    OpenCV 之ios 图像金字塔 目标 本文档尝试解答如下问题: 如何使用OpenCV函数 pyrUp 和 py...

网友评论

      本文标题:【06】OpenCV图像修复inpaint函数

      本文链接:https://www.haomeiwen.com/subject/yyltldtx.html