MATLAB中的最近邻插值算法

20

我正在尝试编写自己的函数,使用最近邻插值算法对输入图像进行放大。糟糕的是,我能够看到它的工作原理,但找不到算法本身。非常感谢任何帮助。

以下是我尝试过的将输入图像放大两倍的方法:

function output = nearest(input)
[x,y]=size(input);
output = repmat(uint8(0),x*2,y*2);
[newwidth,newheight]=size(output);
for i=1:y
    for j=1:x
        xloc = round ((j * (newwidth+1)) / (x+1));
        yloc = round ((i * (newheight+1)) / (y+1));
        output(xloc,yloc) = input(j,i);
    end
end

以下是来自Mark的建议后的输出结果 alt text


抱歉,我当时在想什么 - 你需要迭代输出而不是输入,因为输出更大。在这种情况下,我的公式需要反转。 - Mark Ransom
4个回答

20

这个回答更注重解释而非简洁高效。我认为gnovice的解决方案在这方面最佳。如果你想要理解它是如何工作的,请继续阅读...

现在,你的代码问题在于,你正在将输入图像的位置映射到输出图像上,这就是为什么你得到了“斑点”输出的原因。考虑一个例子,其中输入图像全是白色,输出初始化为黑色,我们得到如下结果:

screenshot

你应该做的是相反的(从输出到输入)。为了说明,请考虑以下表示法:

1           c         1                 scaleC*c
+-----------+ 1       +----------------------+ 1
|    |      |         |        |             |
|----o      |   <===  |        |             |
|  (ii,jj)  |         |--------o             |
+-----------+ r       |      (i,j)           |
  inputImage          |                      |
                      |                      |
                      +----------------------+ scaleR*r
                            ouputImage

Note: I am using matrix notation (row/col), so:
  i ranges on [1,scaleR*r] , and j on [1,scaleC*c]
  and ii on [1,r], jj on [1,c]

我们的想法是,对于输出图像中的每个位置(i,j),我们希望将它映射到输入图像坐标中"最近"的位置。由于这是一个简单的映射,我们使用将给定的x映射到y的公式(给定所有其他参数):

 x-minX      y-minY
--------- = ---------
maxX-minX   maxY-minY

在我们的情况下,xi/j 坐标,而 yii/jj 坐标。因此,将每个坐标替换为其对应的值得到:

jj = (j-1)*(c-1)/(scaleC*c-1) + 1
ii = (i-1)*(r-1)/(scaleR*r-1) + 1

将所有部分组合起来,我们得到以下代码:

% read a sample image
inputI = imread('coins.png');
[r,c] = size(inputI);
scale = [2 2];        % you could scale each dimension differently

outputI = zeros(scale(1)*r,scale(2)*c, class(inputI));

for i=1:scale(1)*r
    for j=1:scale(2)*c
        % map from output image location to input image location
        ii = round( (i-1)*(r-1)/(scale(1)*r-1)+1 );
        jj = round( (j-1)*(c-1)/(scale(2)*c-1)+1 );

        % assign value
        outputI(i,j) = inputI(ii,jj);
    end
end

figure(1), imshow(inputI)
figure(2), imshow(outputI)

gnovice的解决方案大约快了10倍。不过,还是谢谢你的解释! - Wok
当使用最近邻缩放图像I到Izoom,然后以相同的因子缩小Izoom时,我们将得到原始图像I。现在,直观地说,我理解这是很自然的,因为我们只是进行像素复制而不进行任何平均化,但我似乎找不到更严谨的证明。我希望这里有人能够给我一些关于这个问题的指导。 - idexi

19

我之前阅读了imresize函数在MATLAB图像处理工具箱中的代码,为了创建一个仅实现最近邻插值的图像简化版本。以下是如何将其应用于您的问题:

%# Initializations:

scale = [2 2];              %# The resolution scale factors: [rows columns]
oldSize = size(inputImage);                   %# Get the size of your image
newSize = max(floor(scale.*oldSize(1:2)),1);  %# Compute the new image size

%# Compute an upsampled set of indices:

rowIndex = min(round(((1:newSize(1))-0.5)./scale(1)+0.5),oldSize(1));
colIndex = min(round(((1:newSize(2))-0.5)./scale(2)+0.5),oldSize(2));

%# Index old image to get new image:

outputImage = inputImage(rowIndex,colIndex,:);

另一个选项是使用内置的interp2函数,尽管您在其中一个评论中提到不想使用内置函数。

编辑:解释

如果有人感兴趣,我想解释一下上面的解决方案是如何工作的...

newSize = max(floor(scale.*oldSize(1:2)),1);

首先,为了获得新的行列大小,需要使用比例因子乘以旧的行列大小。将结果向下舍入到最接近的整数,可以使用floor函数。如果比例因子小于1,则可能出现一个尺寸值为0的奇怪情况,因此调用max以将小于1的所有值替换为1。

rowIndex = min(round(((1:newSize(1))-0.5)./scale(1)+0.5),oldSize(1));
colIndex = min(round(((1:newSize(2))-0.5)./scale(2)+0.5),oldSize(2));

接下来,为行和列都重新计算了一组索引。首先,计算了一个上采样图像的索引集:1:newSize(...)。将每个图像像素视为具有给定宽度,因此像素1跨越从0到1,像素2跨越从1到2等。因此,“坐标”被视为中心,这就是为什么从索引中减去0.5的原因。然后,这些坐标被除以比例因子,以给出原始图像的一组像素中心坐标,然后加上0.5并四舍五入以获取原始图像的一组整数索引。调用min确保这些索引中没有任何一个大于原始图像大小oldSize(...)

outputImage = inputImage(rowIndex,colIndex,:);

最终,新的上采样图像是通过简单地索引原始图像来创建的。


@Hellnar:很高兴能帮忙!我刚刚更新了代码,现在它可以适用于非整数比例因子以及灰度或RGB图像。 - gnovice

2
MATLAB已经为您完成了这项工作。使用imresize函数:imresize
output = imresize(input,size(input)*2,'nearest');

或者如果你想等比缩放x和y:

output = imresize(input,2,'nearest');

2
我已经知道内置函数,但我需要通过自己的代码来实现这个功能。谢谢。 - Hellnar
1
抱歉,我不知道!很高兴看到你找到了答案。 - Jacob

0

你只需要一个更通用的公式来计算xloc和yloc。

xloc = (j * (newwidth+1)) / (x+1);
yloc = (i * (newheight+1)) / (y+1);

这假设你的变量有足够的范围来存储乘法结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接