28局部与分割-图像修补inpaint

28局部与分割-图像修补

inpaint算法的核心在于:提取带修补区域的边界,分别从最外层的边界到最里层的边界,然后依次进行修补。算法的思想如下:

δΩi = boundary of region to inpaint//修复区域的边缘
δΩ = δΩi
while (δΩ not empty)
{
    p = pixel of δΩ closest to δΩi//修复距离边缘最近的像素
    inpaint p using Eqn.2//利用公式2修复p点
    advance δΩ into Ω//把边缘向里行进
}

看到这里,就会有疑惑了,怎么确定像素与边缘的距离呢。

算法中为待修复区域边缘构建了一个窄边(narrowBand),就是上面所说的δΩ。在opencv里是利用先将mask膨胀得到mask2(结构元素是长为2*ε+1的十字形,以中心点为原点),再用mask2减去mask得到band图,则band中非0元素即narrowBand)。从这里可以看出最初的narrowBand(即δΩ1)是不需要修复的。确定窄边的目的就是为了找到下面要修复的像素。

首先将像素分为三类,用flag标识记录:BAND:其实就是δΩ上的像素; KNOWN:就是δΩ外部不需要修复的像素;INSIDE:就是δΩ内部的等待修复的像素。

另外,每个像素还需要存储两个值:T(该像素离到边缘 δΩ的距离);I(灰度值)。

下面先说一下处理像素是按怎样的行进方式的:

1. 初始化。首先按上面说的方法找到narrowBand,flag记为BAND;窄边内部的待修复区域记为INSIDE,已知像素flag设为KNOWN。BAND和KNOWN类型的像素T值初始化为0(这里看opencv代码里好像把KNOWN也设为106),INSIDE类型像素T值设为无限大(实际中设为106)。

2. 定义一个数据结构NarrowBand(opencv中采用双向链表实现),将窄边中的像素按T值升序排列,依次加入到NarrowBand中,先处理T最小的像素。假设为p点,将p点类型改为KNOWN,然后依次处理p点的四邻域点Pi。如果Pi类型为INSIDE,若是则重新计算I,修复该点,并更新其T值,修改该点类型为BAND,加入NarrowBand(这里仍按顺序,即始终保持NarrowBand是按升序排列的)。依次进行,每次处理的都是NarrowBand中T最小的像素,直到NarrowBand中没有像素。

代码如下:

while (NarrowBand not empty)
{
    extract P(i,j) = head(NarrowBand); /* STEP 1 *//*提取T值最小像素*/
    for (k,l) in (i-1,j),(i,j-1),(i+1,j),(i,j+1)/*依次处理该像素的四邻域像素*/
    {
        if (f(k,l)!=KNOWN)
        {
            if (f(k,l)==INSIDE)
            {
                f(k,l)=BAND; /* STEP 2修改(k,l)像素点的lag*/
                inpaint(k,l); /* STEP 3修复(k,l)像素点*/
            }
            T (k,l) = min(solve(k-1,l,k,l-1), /* STEP 4 更新(k,l)像素点的值*/
            solve(k+1,l,k,l-1),
            solve(k-1,l,k,l+1),
            solve(k+1,l,k,l+1));
            insert(k,l) in NarrowBand; /* STEP 5 将(k,l)像素点加入NarrowBand*/
        }
    }
}

下面是具体计算T的代码个地方,原文中的代码有错误,下面是参考opencv代码修改好的)

T(k,l)=min(solve(k-1,l,k,l-1),solve(k+1,l,k,l-1),solve(k-1,l,k,l+1),solve(k+1,l,k,l+1));
float solve(int i1,int j1,int i2,int j2)
{
    float sol = 1.0e6;
    if (f(i1,j1)==KNOWN)
        if (f(i2,j2)==KNOWN)
        {
            float r = sqrt(2-(T(i1,j1)-T(i2,j2))*(T(i1,j1)-T(i2,j2)));
            float s = (T(i1,j1)+T(i2,j2)-r)/2;
            if (s>=T(i1,j1) && s>=T(i2,j2)) sol = s;
            else
            { s += r; if (s>=T(i1,j1) && s>=T(i2,j2))                            sol = s; }
         }
    else sol = 1+T(i1,j1));
    else if (f(i2,j2)==KNOWN) sol = 1+T(i2,j2));
    return sol;
}

相关函数的接口:

扫描二维码关注公众号,回复: 939866 查看本文章
void inpaint(InputArray src, InputArray inpaintMask, OutputArray dst, double inpaintRadius, int flags)
参数说明:

src:原图像

 - inpaintMask:图像掩码,就是原图像要修复的部分,必须和要修复的图像一样大
 - dst:修复之后的图像
 - inpaintRadius:修复算法取临近值的半径
 - flags:修复算法的选择,有两种:INPAINT_NSI NPAINT_TELEA

使用代码如下:

#include<opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

static void help()
{
    cout << "\nCool inpainging demo. Inpainting repairs damage to images by floodfilling the damage \n"
        << "with surrounding image areas.\n"
        "Using OpenCV version %s\n" << CV_VERSION << "\n";
    cout << "Hot keys: \n"
        "\tESC - quit the program\n"
        "\tr - restore the original image\n"
        "\tw or SPACE - run inpainting algorithm\n"
        "\t\t(before running it, paint something on the image)\n" << endl;
}

Mat img, inpaintMask;
Point prevPt(-1, -1);

static void onMouse(int event, int x, int y, int flags, void*)
{
    if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
        prevPt = Point(-1, -1);
    else if (event == EVENT_LBUTTONDOWN)
        prevPt = Point(x, y);
    else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
    {
        Point pt(x, y);
        if (prevPt.x < 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("原图", img);
    }
}

int main() {
    Mat img0 = imread("fruits.jpg");
    if (img0.empty())
    {
        cout << "Couldn't open the image " << endl;
        return 0;
    }

    help();

    img = img0.clone();
    inpaintMask = Mat::zeros(img.size(), CV_8U);

    imshow("原图", img);
    setMouseCallback("原图", onMouse, 0);

    for (;;)
    {
        char c = (char)waitKey();

        if (c == 27)
            break;

        if (c == 'r')
        {
            inpaintMask = Scalar::all(0);
            img0.copyTo(img);
            imshow("原图", img);
        }

        if (c == 'w' || c == ' ')
        {
            Mat inpainted;
            inpaint(img, inpaintMask, inpainted, 3, INPAINT_TELEA);
            imshow("修复后", inpainted);
            imshow("图像掩码", inpaintMask);
        }
    }

    return 0;
}

结果如下:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/z827997640/article/details/80023839