使用双线性插值调整图像大小,而无需调整大小
image
image-processing
matlab
5
0

我发现了一些放大图像的方法,但是没有缩小图像的解决方案。我目前正在使用最近的邻居方法。在不使用MATLAB中的imresize函数的情况下,如何使用双线性插值来做到这一点?

参考资料:
Stack Overflow
收藏
评论
共 1 个回答
高赞 时间 活跃

在评论中,您提到要使用双线性插值来调整图像大小。请记住,双线性插值算法与大小无关。您可以很好地使用相同的算法来放大图像和缩小图像。采样像素位置的正确比例因子取决于您指定的输出尺寸。顺便说一下,这不会改变核心算法。

在开始编写任何代码之前,我将先向您介绍Richard Alan Peters的关于插值的II数字图像处理幻灯片 ,尤其是幻灯片#59。它很好地说明了如何进行MATLAB友好的双线性插值方法以及伪代码。为了自给自足,我将在此处包括他的幻灯片,以便我们可以继续进行编码:

在此处输入图片说明

让我们写一个函数来为我们做这件事。此函数将接收可以是彩色或灰度的图像(通过imread读取),以及两个元素的数组-您要调整大小的图像以及输出元素的尺寸为两元素的数组您想要的最终调整大小的图像。此数组的第一个元素将是行,而此数组的第二个元素将是列。我们将简单地通过此算法,并使用此伪代码计算输出像素的颜色/灰度值:

function [out] = bilinearInterpolation(im, out_dims)

    %// Get some necessary variables first
    in_rows = size(im,1);
    in_cols = size(im,2);
    out_rows = out_dims(1);
    out_cols = out_dims(2);

    %// Let S_R = R / R'        
    S_R = in_rows / out_rows;
    %// Let S_C = C / C'
    S_C = in_cols / out_cols;

    %// Define grid of co-ordinates in our image
    %// Generate (x,y) pairs for each point in our image
    [cf, rf] = meshgrid(1 : out_cols, 1 : out_rows);

    %// Let r_f = r'*S_R for r = 1,...,R'
    %// Let c_f = c'*S_C for c = 1,...,C'
    rf = rf * S_R;
    cf = cf * S_C;

    %// Let r = floor(rf) and c = floor(cf)
    r = floor(rf);
    c = floor(cf);

    %// Any values out of range, cap
    r(r < 1) = 1;
    c(c < 1) = 1;
    r(r > in_rows - 1) = in_rows - 1;
    c(c > in_cols - 1) = in_cols - 1;

    %// Let delta_R = rf - r and delta_C = cf - c
    delta_R = rf - r;
    delta_C = cf - c;

    %// Final line of algorithm
    %// Get column major indices for each point we wish
    %// to access
    in1_ind = sub2ind([in_rows, in_cols], r, c);
    in2_ind = sub2ind([in_rows, in_cols], r+1,c);
    in3_ind = sub2ind([in_rows, in_cols], r, c+1);
    in4_ind = sub2ind([in_rows, in_cols], r+1, c+1);       

    %// Now interpolate
    %// Go through each channel for the case of colour
    %// Create output image that is the same class as input
    out = zeros(out_rows, out_cols, size(im, 3));
    out = cast(out, class(im));

    for idx = 1 : size(im, 3)
        chan = double(im(:,:,idx)); %// Get i'th channel
        %// Interpolate the channel
        tmp = chan(in1_ind).*(1 - delta_R).*(1 - delta_C) + ...
                       chan(in2_ind).*(delta_R).*(1 - delta_C) + ...
                       chan(in3_ind).*(1 - delta_R).*(delta_C) + ...
                       chan(in4_ind).*(delta_R).*(delta_C);
        out(:,:,idx) = cast(tmp, class(im));
    end

使用上面的代码,将其复制并粘贴到名为bilinearInterpolation.m的文件中并保存。确保更改保存此文件的工作目录。


除了sub2ind ,也许还有meshgrid ,一切似乎都与算法一致。 meshgrid非常容易解释。您要做的只是指定一个(x,y)坐标的2D网格,其中图像中的每个位置都有一对(x,y)或列和行坐标。通过meshgrid创建网格可避免任何for循环,因为我们将在继续之前从所需算法中生成所有正确的像素位置。

sub2ind工作方式是将其放在2D矩阵中的行和列位置(好吧……它实际上可以是您想要的任意数量的尺寸),并输出单个线性索引。如果您不知道MATLAB如何索引到矩阵中,则可以通过两种方式访问矩阵中的元素。您可以使用行和列来获取所需的内容,也可以使用以列为主的索引。看下面这个矩阵示例:

A = 

1  2  3  4  5
6  7  8  9  10
11 12 13 14 15

如果我们要访问数字9,我们可以执行A(2,4) ,这是大多数人倾向于默认的。还有一种使用单个数字访问数字9的方法,即A(11) ...现在情况如何? MATLAB以列优先格式布置其矩阵的内存。这意味着,如果您要使用此矩阵并将其所有堆叠到一个数组中,则它将如下所示:

A = 

1
6
11
2
7
12
3
8
13
4
9
14
5
10
15

现在,如果要访问元素编号9,则需要访问此数组的第11个元素。回到插值位,如果要向量化访问图像中的元素以进行插值而不进行任何for循环,则sub2ind至关重要。这样,如果您查看伪代码的最后一行,我们想访问rcr+1c+1处的元素。请注意,所有这些都是2D数组 ,其中所有这些数组中每个匹配位置的每个元素都告诉我们我们需要从中采样的四个像素才能产生最终的输出像素。 sub2ind的输出将是与输出图像大小相同的2D数组。这里的关键是rcr+1c+1的2D数组的每个元素将为我们提供要访问的图像的列主索引,并将其作为输入输入图像对于索引,我们将准确获得所需的像素位置。


在实现算法时,我想补充一些重要的细节:

  1. 您需要确保将在图像外部进行插值时用于访问图像的所有索引都设置为1或行数或列数,以确保不会超出范围。实际上,如果您扩展到图像的右侧或下方,则需要将其设置为最大值以下之一,因为插值要求您访问的像素是右侧或下方的一。这将确保您仍然在界限之内。

  2. 您还需要确保将输出图像强制转换为与输入图像相同的类。

  3. 我运行了一个for循环,以自己对每个通道进行插值。您可以使用bsxfun明智地执行此bsxfun ,但是为了简单起见,我决定使用for循环,以便您能够跟随算法一起使用。


作为展示此工作原理的示例,让我们使用属于MATLAB系统路径一部分的onion.png图像。该图像的原始尺寸为135 x 198 。让我们通过放大图像来对其进行插值,使其达到270 x 396 ,这是原始图像尺寸的两倍:

im = imread('onion.png');
out = bilinearInterpolation(im, [270 396]);
figure;
imshow(im);
figure;
imshow(out);

上面的代码将通过将每个维度增加两倍来对图像进行插值,然后显示一个具有原始图像的图形和另一个具有放大图像的图形。这是我得到的两个:

在此处输入图片说明

在此处输入图片说明


同样,让我们将图像缩小一半:

im = imread('onion.png');
out = bilinearInterpolation(im, [68 99]);
figure;
imshow(im);
figure;
imshow(out);

请注意,行的135的一半为67.5,但是我将其舍入为68。这是我得到的:

在此处输入图片说明

在此处输入图片说明


我在实践中注意到的一件事是,与其他方案(如双三次...甚至Lanczos)相比,使用双线性进行上采样具有不错的性能。但是,在缩小图像时(因为要删除细节),最近的邻居就足够了。我发现双线性或双三次过大。我不确定您的应用程序是什么,但是尝试使用不同的插值算法,然后从结果中查看您喜欢的内容。 Bicubic是另一个故事,我将作为练习留给您。如果您有兴趣,我推荐您的那些幻灯片确实有关于三次三次插值的资料。


祝好运!

收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号