使用双线性插值调整图像大小而不会使用imresize

tea*_*rop 13 matlab interpolation image image-processing

我找到了一些放大图像的方法,但没有解缩缩图像的方法.我目前正在使用最近邻法.如果不使用imresizeMATLAB中的函数,我怎样才能使用双线性插值?

ray*_*ica 58

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

在开始使用任何代码之前,我将向您介绍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
Run Code Online (Sandbox Code Playgroud)

取上面的代码,将其复制并粘贴到一个名为的文件中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
Run Code Online (Sandbox Code Playgroud)

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

A = 

1
6
11
2
7
12
3
8
13
4
9
14
5
10
15
Run Code Online (Sandbox Code Playgroud)

现在,如果要访问元素编号9,则需要访问此数组的第11个元素.sub2ind如果要对图像中的元素进行矢量化以进行插值而不进行任何for循环,则返回插值位是至关重要的.因此,如果你看一下伪代码的最后一行,我们要访问的元素r,c,r+1c+1.请注意,所有这些都是2D数组,其中所有这些数组中每个匹配位置中的每个元素都告诉我们需要采样的四个像素,以便生成最终输出像素.的输出sub2ind意愿具有相同的尺寸作为输出图像的2D阵列.这里的关键是,在二维数组中的每个元素r,c,r+1,并且c+1会给我们列主要指标成图像,我们要访问,并通过抛出此作为输入索引图像,我们将准确地得到我们想要的像素位置.


在实现算法时,我想添加一些重要的细微之处:

  1. 您需要确保在图像外部插值时访问图像的任何索引都设置为1或行数或列数,以确保您不会超出范围.实际上,如果您向图像的右侧或下方延伸,则需要将其设置为低于最大值的一个,因为插值要求您将像素访问到右侧或下方的像素.这将确保你仍然在界限内.

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

  3. 我经历了一个for循环来自己插入每个通道.您可以智能地使用bsxfun,但我决定使用for循环以简化,以便您能够遵循该算法.


作为展示这种作用的一个例子,让我们使用作为onion.pngMATLAB系统路径一部分的图像.此图像的原始尺寸为135 x 198.让我们通过使其变大来插入此图像,其270 x 396大小是原始图像的两倍:

im = imread('onion.png');
out = bilinearInterpolation(im, [270 396]);
figure;
imshow(im);
figure;
imshow(out);
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

在此输入图像描述


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

im = imread('onion.png');
out = bilinearInterpolation(im, [68 99]);
figure;
imshow(im);
figure;
imshow(out);
Run Code Online (Sandbox Code Playgroud)

请注意,对于行,135的一半是67.5,但我向上舍入到68.这是我得到的:

在此输入图像描述

在此输入图像描述


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


祝好运!

  • 讲座(理查德·艾伦·彼得斯二世)很棒。但是,该链接不再有效。我找到了幻灯片 [here](https://archive.org/details/Lectures_on_Image_Processing)。有问题的幻灯片出现在第 15 讲的幻灯片编号 58 中。 (2认同)