图像的边缘检测和缺失修复(附matlab代码)

       最近在数学建模过程中老师给了个图片分别在清晰、有噪声及模糊状态下对其进行边缘检测的题目,最后一题是将图片中一部分抠出后将图片按照剩余图片的特征进行修复,整个问题涉及到人工智能及计算机视觉的领域较多,和队友三个人三天时间学到了不少东西,在这里对我们三个人的研究成果做一个总结,顺便和各位有需要的程序猿互相交流一波,欢迎评论。

———————————————————

目录:

1. 清晰图片的边缘检测

  1.1 5种边缘检测算子的对比

  1.2 Canny算子边缘检测

2. 噪声干扰下的图片边缘检测

  2.1 5种去噪方式的对比

  2.2 改进的Canny梯度幅值运算进行边缘检测

  2.3 对4种不同种类噪声进行分析

3. 模糊干扰下的图片边缘检测

  3.1 图片7种预处理方式的对比

  3.2 定义模糊度判断最优图片预处理方法

  3.3 对4种不同种类模糊进行分析

4. 基于纹理合成的快速自适应的自然图像缺失补全算法


好~由于整个项目我们是通过MATLAB完成的,那么我们先从如何安装MATLAB这个软件以及如何创建一个工程文件开始慢慢讲起......首先需要去...(不开玩笑,直接步入正题)

1. 清晰图片的边缘检测

  1.1 5种边缘检测算子的对比

    其实每种不同的边缘检测算子都是有模板的,至于他们的模型和原理可以自行上网查找,在这里只对他们做一个效果对比。

目前最流行的几种边缘检测有Roberts算子、Prewitt算子、Sobel算子、Canny算子和Log算子,这几种算子对图像的边缘提取都有着一定的应用,用他们对同一张比较简单的黑白图片进行边缘检测可以看出效果的不同,效果图如下:

                                     0566b21968558c7b4fc94a3a5eeb34f5.png

    对比一下就不难发现,canny算子检测效果是明显高于其他几种算子的,保留的信息更多,其它算子对清晰的图片检测都这个diao样就更不用说模糊和加噪声的图片了,所以后面只采用Canny来提取。

各算子具体的matlab代码:

Canny算子:

%Canny算子
close all
clear all
clc
I=imread('timg.jpg');   %导入路径下的图片
U=I;
I=rgb2gray(I);    %如果是灰度图片的话需要注释掉
I=im2double(I);
J=imnoise(I,'gaussian',0,0);
[K,thresh]=edge(J,'canny');
figure;
subplot(121);imshow(U);
xlabel('原图');
subplot(122);imshow(K);
xlabel('Canny检测');

Roberts算子:

clear all; close all;
I=imread('rice.png');  %导入路径下的图片
I=rgb2gray(I);    %如果是灰度图的话需要注释掉
I=im2double(I);
[J, thresh]=edge(I, 'roberts', 35/255);
figure;
subplot(121);  imshow(I);
xlabel('原图');
subplot(122);  imshow(J);
xlabel('roberts检测');

Prewitt算子:

clear all; close all;
I=imread('cameraman.tif');
I=rgb2gray(I);
I=im2double(I);
[J, thresh]=edge(I, 'prewitt', [], 'both');
figure;
subplot(121);  imshow(I);
xlabel('原图');
subplot(122);  imshow(J);
xlabel('Prewitt检测');

Sobel算子:

I=imread('gantrycrane.png');
I=rgb2gray(I);
I=im2double(I);
[J, thresh]=edge(I, 'sobel', [], 'horizontal');
figure;
subplot(121);  imshow(I);
xlabel('原图');
subplot(122);  imshow(J);
xlabel('Sobel检测');

Log算子:

I=imread('cameraman.tif');
I=rgb2double(I);
I=im2double(I);
J=imnoise(I, 'gaussian', 0, 0.005);
[K, thresh]=edge(J, 'log', [], 2.3);
figure;
subplot(121);  imshow(J);
xlabel('原图');
subplot(122);  imshow(K);
xlabel('Log检测');

  1.2Canny算子边缘检测

     在这里对Canny的相关原理做个补充,Canny的原理其实还是很简单的,大致分为下面几步:

    Step1:使用高斯滤波对图像进行降噪处理;

    Step2:使用一阶偏导的有限差分来计算梯度的幅值和方向;

               梯度幅值 M(x,y) 和梯度方向 798a135f47a1ec3dc636082b66dc9938.gif(x,y) 的计算公式如下:

                                                                    57b8e8f32f537663cccb0f46b143c5b2.gif

                                                                           798a135f47a1ec3dc636082b66dc9938.gif%20%28x%2Cy%29%3Darctan%5Cfrac%7BE_%7By%7D%28x%2Cy%29%7D%7BE_%7Bx%7D%28x%2Cy%29%7D

        其中,gif.latex?E_%7Bx%7D%28x%2Cy%29%5Efd6bf0db200b42d6b3ee4a7df8e3995b.gif分别为边缘通过点乘Sobel算子的到的不同方向梯度值,其计算公式如下:

                                           192dff924a5e3221eeaf08bd3c0aa831.png

    Step3:对梯度幅值进行非极大值抑制;

       由于在高斯滤波的过程中有将边缘宽度放大的可能,因此为使边缘宽度尽量为一个像素点,我们需要过滤非边缘点。若一个像素点属于边缘点,则此像素点在梯度方向上的梯度值最大,否则其灰度值为0。计算公式如下: 

                                                          66359a248ad13ad8011cc51be73b7fba.png

    Step4:使用双阈值算法检测和连接边缘。

        这是该算子最核心的一步,主要思想设置两类阈值,上阈值与下阈值。若梯度幅值小于下阈值,即认为是非边缘信息,将其排除(像素值设为0);若梯度幅值介于下阈值与上阈值之间,若该像素点和边缘相连则认为其是连续边缘的一部分,否则排除掉;若梯度幅值大于上阈值,则认为其是边缘。

       整个算法流程图如下:

                             46be8d599bc7ad1cd5801f2aa149f690.png

       Canny对灰度图的检测效果还ok,那么Canny对彩色图片提取效果咋么样呢。。也进行了尝试,效果还算勉强,但是对于很复杂的彩色图片就不行了,简单的还是ok的,效果图如下:

                                                    15a98fb37cd568d79f2b1c0b48814e74.png

有没有被香蕉君的笑容迷惑到 ,毕竟当代青年必须要在哲♂学中研究学习才有动力~

然后8,其实我有个想法就是通过代码对某段视频的图像进行固定帧数的不断截取,然后不断进行边缘检测,把结果图搞成一个视频,这样子可以对视频进行不断的边缘检测了,,,,好想搞一个某鲲打篮球的线条效果hhh,有时间一定要尝试。

2. 噪声干扰下的图片边缘检测

  2.1 5种去噪方式的对比

       有了噪声干扰以后的图片和普通Canny检测后的效果是啥样子呢。。。

                                           8820a8178e08e26c6abbecf13686e402.png   8177fd3f3070ae0c262bd862a3b121ae.png

    超级丑陋,,,多了很多杂七杂八的线,因为噪点本身就是让某个独立的像素点或者某个独立的像素矩阵种的像素值为零造成的,所以不加改进的话机器很容易会将其判断为边缘信息,然后错误提取。因此需要对图片进行预先的去噪处理,在这试用了5种常用的去噪方式,效果图如下:

                                        1f1af61a0421734e6a49f74feb139389.png

       可以看到的是不管哪种滤波方式其实都没有将噪声完全去除,只是一定程度上减少了噪声,可能在这上面看的不是很清楚,但是通过矢量图放大以后感觉中值滤波会稍微好一点,但是其实8,每种滤波方式对不同种类的噪声去噪能力不同,在这里不一一介绍,在2.3里面会对不同去噪方式对不同种类噪声的去噪效果做一个分析,在这里只针对这张图片来进行处理,所以后面对图片选择了中值滤波的处理方式。

  2.2 改进的Canny梯度幅值运算进行边缘检测

      这个模型呢是参考了一份研究生的毕业论文里面的内容:

      文献信息:靳艳红. 基于改进canny算法的图像边缘检测的研究[D].重庆师范大学,2011.

      这份文献里很清楚的把她改进以后的Canny边缘检测模型进行了说明,整个模型变化很大,从双阈值改进为单阈值,但整个模型和算法思想并不是很难,且篇幅不长,我对其进行了算法实现以后,对我们中值滤波以后的图片进行了边缘提取,效果如图:

                                                                   77ada3d7d06fd905dfb4daa0c56d3e29.png

        这张图和上面的检测图对比一下发现真的是好了很多了,所以当时结果出来以后,还是忍不住大喊了一句“研究生牛逼”。

但是这算法明显有很多可以改进的地方,边缘的线条太粗,如果加一个非极大值抑制其实会更好(这也是我在写这篇博客的时候想到的,当时脑子抽筋居然没想到,wtf.....有兴趣的朋友可以尝试对它进行优化一下)。

        其次就是提取后的图片中剩余的独立存在的噪点还是较多,虽然效果已经很好了,但是很多点点在上面看着就是很难受,于是8,观察了一下这张图的像素矩阵以后发现,那些独立的噪点大多还是以小于等于5×5的矩阵形式存在,于是我对这个算法又做了进一步改进,遍历整个256*256的像素矩阵,对那些5×5的独立像素矩阵进行了排除,只要这个5*5的矩阵周围一圈都没有其他像素点,那么就将整个5*5的矩阵像素值设为0即可。做出的第二次改进的检测效果图如下:

                                                                  465e3fab0585894a453a6cb92ec6efdd.png

      这次就干净好多了,第一次剩余的大部分噪点都已经排除干净了,但是有些本来属于边缘的由于检测不完整造成的独立的边缘点也一起被排除掉了,不过舍不得羊群套不住狼,这种情况在所难免,所以我还是比较喜欢第二次改进后的结果。

两次改进的Canny算法:

close all
clear all
clc
I=imread('timg.jpg');   %导入路径下的图片
U=I;
I=rgb2gray(I);  %如果是灰度图需要注释掉
 
I=zzlb(I,3);    %3是个常用参数,可以控制滤波的程度,具体内容可自行百度
I=double(I);
%求四个方向的梯度
w=fspecial('sobel');
Fx=imfilter(I,w,'replicate');      %求横边缘
w=w';
Fy=imfilter(I,w,'replicate');      %求竖边缘
for i=1:256
    for j=1:256
        if i-1>0&&j+1<=256&&i+1<=256&&j-1>0
            F45(i,j)=I(i-1,j+1)-I(i+1,j-1);
            F135(i,j)=I(i+1,j+1)-I(i-1,j-1);
        else
            F45(i,j)=I(i,j);
            F135(i,j)=I(i,j);
        end
    end
end
for i=1:256
    for j=1:256
        Ex(i,j)=Fx(i,j)+[F45(i,j)+F135(i,j)]/2;
        Ey(i,j)=Fy(i,j)+[F45(i,j)-F135(i,j)]/2;
        %像素的梯度幅值
        M(i,j)=sqrt(Fx(i,j)^2+Fy(i,j)^2+F45(i,j)^2+F135(i,j)^2);
        %梯度方向
        Q(i,j)=atan(Ey(i,j)/Ex(i,j));
    end
end
 
m=256;n=256;
g=(1/(m*n))*(sum(M(:)));
for i=1:256
    for j=1:256
        M1(i,j)=(M(i,j)-g)^2;
    end
end
o=sqrt((1/(m*n))*sum(M1(:)));
%求阈值
k=g+o;
 
for i=2:255
    for j=2:255
        if M(i-1,j)>k||M(i-1,j+1)>k||M(i-1,j-1)>k||M(i,j-1)>k||M(i,j+1)>k||M(i+1,j+1)>k||M(i+1,j-1)>k||M(i+1,j)>k
            M(i,j)=1;
        else
            M(i,j)=0;
        end
    end
end
M2=M;
%第二次改进,剔除掉独立的噪点
for i=4:253
    for j=4:253
        if M(i-3,j-3)==0&&M(i-3,j-2)==0&&M(i-3,j-1)==0&&M(i-3,j)==0&&M(i-3,j+2)==0&&M(i-3,j+1)==0&&M(i-3,j+3)==0&&M(i-2,j-3)==0&&M(i-2,j+3)==0&&M(i-1,j-3)==0&&M(i-1,j+3)==0&&M(i,j-3)==0&&M(i,j+3)==0&&M(i+1,j-3)==0&&M(i+1,j+3)==0&&M(i+2,j-3)==0&&M(i+2,j+3)==0&&M(i+3,j-3)==0&&M(i+3,j-2)==0&&M(i+3,j-1)==0&&M(i+3,j)==0&&M(i+3,j+3)==0&&M(i+3,j+2)==0&&M(i+3,j+1)==0                                        
            M(i,j)=0; M(i-1,j-1)=0;  M(i-1,j)=0; M(i-1,j+1)=0; M(i,j-1)=0; M(i,j+1)=0;M(i+1,j+1)=0;M(i+1,j)=0; M(i+1,j-1)=0; 
           M(i-2,j-2)=0;M(i-2,j-1)=0;M(i-2,j)=0; M(i-2,j+1)=0; M(i-2,j+2)=0;M(i-1,j-2)=0;M(i-1,j+2)=0;M(i,j-2)=0;M(i,j+2)=0;M(i+1,j-2)=0; M(i+1,j+2)=0;
           M(i+2,j-2)=0; M(i+2,j-1)=0;M(i+2,j)=0;M(i+2,j+1)=0; M(i+2,j+2)=0;
        end
    end
end
 
I=U;
I=rgb2gray(I);
I=im2double(I);
J=imnoise(I,'gaussian',0,0);
[K,thresh]=edge(J,'canny');
figure;
subplot(141);imshow(U);
xlabel('原图');
subplot(142);imshow(K);
xlabel('传统canny');
subplot(143);imshow(M2);
xlabel('改进一次canny');
subplot(144);imshow(M);
xlabel('改进两次canny');

中值滤波的函数(zzlb(I,3)):

%------
 I:原图片(灰度图)的像素矩阵
 m:中值滤波参数,不同参数有着不同程度的滤波效果
%------

%中值滤波的函数代码
function [ img ] = zzlb( image, m )
    n = m;
    [ height, width ] = size(image);
    x1 = double(image);
    x2 = x1;
    for i = 1: height-n+1
        for j = 1:width-n+1
            mb = x1( i:(i+n-1),  j:(j+n-1) );
            mb = mb(:);
            mm = median(mb);
            x2( i+(n-1)/2,  j+(n-1)/2 ) = mm;
 
        end
    end
    img = uint8(x2);
end

  2.3 对4种不同种类噪声进行分析

       上面提到过不同的滤波方式其实对不同种类噪声的过滤效果也是不同的,所以改进后的算法的灵敏度和稳定性有待验证,于是我们给同一张图片添加不同种类的噪声并且在不进行任何滤波处理的情况下,分别用传统Canny和改进后的Canny进行边缘检测做出了效果对比图,为了凸显效果的好坏用了另一张比较好检测的图片试了一下:  

                               c543407c31227ed789546f60c538cbd6.png      

        可见该算法其实稳定性还不错,除了对乘性噪声的抗干扰能力有待增强以外,其它的三种噪声检测效果都还是很满意的了。最后忍不住再喊一句“还是研究生牛逼!”,天气冷(tql),我睡了(wsl)。

3. 模糊干扰下的图片边缘检测

  3.1 图片7种预处理方式的对比

      和上面一样,先给出加了模糊后图片和普通Canny算子检测的效果图,让大家康康是啥鸭子,,,为了增大效果,换了轮廓比较清楚的图:

                                           5ab15f1870f99b6ebf129ff1f1466a1d.png             a8b0b30f58dc9f814aee2b5ff9ed77a1.png

         观察图片像素矩阵可以发现,其实模糊就是让原本差值较大的两个点的像素值更加接近,减少了差值,造成整张图片的模糊效果。而边缘检测其实就是通过判断像素值差距较大的地方,所以对检测造成了干扰,给一个边缘附近的像素矩阵图加深下理解:

                     81ffe141b4b599e2510ecfe9e6dd1f45.png

        和噪声一样,首先需要对其进行预处理,针对处理后的图片再采用Canny算子对其进行检测,对上面那张图的清晰图进行略微的模糊以后,效果图如下:

                     fd5482bea2e2f91a9a02a2a68f9ac65f.png

         最后几张图有点恐怖,是因为过度提取造成的,把每个轮廓都给提取出来了,不过也应该反应出,最后几种处理方案是对超级模糊的图片进行预处理的最好方式。但是在这种轻微模糊状态下,我们认为还是Prewitt锐化处理比较不错(图中已标红)。

PS:最后三种处理方法是一个层层递进的关系,后面的方法是在前面处理的结果上进一步处理。

这样子的话,对图片分别做一个2倍、5倍和10倍的模糊,然后做一个对比图如下:

                   ef5a96f2a7d1b6774a7279125f1c2603.png

为什么只做这三个方法的对比图呢,,,,见 3.2

  3.2 定义模糊度判断最优图片预处理方法

       上面说过了,图片越模糊像素矩阵中各像素值应该越靠近,如边缘两侧像素值本身应该具有明显的差值,但经过模糊处理以后差值明显变小,因此可以通过计算图片像素矩阵的标准差大小来对图片的模糊程度进行评价。(此处的模糊度是我们自己定义的,不是官方标准),像素矩阵的标准差计算公式就不用给了8,只给出模糊度的计算公式:

                                                                     e35323c396451c0ebc2f657b9ce3b88e.png

其中185df2ef2e6e5f531661a4df357ae7fa.gif为第c41d653023a050de9bcff8a9260a5ecc.gif种程度模糊后的模糊度大小,1813a657c7ee45b062ec907d764a8786.gif为第c41d653023a050de9bcff8a9260a5ecc.gif种程度模糊后图片像素矩阵的标准差大小,918434cb3ce9ff0c1c7d593aeb9061a0.gif为原图片像素矩阵标准差大小。

         根据该计算公式,用Photoshop对图片不同程度的模糊处理后做出了模糊度大小随模糊程度变化的曲线图如下:

                                                   8c1915c8e8f76a96518325c7adce8b62.png

       很明显这个模糊度是随着模糊程度增大而增大的,那么根据这个标准,用程序不断的去遍历不同程度模糊下各种处理方法的检测效果,然后根据肉眼判断(没错,就是肉眼判断......没有找到更好的判断标准,只能这鸭子了)选取最好的处理方案,最后给出了方案选取表格如下: 

                                               13df15d186f674a49cd6fcd008d9f536.png

        根据这个表格就可以判断什么时候该使用什么方法去处理图片了。

(知道为啥上面只对那三种方法进行对比了吧)

  3.3 对4种不同种类模糊进行分析

         还是和噪声图片一样,不同处理方法对不同种类的模糊有不同的效果。(哈?模糊还有种类?)没错!就是有种类,噪声有不同的种类,模糊也有da,在这里我们共收集了四种常见模糊:马赛克模糊、高斯模糊、运动模糊和径向模糊,每种模糊造成的效果都不同(可以自行查阅相关资料)。这三种算法对四种模糊的处理效果对比图如下:(写到这里突然发现好像这三个没做完整,那就把做了的给出吧,有时间再补充)

                          2a6b85b96cf7c8435a17618cb3bb1987.png

                           c0abd1d95f2bc2e6b681a7daf4e8d6f7.png

                            88e955cb24ffbfc927b9c5968d10495f.png 

       总结就是,,Laplacian锐化算子的效果最好,灵敏度最低,其他三种处理方法灵敏度都还是挺高的,但是8,,,高斯模糊是什么算子都可以搞它,很没有地位的一种模糊,,,

4. 基于纹理合成的快速自适应的自然图像缺失补全算法

          这个还是超好玩的,根据需求从Github找到了一份代码,玩了半天,,,,

          啥叫图像缺失补全呢,其实就是给你一张图片,随便扣掉一块,它可以帮你还原,或者说把图片中的某个人或者物给搞掉,类似于把他(或它)从图片里抹除,,

         上一张图康康,假设原来一张图是这个样子:

                                                      e354ebbec4d58ecb46a854ff9ccfa88d.png

    然后你看图片里面那个男的不爽,想搞掉他,那,,,,

                                                      df44e6888d5668d0a83fdadab4ff9e56.png

       然后,,,,,,,,,,他就没了,,,,,,

                                                      17010654016b643767cc559126146499.png

       可能非程序猿看到这里很想吐槽,这不就几下PS的事情吗,我手动也可以把他搞掉,甚至可以P的更真实,,,,那你可能无法做到完全让机器来做这个过程,让电脑自动来把这个图像补全可没你想的那么简单,需要设计一个非常高效算法来让计算机自动完成这整个过程,,,,这种算法都是各种大牛研究了很久的东西,反正我是没看懂,就简要提一下它的算法思想好了,,,,它其实就是通过缺失区域的周围的像素点来判断待修复的区域哪个像素点应该是什么颜色,从外往内不断插值的过程,直到所有像素点都被填充完毕。。。给个图理解一下:

                                                     e85b007d52c458e22d1df2a62e3505b7.png

        当然具体细节肯定没有这么简单啦,最后因为时间问题也没有对算法做出自己的改进,有兴趣的大佬可以去研究下,,

        然后不知道大家有没有注意到,这个算法的名字有个关键词,是对自然图片进行修复的一种算法,上面搞掉小男孩那张图已经可以说明它的效果是非常不错的了,那它对非自然图片的效果如何,,,,也做了试验,这次先给一张做出的最满意的效果图:

                                                      fe56e0703a1ce2bf4f0e447f0d318877.png

先卖个关子,你能看出哪里少了点什么吗?

没错,就是少了Tom的女朋友Honey,

                                              dff4ce8b13befd627f4d6a985f7c2597.png

其实原图是这个样子的,,,毕竟Jerry和Tom才是真爱,

 

                                                        e09ee4f99dd1ee1efe0761264f335ade.png

为了把Jerry搞掉,,程序运行一次以后的效果如下:

                                                       1fd289c9424df902f6cd9ab677f3c98e.png

       和上面那张图对比一下,确实这个算法对非自然图片的修复效果很一般,还残留了很多Jerry身体的某个部分,,,然后继续手动选取需要进一步修复的部分(相当于一个迭代过程),大约是不断修复了5 6次的鸭子,最后得到了上面那张比较满意的效果图。 虽然很笨拙,每次需要人工干预去告诉机器哪里要重新搞,但是也还是能接受的,毕竟对非自然图片不是它的强项,,,,,强人锁男可不太好,这样会男上加男,,造成前后为男,,,

       其实现在很多智能算法也可以对这类问题进行解决,比如用神经网络对图片训练集训练以后来填补空缺,虽然效果肯定是很不错的,但是用这种方法来研究一个数学建模问题没有多大的意义,而且作为一个本科生,那些东西是打死我也研究不出什么东西的,只能是套用套用再套用,,,

 

基于纹理合成的快速自适应的自然图像补全算法:

main.m:

close all; % closes all figures
clear;
im = im2single(imread('../samples/river.jpg'));
%im = im2single(imread('../samples/water_small.jpg'));
patch_size = 21;
figure(3), hold off, imagesc(im)
[x, y] = ginput;                                                              
target_mask = poly2mask(x, y, size(im, 1), size(im, 2)); 
hole_filling_crimnisi(im, target_mask, patch_size, 0.01, 'river');

ssd_patch_2d.m:

function [ ssd_map ] = ssd_patch_2d( I, T )
    mask = T >= 0; % -1 represents empty
    ssd_map = filter2(mask, I .* I, 'valid') + sum(sum(T .* T)) - 2 * filter2(mask .* T, I, 'valid');
end

ssd_patch.m:

function [ ssd_map ] = ssd_patch(I, T)
    ssd_map_r = ssd_patch_2d(I(:, :, 1), T(:, :, 1));
    ssd_map_g = ssd_patch_2d(I(:, :, 2), T(:, :, 2));
    ssd_map_b = ssd_patch_2d(I(:, :, 3), T(:, :, 3));
    ssd_map = ssd_map_r + ssd_map_g + ssd_map_b;
    ssd_map = normalize_2d_matrix(ssd_map);
end

set_forbid_region.m:

function [ ssd_map ] = set_forbid_region( ssd_map, target_mask, patch_size )
    LARGE_CONST = 100;
    hp_size = floor(patch_size / 2);
    forbid_area = imdilate(target_mask, ones(patch_size, patch_size));
    ssd_map = ssd_map + (forbid_area(hp_size + 1 : size(target_mask, 1) - hp_size,...
          hp_size + 1 : size(target_mask, 2) - hp_size) * LARGE_CONST);
end

point_fil.m:

function [ value ] = point_fil( I, h, patch_size, y, x )
hp_size = floor(patch_size / 2);
value = sum(sum(I(y - hp_size : y + hp_size, x - hp_size : x + hp_size) .*  h));
end

normalize_2d_matrix.m:

function [ norm_m ] = normalize_2d_matrix( m )
    norm_m = (m - min(min(m))) / (max(max((m)) - min(min(m))));
end

norm_v.m:

function [ norm_vec ] = norm_v( target_mask, y, x )
    window = target_mask(y - 1 : y + 1, x - 1 : x + 1);
    center_value = window(2, 2);
    window(window == 0) = center_value;
    fx = window(2, 3) - window(2, 1);
    fy = window(3, 2) - window(1, 2);
    if fx == 0 && fy == 0
        norm_vec = [1; 1] / norm([1; 1]); 
    else
        norm_vec = [fx; fy] / norm([fx; fy]);
    end
end

isophote.m:

function [ isoV ] = isophote(im, y, x)
    window = im(y - 1 : y + 1, x - 1 : x + 1);
    center_value = window(2, 2);
    window(window == -1) = center_value;
    fx = window(2, 3) - window(2, 1);
    fy = window(3, 2) - window(1, 2);
    if fx == 0 && fy == 0
       isoV = [0; 0]; 
    else
        I = sqrt(fx^2 + fy^2);
        theta = acot(fy / fx);
        [isoV_x, isoV_y] = pol2cart(theta, I); 
        isoV = [isoV_x; isoV_y];
    end
end

hole_filling_crimnisi.m:

function [ syn_im ] = hole_filling_crimnisi( I, target_mask, patch_size, tol, out_file_name )
    figure(5), imshow(I);
    I = repmat((~target_mask), 1, 1, 3) .* I;
    figure(11), imshow(I);
    out_file_name_mask = strcat('../results/', out_file_name, '_hole_mask.jpg');
    imwrite(I, out_file_name_mask);
    syn_im = I;
    syn_im(syn_im == 0) = -1;
    hp_size = floor(patch_size / 2);
    confidence_map = double(~target_mask);
    i = 1;
    while any(target_mask(:) == 1)
        [t_candi_x, t_candi_y] = fill_front(target_mask);
        [template, y, x, confidence] = choose_template_criminisi(syn_im, t_candi_y, t_candi_x, target_mask, confidence_map, patch_size);
        ssd_map = ssd_patch(syn_im, template);
        ssd_map = set_forbid_region( ssd_map, target_mask, patch_size );
        patch = choose_sample(ssd_map, tol, syn_im, patch_size, 0.0001); 
        %figure(6), imshow(patch); 
        tplt_mask = template >= 0;
        %cut_mask = imdilate((~t_mask), ones(overlap * 2 + 1, overlap * 2 + 1));
        patch = tplt_mask .* template + ~tplt_mask .* patch;
        %figure(7), imshow(patch);
        syn_im(y - hp_size : y + hp_size, x - hp_size : x + hp_size, :) = patch;
        figure(8), imshow(syn_im); 
        target_mask(y - hp_size : y + hp_size, x - hp_size : x + hp_size) = 0;
        %figure(9), imshow(target_mask);
        confidence_map(y - hp_size : y + hp_size, x - hp_size : x + hp_size) =...
            confidence_map(y - hp_size : y + hp_size, x - hp_size : x + hp_size)...
            + ((~tplt_mask(:, :, 1)) * confidence);
        i = i + 1;
    end
figure(10), imshow(syn_im);
out_file_name = strcat('../results/', out_file_name, '_hole.jpg');
imwrite(syn_im, out_file_name);
end

find_err_patch_2D.m:

function [ err_patch ] = find_err_patch_2D( T, patch, overlap)
    diff = T(1 : overlap, :) - patch(1 : overlap, :);
    err_patch = diff .* diff;
end

find_err_patch.m:

function [ err_patch ] = find_err_patch( T, patch, overlap)
    err_patch_r = find_err_patch_2D( T(:, :, 1), patch(:, :, 1), overlap);
    err_patch_g = find_err_patch_2D( T(:, :, 2), patch(:, :, 2), overlap);
    err_patch_b = find_err_patch_2D( T(:, :, 3), patch(:, :, 3), overlap);
    err_patch = err_patch_r + err_patch_g + err_patch_b;
end

find_cut_mask.m:

function [ mask ] = find_cut_mask(template, patch, overlap)
    t_size = size(template, 1);
    mask = zeros(t_size);
    mask_up = zeros(overlap, t_size);
    mask_left = zeros(t_size, overlap);
    is_up = nnz(template(1 : overlap, ceil(t_size / 2), 1) >= 0);
    is_left = nnz(template(ceil(t_size / 2), 1 : overlap, 1) >= 0);
    if is_up > 0
        err_patch = find_err_patch(template, patch, overlap);
        mask_up = cut_dp(err_patch);
    end
    if is_left > 0
        err_patch = find_err_patch(permute(template, [2 1 3]), permute(patch, [2 1 3]), overlap);
        mask_left = cut_dp(err_patch)';
    end
    mask(1 : overlap, :) = mask(1 : overlap, :) | mask_up;
    mask(:, 1 : overlap) = mask(:, 1 : overlap) | mask_left;
    mask;
end

fill_front.m:

function [ front_x, front_y ] = fill_front( target_mask )
    front = imdilate(target_mask, ones(3,3)) & ~target_mask;
    [front_y, front_x] = find(front);
end

choose_template_criminisi.m:

function [ template, y, x, conf] = choose_template_criminisi(I, t_candi_y, t_candi_x, target_mask, confidence_map,  patch_size)
   
    data = zeros(size(t_candi_y));
    confidence = zeros(size(t_candi_y));
    hp_size = floor(patch_size / 2);
    
    for i = 1 : size(t_candi_y, 1)
        yy = t_candi_y(i); xx = t_candi_x(i);
        if xx == 121 && yy == 253
            xx;
        end
        norm_vec = norm_v(target_mask, yy, xx);
        iso_v = isophote(I(:, :, 1), yy, xx);
        confidence(i) = point_fil(confidence_map, ones(size(patch_size)), patch_size, yy, xx) / (patch_size^2);
        data(i) = abs(dot(iso_v, norm_vec(:, 1)));
    end
    priority = confidence + data;
    [priority, sorted_id] = sort(priority, 'descend');
    t_candi_y = t_candi_y(sorted_id);
    t_candi_x = t_candi_x(sorted_id);
    confidence = confidence(sorted_id);
    data = data(sorted_id);
    y = t_candi_y(1); x = t_candi_x(1);
    conf = confidence(1);
    template = I(y - hp_size : y + hp_size, x - hp_size : x + hp_size, : );
end

choose_sample.m:

function [patch] = choose_sample( ssd_map, tol, I, patch_size, small_cost_value)
    min_c = min(min(ssd_map));
    min_c = max(min_c,small_cost_value);
    [y, x] = find(ssd_map <= min_c * (1 + tol));
    index = randi(size(y, 1));
    hp_size = floor(patch_size / 2);
    y = y(index) + hp_size; % transfrom to I coordinate
    x = x(index) + hp_size;
    patch = I((y - hp_size) : (y + hp_size), (x - hp_size) : (x + hp_size), :);
end

最后给大家推荐一本书,,,

                                                     86a442850112a57675e059ca1e65d351.png

       这本书对类似问题还是很有帮助的,整个建模过程也参考了很多,,网上也有电子版和对应的配套源文件,给大家安利一波,,,

       估计以后没什么时间再去专门长时间的研究这方面的东西了,,,不过还是感觉这个领域还是很有意思的,也没有深到理解不透的程度,感觉本科生也是可以对现有成果进行设计和改进的,,,欢迎各位大佬在评论内一起讨论交流,有不对的地方欢迎指出,顺便可以一起研究下哲♂学。

 

如有侵权请联系删除,转载请附上本文链接,尊重原创,谢谢!!

      

                                                       

 

 

评论 37
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值