imresize - 试图理解双三次插值

Gil*_*lad 5 matlab interpolation image-processing

我正在尝试理解这个功能:

function [weights, indices] = contributions(in_length, out_length, ...
                                            scale, kernel, ...
                                            kernel_width, antialiasing)


if (scale < 1) && (antialiasing)
    % Use a modified kernel to simultaneously interpolate and
    % antialias.
    h = @(x) scale * kernel(scale * x);
    kernel_width = kernel_width / scale;
else
    % No antialiasing; use unmodified kernel.
    h = kernel;
end
Run Code Online (Sandbox Code Playgroud)

我真的不明白这条线是什么意思

 h = @(x) scale * kernel(scale * x);
Run Code Online (Sandbox Code Playgroud)

我的规模是0.5
内核是立方的.

但除此之外是什么意思?我认为这就像创建一个稍后会调用的函数?

cha*_*pjc 11

imresize 通过简单地扩展立方核而不是离散的预处理步骤,在缩小图像时实现抗锯齿.

对于kernel_width4个像素(重新缩放后为8个),其中contributions函数对每个像素使用10个邻居,kernelvs h(缩放内核)看起来像(非标准化,忽略x轴):

在此输入图像描述

这比在单独的预处理步骤中首先执行低通滤波器或高斯卷积更容易.

立方内核定义在底部imresize.m:

function f = cubic(x)
% See Keys, "Cubic Convolution Interpolation for Digital Image
% Processing," IEEE Transactions on Acoustics, Speech, and Signal
% Processing, Vol. ASSP-29, No. 6, December 1981, p. 1155.

absx = abs(x);
absx2 = absx.^2;
absx3 = absx.^3;

f = (1.5*absx3 - 2.5*absx2 + 1) .* (absx <= 1) + ...
                (-0.5*absx3 + 2.5*absx2 - 4*absx + 2) .* ...
                ((1 < absx) & (absx <= 2));
Run Code Online (Sandbox Code Playgroud)

参考文件的PDF.

相关部分是等式(15):

在此输入图像描述

这是a = -0.5以下等式中的一般插值方程的特定版本:

在此输入图像描述

a通常设置为-0.5或-0.75.注意,a = -0.5对应于Cubic Hermite样条曲线,它将是连续的并且具有连续的第一导数. OpenCV似乎使用-0.75.

但是,如果您编辑[OPENCV_SRC]\modules\imgproc\src\imgwarp.cpp并更改代码:

static inline void interpolateCubic( float x, float* coeffs )
{
    const float A = -0.75f;
    ...
Run Code Online (Sandbox Code Playgroud)

至:

static inline void interpolateCubic( float x, float* coeffs )
{
    const float A = -0.50f;
    ...
Run Code Online (Sandbox Code Playgroud)

并重建OpenCV(提示:在短编译时禁用CUDA和gpu模块),然后得到相同的结果.在OP的相关问题的其他答案中查看匹配的输出.

  • @Gilad我记得你正在研究MATLAB与OpenCV立方插值,似乎MATLAB的差异是a = -0.5,而OpenCV的差异是a = -0.75(https://github.com/Itseez/opencv/blob /master/modules/imgproc/src/imgwarp.cpp#L155). (2认同)
  • @chappjc:+1好找.我实际上正在写一个关于:)的答案 (2认同)

Amr*_*mro 10

这是对先前 关于MATLAB和OpenCV 之间的差异的后续问题的后续问题,给出了双三次插值.imresizecv::resize

我对自己感兴趣的是找出原因存在差异.这些是我的发现(因为我理解算法,如果我犯了任何错误,请纠正我).


考虑将图像大小调整为从大小的输入图像到大小M-by-N的输出图像的平面变换scaledM-by-scaledN.

问题是这些点不一定适合离散网格,因此为了获得输出图像中像素的强度,我们需要插入一些相邻样本的值(通常以相反的顺序执行,即每个输出像素,我们在输入空间中找到相应的非整数点,并在其周围插值).

这是插值算法不同的地方,通过选择邻域的大小和给予该邻域中每个点的权重系数.该关系可以是一阶或更高阶(其中涉及的变量是从逆映射的非整数样本到原始图像网格上的离散点的距离).通常,您为较近的点分配较高的权重.

看着imresize在MATLAB,这里是线性和立方内核的权重函数:

function f = triangle(x)
    % or simply: 1-abs(x) for x in [-1,1]
    f = (1+x) .* ((-1 <= x) & (x < 0)) + ...
        (1-x) .* ((0 <= x) & (x <= 1));
end

function f = cubic(x)
    absx = abs(x);
    absx2 = absx.^2;
    absx3 = absx.^3;
    f = (1.5*absx3 - 2.5*absx2 + 1) .* (absx <= 1) + ...
        (-0.5*absx3 + 2.5*absx2 - 4*absx + 2) .* ((1 < absx) & (absx <= 2));
end
Run Code Online (Sandbox Code Playgroud)

(这些基本上返回样本的插值权重,基于它与插值点的距离.)

这就是这些函数的样子:

>> subplot(121), ezplot(@triangle,[-2 2])  % triangle
>> subplot(122), ezplot(@cubic,[-3 3])     % Mexican hat
Run Code Online (Sandbox Code Playgroud)

interpolation_kernels

请注意,线性内核([-1,0]和[0,1]间隔上的分段线性函数,以及其他地方的零点)在2个相邻点上工作,而立方核(分段立方函数在区间[-2,-1],[ - 1,1]和[1,2],以及其他地方的零)适用于4个相邻点.

以下是1维情况的示意图,显示了如何使用立方内核x从离散点插值f(x_k):

1d_interpolation

内核函数以要插补的点的位置h(x)为中心x.内插值f(x)是由那些离散点处的内插函数的值缩放的离散相邻点(左侧2和右侧2)的加权和.

假设距离x和最近点之间的距离是d(0 <= d < 1),位置处的插值x将是:

f(x) = f(x1)*h(-d-1) + f(x2)*h(-d) + f(x3)*h(-d+1) + f(x4)*h(-d+2)
Run Code Online (Sandbox Code Playgroud)

点的顺序如下所示(注意x(k+1)-x(k) = 1):

x1      x2   x    x3       x4
o--------o---+----o--------o
         \___/
       distance d
Run Code Online (Sandbox Code Playgroud)

现在由于点是离散的并且以均匀的间隔采样,并且内核宽度通常很小,因此插值可以简洁地表示为卷积运算:

interp_conv_equation

该概念仅通过首先沿一个维度插值,然后使用前一步骤的结果在另一维度上进行插值,扩展到2维.

以下是双线性插值的示例,在2D中考虑4个相邻点:

bilinear_interpolation

2D中的双三次插值使用16个相邻点:

双三次

首先,我们使用16个网格样本(粉红色)沿着行(红点)进行插值.然后我们使用上一步中的插值点沿其他维度(红线)进行插值.在每个步骤中,执行常规的1D插值.在这方面,方程太长,太复杂,我不能手工锻炼!


现在,如果我们回到cubicMATLAB中的函数,它实际上将参考文献中所示的卷积核的定义与等式(4)相匹配.以下是维基百科的相同内容:

conv_kernel

您可以看到,在上面的定义中,MATLAB选择了一​​个值a=-0.5.

现在,MATLAB和OpenCV的实现之间的区别在于OpenCV选择了一个值a=-0.75.

static inline void interpolateCubic( float x, float* coeffs )
{
    const float A = -0.75f;

    coeffs[0] = ((A*(x + 1) - 5*A)*(x + 1) + 8*A)*(x + 1) - 4*A;
    coeffs[1] = ((A + 2)*x - (A + 3))*x*x + 1;
    coeffs[2] = ((A + 2)*(1 - x) - (A + 3))*(1 - x)*(1 - x) + 1;
    coeffs[3] = 1.f - coeffs[0] - coeffs[1] - coeffs[2];
}
Run Code Online (Sandbox Code Playgroud)

这可能不会立即显而易见,但代码确实计算了三次卷积函数的项(在文章中的等式(25)之后列出):

bicubic_kernel

我们可以在符号数学工具箱的帮助下验证:

A = -0.5;
syms x
c0 = ((A*(x + 1) - 5*A)*(x + 1) + 8*A)*(x + 1) - 4*A;
c1 = ((A + 2)*x - (A + 3))*x*x + 1;
c2 = ((A + 2)*(1 - x) - (A + 3))*(1 - x)*(1 - x) + 1;
c3 = 1 - c0 - c1 - c2;
Run Code Online (Sandbox Code Playgroud)

这些表达式可以改写为:

>> expand([c0;c1;c2;c3])
ans =
       - x^3/2 + x^2 - x/2
 (3*x^3)/2 - (5*x^2)/2 + 1
 - (3*x^3)/2 + 2*x^2 + x/2
             x^3/2 - x^2/2
Run Code Online (Sandbox Code Playgroud)

符合上述等式中的术语.

显然,MATLAB和OpenCV之间的区别归结为使用不同的自由项值a.根据该论文的作者,值0.5是首选,因为它意味着比其他任何选择更好的近似误差属性a.

  • @Gilad我刚刚确认将OpenCV改为`a = -0.5f`给出了与MATLAB相同的结果.我更新了[我的答案](http://stackoverflow.com/a/26812346/2778484)到你的另一个问题,但我也会在这里添加一些答案.在我的i7笔记本电脑上使用常规硬盘驱动器编译大约需要6分钟,顺便说一句.:)只需禁用CUDA! (2认同)