首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >imresize -尝试理解双三次插值

imresize -尝试理解双三次插值
EN

Stack Overflow用户
提问于 2014-11-09 07:15:32
回答 2查看 10.7K关注 0票数 8

我在试着理解它的功能:

代码语言:javascript
复制
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

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

代码语言:javascript
复制
 h = @(x) scale * kernel(scale * x);

我的分数是0.5

内核是立方的。

但除此之外,它还意味着什么?我想这就像创建了一个稍后会调用的函数。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-11-09 07:32:16

当缩小图像大小时,imresize通过简单地扩大立方体内核而不是离散的预处理步骤来实现抗锯齿。

对于4个像素的kernel_width (重新缩放后为8个),其中contributions函数对每个像素使用10个邻居,kernelh (缩放的内核)的外观类似(未规格化,忽略x轴):

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

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

代码语言:javascript
复制
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));

PDF of the referenced paper.

相关部分是公式(15):

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

a通常设置为-0.5或-0.75。请注意,a = -0.5对应于Cubic Hermite spline,它将是连续的,并且具有连续的一阶导数。OpenCV seems to use -0.75

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

代码语言:javascript
复制
static inline void interpolateCubic( float x, float* coeffs )
{
    const float A = -0.75f;
    ...

至:

代码语言:javascript
复制
static inline void interpolateCubic( float x, float* coeffs )
{
    const float A = -0.50f;
    ...

并重建OpenCV (提示:在短编译时间内禁用CUDA和gpu模块),然后您会得到相同的结果。查看my other answer中与OP提出的相关问题相匹配的输出。

票数 13
EN

Stack Overflow用户

发布于 2014-11-09 21:37:31

这是你的previous questions的后续,关于MATLAB中的imresize和OpenCV中的cv::resize之间的区别,给出了一个双三次插值。

我自己很感兴趣,想知道为什么会有不同。这些是我的发现(因为我理解算法,如果我犯了任何错误,请纠正我)。

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

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

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

看看MATLAB中的imresize,下面是线性核和三次核的权重函数:

代码语言:javascript
复制
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

(这些参数基本上是根据样本与插值点的距离返回样本的插值权重。)

下面是这些函数的外观:

代码语言:javascript
复制
>> subplot(121), ezplot(@triangle,[-2 2])  % triangle
>> subplot(122), ezplot(@cubic,[-3 3])     % Mexican hat

请注意,线性核(在-1,0和0,1区间上的分段线性函数,在其他地方为零)作用于2个相邻的点,而立方核(在-2,-1,-1, 1,2和1,2区间上的分段三次函数,在其他地方为零)作用于4个相邻的点。

以下是一维情况的插图,显示了如何使用三次核从离散点f(x_k)插入值x

内核函数h(x)x为中心,这是要插值的点的位置。插值值f(x)是离散邻近点(左侧2个,右侧2个)的加权和,按这些离散点的插值函数的值缩放。

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

代码语言:javascript
复制
f(x) = f(x1)*h(-d-1) + f(x2)*h(-d) + f(x3)*h(-d+1) + f(x4)*h(-d+2)

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

代码语言:javascript
复制
x1      x2   x    x3       x4
o--------o---+----o--------o
         \___/
       distance d

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

该概念扩展到2维,只需先沿一个维度插值,然后使用上一步的结果跨另一个维度进行插值。

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

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

首先,我们使用16个网格样本(粉色)沿着行(红色点)进行插值。然后,我们使用上一步中的插值点沿着另一个维度(红线)进行插值。在每个步骤中,执行常规的一维插值。这里面的方程式太长太复杂了,我手算不出来!

现在如果我们回到MATLAB中的cubic函数,它实际上与reference paper中所示的卷积核的定义相匹配,如公式(4)所示。下面是取自Wikipedia的相同内容

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

现在,在MATLAB和OpenCV中实现的不同之处在于OpenCV选择了a=-0.75的值。

代码语言:javascript
复制
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];
}

这可能不是很明显,但代码确实计算了立方卷积函数的项(在本文的等式(25)后面列出):

我们可以在Symbolic Toolbox的帮助下进行验证:

代码语言:javascript
复制
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;

这些表达式可以重写为:

代码语言:javascript
复制
>> 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

它们与上面等式中的项相匹配。

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

票数 15
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26823140

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档