這是關於您的previousquestions的後續程序,關於在給定雙三次插值的情況下MATLAB中的imresize
與cv::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](https://i.stack.imgur.com/JJ6ll.png)
請注意,線性核([-1,0]和[0,1]區間上的分段線性函數和其他地方的零)適用於2鄰近點,而立方體內核(分片區間[-2,-1],[-1,1]和[1,2]上的三次函數以及別處的零)適用於4個相鄰點。
這裏是一維的情況下,示出了如何從離散點f(x_k)
使用三次內核內插值x
的圖示:
![1d_interpolation](https://i.stack.imgur.com/eHSvT.png)
核函數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)
其中點的順序描述如下(請注意,x(k+1)-x(k) = 1
):
x1 x2 x x3 x4
o--------o---+----o--------o
\___/
distance d
現在由於點是以均勻間隔離散和採樣的,並且內核寬度通常很小,所以可以制定內插簡明地爲卷積操作:
![interp_conv_equation](https://i.stack.imgur.com/Aghi4.png)
的概念延伸到2個維度簡單地通過沿着一個維度的第一內插,然後再使用先前步驟的結果跨越另一維度內插。
這裏是雙線性內插,這在2D認爲4個相鄰點的一個示例:
![bilinear_interpolation](https://i.stack.imgur.com/RBHwC.png)
在2D的雙三次插值使用16相鄰點:
![bicubic](https://i.stack.imgur.com/MmBCw.png)
首先,我們使用16個網格樣本(粉紅色)沿行(紅色點)插值。然後我們使用上一步的插值點沿着另一個維度(紅線)進行插值。在每一步中,執行常規的一維插值。在這個方程式中,對於我來說手工製作太複雜了!
現在,如果我們回去在MATLAB中cubic
功能,實際上它會匹配在reference paper顯示爲式(4)卷積核的定義。下面是從採取了同樣的事情:
![conv_kernel](https://i.stack.imgur.com/Dn6rm.png)
你可以看到,在上述定義,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](https://i.stack.imgur.com/sb53K.png)
我們可以驗證與符號數學工具箱的幫助:
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
的任何其他選擇。
+1 - 酷!所以這就是那些鑰匙從哪裏來! – rayryeng 2014-11-09 00:35:10
@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
@chappjc:+1好找。我實際上是在寫關於這個的答案:) – Amro 2014-11-09 01:57:05