使用相邻像素进行高效修复

Efficient inpaint with neighbouring pixels

我正在实施一个简单的算法来在 "damaged" 图像上进行内画。我有一个预定义的掩码,用于指定需要修复的区域。我的策略是从遮罩区域的边界开始,用相邻非零像素的中心平均值对每个像素进行内绘,重复直到没有留下未知像素。

function R = inPainting(I, mask)
H = [1 2 1; 2 0 2; 1 2 1];
R = I;

n = 1;
[row,col,~] = find(~mask); %Find zeros in mask (area to be inpainted)
unknown = horzcat(row, col)';
while size(unknown,2) > 0
    new_unknown = [];
    new_R = R;
    for u = unknown
        r = u(1);
        c = u(2);
        nb = R(max((r-n), 1):min((r+n), end), max((c-n),1):min((c+n),end));
        nz = nb~=0;
        nzs = sum(nz(:));

        if nzs ~= 0 %We have non-zero neighbouring pixels. In-paint with average.
            new_R(r,c) = sum(nb(:)) / nzs;
        else
            new_unknown = horzcat(new_unknown, u);
        end
    end
    unknown = new_unknown;
    R = new_R;
end

这很好用,但效率不高。是否可以主要使用矩阵运算来向量化这种方法?有人知道实现此算法的更有效方法吗?

如果我理解你的问题陈述,你会得到一个掩码,你希望用掩码中每个像素周围的邻域像素的平均值填充这个掩码中的这些像素。另一个约束是图像被定义为使得在相同空间位置属于掩模的任何像素在该掩模中为零。你从面具的边界开始,向面具的内部传播信息。鉴于此算法,不幸的是,您无法使用标准过滤技术执行此操作,因为当前时间步长取决于之前的时间步长。

图像过滤机制,如 imfilter or conv2 由于这种依赖性,无法在此处工作。

因此,我能做的就是帮助您加快循环中发生的事情,希望这能给您带来一些整体上的加速。我将向您介绍一个名为 im2col 的函数。这是来自图像处理工具箱,既然你可以使用imfilter,我们就可以使用这个功能。

im2col 创建一个二维矩阵,使每一列都是像素邻域 展开 到单个向量中。它的工作原理是抓取列主要顺序中的每个像素邻域,因此我们在图像的左上角获得一个像素邻域,然后向下移动一行,再移动另一行,继续前进直到到达最后一行。然后我们移动一列并重复相同的过程。对于我们拥有的每个像素邻域,它被展开为一个向量,输出将是一个 MN x K 矩阵,其中每个像素邻域的邻域大小为 M x N,并且有 K 社区。

因此,在循环的每次迭代中,我们可以将当前修复图像的像素邻域展开为单个向量,确定哪些像素邻域是非零的,然后从那里确定每个像素邻域有多少个零值选定的像素邻域。之后,我们计算这些非零列的平均值,忽略零元素。完成后,我们更新图像并进入下一次迭代。

我们首先需要做的是用 1 像素的边框填充图像,以便我们能够抓取超出图像边界的邻域。您也可以使用图像处理工具箱中的 padarray

因此,我们可以简单地这样做:

function R = inPainting(I, mask)
R = double(I); %// For precision

n = 1;
%// Change - column major indices
unknown = find(~mask); %Find zeros in mask (area to be inpainted)  

%// Until we have searched all unknown pixels
while numel(unknown) ~= 0    
    new_R = R;

    %// Change - take image at current iteration and 
    %// create columns of pixel neighbourhoods
    padR = padarray(new_R, [n n], 'replicate');
    cols = im2col(padR, [2*n+1 2*n+1], 'sliding');  

    %// Change - Access the right pixel neighbourhoods
    %// denoted by unknown       
    nb = cols(:,unknown);

    %// Get total sum of each neighbourhood
    nbSum = sum(nb, 1);

    %// Get total number of non-zero elements per pixel neighbourhood
    nzs = sum(nb ~= 0, 1);

    %// Replace the right pixels in the image with the mean
    new_R(unknown(nzs ~= 0)) = nbSum(nzs ~= 0) ./ nzs(nzs ~= 0);

    %// Find new unknown pixels to look at
    unknown = unknown(nzs == 0);

    %// Update image for next iteration
    R = new_R;
end

%// Cast back to the right type
R = cast(R, class(I));