2014-11-08 45 views
5

我想了解功能:imresize - 試圖瞭解雙三次插值

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 

我真的不明白這是什麼行表示

h = @(x) scale * kernel(scale * x); 

我的規模爲0.5
內核是立方體。

但除此之外它是什麼意思? 我認爲這就像創建一個稍後會被調用的函數?

回答

10

imresize通過簡單地擴大立方體內核而不是離散的預處理步驟來縮小圖像尺寸時實現了抗鋸齒。

對於kernel_width 4的像素(8重新縮放後),其中contributions功能利用10點的鄰居的每個像素中,kernel VS h(縮放內核)看起來像(非標準化,忽略x軸):

enter image description here

這比在單獨的預處理步驟首先進行低通濾波器或高斯卷積更容易。

立方內核是在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)); 

PDF of the referenced paper.

相關部分是方程(15):

enter image description here

這是一個特定a = -0.5的通用插值公式的版本:

enter image description here

a通常被設定爲-0.5,或-0.75。請注意,a = -0.5對應於Cubic Hermite spline,它將是連續的並且具有連續的第一導數。 OpenCV seems to use -0.75

然而,如果編輯[OPENCV_SRC] \模塊\ imgproc \ SRC \ imgwarp.cpp和更改代碼:

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

到:

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

和重建的OpenCV(尖端:禁用CUDA和gpu模塊編譯時間短),那麼你會得到相同的結果。請參閱my other answer中匹配的輸出到OP的相關問題。

+1

+1 - 酷!所以這就是那些鑰匙從哪裏來! – rayryeng 2014-11-09 00:35:10

+2

@Gilad我記得你正在研究MATLAB與OpenCV的三次插值,看起來這個差別對於MATLAB來說是a = -0.5,對於OpenCV來說是a = -0.75(https://github.com/Itseez/opencv/斑點/主/模塊/ imgproc/SRC/imgwarp.cpp#L155)。 – chappjc 2014-11-09 01:49:58

+2

@chappjc:+1好找。我實際上是在寫關於這個的答案:) – Amro 2014-11-09 01:57:05

9

這是關於您的previousquestions的後續程序,關於在給定雙三次插值的情況下MATLAB中的imresizecv::resize之間的區別。

我感興趣的是自己找出爲什麼有差異。這些是我的發現(因爲我理解算法,如果我犯了錯誤,請糾正我)。


思考調整圖像大小作爲從大小M-by-N的輸入圖像大小scaledM-by-scaledN的輸出圖像的平面變換。

問題在於點不一定適合離散網格,因此爲了獲得輸出圖像中像素的強度,我們需要內插一些相鄰樣本的值(通常以相反的順序執行,即針對每個輸出像素,我們在輸入空間中找到相應的非整數點,並在其周圍插值)。

這是插值算法不同的地方,通過選擇鄰域的大小和賦予該鄰域中每個點的權重係數。該關係可以是一階或更高階(其中涉及的變量是從反向映射的非整數樣本到原始圖像網格上的離散點的距離)。通常,您將更高的權重分配給更近的點。

尋找在MATLAB imresize,這裏有用於線性和立方內核權值功能:

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 

(這些基本上返回基於它是從內插點多遠的樣品的內插權重。 )

這是這些功能是如何模樣:

>> subplot(121), ezplot(@triangle,[-2 2]) % triangle 
>> subplot(122), ezplot(@cubic,[-3 3])  % Mexican hat 

interpolation_kernels

請注意,線性核([-1,0]和[0,1]區間上的分段線性函數和其他地方的零)適用於2鄰近點,而立方體內核(分片區間[-2,-1],[-1,1]和[1,2]上的三次函數以及別處的零)適用於4個相鄰點。

這裏是一維的情況下,示出了如何從離散點f(x_k)使用三次內核內插值x的圖示:

1d_interpolation

核函數h(x)x表示居中要插補點的位置。插值f(x)是在這些離散點上由插值函數的值縮放的離散相鄰點(向左2和向右2)的加權和。

說,如果x和最近點之間的距離爲d0 <= d < 1),在位置x插值值將是:

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):

x1  x2 x x3  x4 
o--------o---+----o--------o 
     \___/ 
     distance d 

現在由於點是以均勻間隔離散和採樣的,並且內核寬度通常很小,所以可以制定內插簡明地爲卷積操作:

interp_conv_equation

的概念延伸到2個維度簡單地通過沿着一個維度的第一內插,然後再使用先前步驟的結果跨越另一維度內插。

這裏是雙線性內插,這在2D認爲4個相鄰點的一個示例:

bilinear_interpolation

在2D的雙三次插值使用16相鄰點:

bicubic

首先,我們使用16個網格樣本(粉紅色)沿行(紅色點)插值。然後我們使用上一步的插值點沿着另一個維度(紅線)進行插值。在每一步中,執行常規的一維插值。在這個方程式中,對於我來說手工製作太複雜了!


現在,如果我們回去在MATLAB中cubic功能,實際上它會匹配在reference paper顯示爲式(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]; 
} 

這可能不是很明顯向右走,但代碼不計算三次卷積功能的條款(列在紙上式(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; 

這些表達式可以改寫爲:

>> 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的任何其他選擇。

+0

優秀的答案。感謝您驗證方程式與符號數學工具箱的匹配。非常值得努力! – chappjc 2014-11-09 16:58:14

+1

@chappjc:謝謝。太糟糕了,參數'a'的值在兩個實現中都是硬編碼。如果我們希望它們匹配,在MATLAB中你必須修改內建的'imresize'函數(我從來不喜歡這樣做),而在OpenCV中,你必須從源代碼編譯整個東西只是爲了翻轉齒值!看看OP是否確實將OpenCV中的'a'改爲'-0.5',並驗證我們在兩個實現之間得到了相同的結果。上次我嘗試了它,我記得需要10分鐘來編譯OpenCV刮。 – Amro 2014-11-10 06:13:44

+0

@Amro OMG非常感謝你!我需要2天時間來閱讀您的整個發現! – Gilad 2014-11-10 07:27:35