1 圖像翻轉(圖像鏡像旋轉)
在OpenCV中,圖片的鏡像旋轉是以圖像的中心為原點進行鏡像翻轉的。
cv2.flip(img,flipcode)
參數
img: 要翻轉的圖像
flipcode: 指定翻轉類型的標志
????????flipcode=0: 垂直翻轉,圖片像素點沿x軸翻轉
????????flipcode>0: 水平翻轉,圖片像素點沿y軸翻轉
????????flipcode<0: 水平垂直翻轉,水平翻轉和垂直翻轉的結合
參數是1,表示水平翻轉
參數是0,表示垂直翻轉
參數是-1,表示水平垂直翻轉
2 圖像仿射變換
仿射變換(Affine Transformation)是一種線性變換,保持了點之間的相對距離不變。
仿射變換的基本性質
????????保持直線
????????保持平行
????????比例不變性
????????不保持角度和長度
常見的仿射變換類型
????????旋轉:繞著某個點或軸旋轉一定角度。
????????平移:僅改變物體的位置,不改變其形狀和大小。
????????縮放:改變物體的大小。
????????剪切:使物體發生傾斜變形。
仿射變換的基本原理
????????線性變換
????????二維空間中,圖像點坐標為(x,y),仿射變換的目標是將這些點映射到新的位置 (x', y')。
????????為了實現這種映射,通常會使用一個矩陣乘法的形式:
(類似于y=kx+b)
a,b,c,d 是線性變換部分的系數,控制旋轉、縮放和剪切。
t_x,t_y 是平移部分的系數,控制圖像在平面上的移動。
輸入點的坐標被擴展為齊次坐標形式[x,y,1],以便能夠同時處理線性變換和平移
cv2.warpAffine()函數
????????仿射變換函數
????????cv2.warpAffine(img,M,dsize)
????????img:輸入圖像。
????????M:2x3的變換矩陣,類型為np.float32
。
????????dsize:輸出圖像的尺寸,形式為(width,height)
。
2.1 圖像旋轉
旋轉圖像可以將圖像繞著某個點旋轉一定的角度。
cv2.getRotationMatrix2D()函數
獲取旋轉矩陣
????????cv2.getRotationMatrix2D(center,angle,scale)
????????center:旋轉中心點的坐標,格式為(x,y)
。
????????angle:旋轉角度,單位為度,正值表示逆時針旋轉負值表示順時針旋轉。
????????scale:縮放比例,若設為1,則不縮放。
????????返回值:M,2x3的旋轉矩陣。
?
原圖
旋轉后圖像
2.2 圖像平移
移操作可以將圖像中的每個點沿著某個方向移動一定的距離。
假設我們有一個點 P(x,y),希望將其沿x軸方向平移t_x*個單位,沿y軸方向平移t_y個單位到新的位置P′(x′,y′),那么平移公式如下:
x′=x+tx
y′=y+ty
?
對于圖像的平移,矩陣的前兩列
[a, b]
和[d, e]
通常設置為[1, 0]
和[0, 1]
,表示沒有旋轉或縮放。第三列[c, f]
則定義了平移的方向和距離:
c
是 x 軸方向的平移距離(水平方向)。
f
是 y 軸方向的平移距離(垂直方向)。
M = np.float32([[1,0,100],[0,1,50]])
是一個平移矩陣,它的作用是將圖像向右平移 100 像素,向下平移 50 像素。通過cv.warpAffine
函數,這個矩陣被應用到圖像上,生成平移后的結果。
2.3 圖像縮放
縮放操作可以改變圖片的大小。
假設要把圖像的寬高分別縮放為0.5和0.8,那么對應的縮放因子sx=0.5,sy=0.8。
點P(x,y)對應到新的位置P'(x',y'),縮放公式為:
x′=s_x*x
y′=s_y*y
對于圖像的縮放,矩陣的前兩列
[a, b]
和[d, e]
通常設置為[s_x, 0]
和[0, s_y]
,其中:
s_x
是 x 軸方向的縮放因子(水平方向)。
s_y
是 y 軸方向的縮放因子(垂直方向)。
M = np.float32([[0.5,0,0],[0,0.8,0]])
是一個縮放矩陣,它的作用是將圖像在 x 軸方向縮小為原來的 50%,在 y 軸方向縮小為原來的 80%。通過cv.warpAffine
函數,這個矩陣被應用到圖像上,生成縮放后的結果。
2.4 圖像剪切
剪切操作可以改變圖形的形狀,以便其在某個方向上傾斜,它將對象的形狀改變為斜邊平行四邊形,而不改變其面積。
想象我們手上有一張矩形紙片,如果你固定紙片的一邊,并沿著另一邊施加一個平行于該邊的力,這張紙片就會變形為一個平行四邊形。這就是剪切變換的一個直觀解釋。
對于二維空間中的點P(x,y),對他進行剪切變換:
沿x軸剪切:x'=x+sh_y*y y'=y
沿y軸剪切:x'=x y'=sh_x*x+y
當需要同時沿兩個方向進行剪切時,x'=x+sh_y*y , y'=sh_x*x+y
x方向剪切
M_x_shear = np.float32([[1, sh_y, 0],
? ? ? ? ? ? ? ? ? ? ? ? [0, 1, 0]])
沿x軸剪切:
新的 x 坐標:
x' = x + sh_y * y
新的 y 坐標:
y' = y
y方向剪切
M_y_shear = np.float32([[1, 0, 0],
? ? ? ? ? ? ? ? ? ? ? ? [sh_x, 1, 0]])
沿y軸剪切:
新的 x 坐標:
x' = x
新的 y 坐標:
y' = sh_x * x + y
xy方向剪切
M_xy_shear = np.float32([[1, sh_xy, 0],
? ? ? ? ? ? ? ? ? ? ? ? ?[sh_xy, 1, 0]])
同時沿x和y軸剪切:
新的 x 坐標:
x' = x + sh_xy * y
新的 y 坐標:
y' = sh_xy * x + y
3 插值方法
?
在圖像處理和計算機圖形學中,插值(Interpolation)是一種通過已知數據點之間的推斷或估計來獲取新數據點的方法。它在圖像處理中常用于處理圖像的放大、縮小、旋轉、變形等操作,以及處理圖像中的像素值。
圖像插值算法是為了解決圖像縮放或者旋轉等操作時,由于像素之間的間隔不一致而導致的信息丟失和圖像質量下降的問題。當我們對圖像進行縮放或旋轉等操作時,需要在新的像素位置上計算出對應的像素值,而插值算法的作用就是根據已知的像素值來推測未知位置的像素值。
3.1 最近鄰插值
CV2.INTER_NEAREST
new_img1=cv.warpAffine(img,M,(w,h),flags=cv.INTER_NEAREST)
首先給出目標點與原圖像點之間坐標的計算公式:
dstX:目標圖像中某點的x坐標,
dstY:目標圖像中某點的y坐標,
srcWidth:原圖的寬度,
dstWidth:目標圖像的寬度;
srcHeight:原圖的高度,
dstHeight:目標圖像的高度。
而srcX和srcY:目標圖像中的某點對應的原圖中的點的x和y的坐標。
通俗的講,該公式就是讓目標圖像中的每個像素值都能找到對應的原圖中的像素值,這樣才能根據不同的插值方法來獲取新的像素值。根據該公式,我們就可以得到每一個目標點所對應的原圖像的點,比如一個2*2的圖像放大到4*4,如下圖所示,其中紅色的為每個像素點的坐標,黑色的則表示該像素點的像素值。
那么根據公式我們就可以計算出放大后的圖像(0,0)點對應的原圖像中的坐標為:
也就是原圖中的(0,0)點,而最近鄰插值的原則是:目標像素點的像素值與經過該公式計算出來的對應的像素點的像素值相同,如出現小數部分需要進行取整。那么放大后圖像的(0,0)坐標處的像素值就是原圖像中(0,0)坐標處的像素值,也就是10。接下來就是計算放大后圖像(1,0)點對應的原圖像的坐標,還是帶入公式:
也就是原圖中的(0.5,0)點,因此需要對計算出來的坐標值進行取整,取整后的結果為(0,0),也就是說放大后的圖像中的(1,0)坐標處對應的像素值就是原圖中(0,0)坐標處的像素值,其他像素點計算規則與此相同。
思考:
一張圖的第一行像素點的值分別為: 10 10 20 30 40 放大一倍后 新圖像的第一行的第3個像素點的值是多少?
點坐標:(2,0)
(1,0)==>10
4x2的圖像放大到8x4,放大一倍
3.2 雙線性插值
CV2.INTER_LINEAR
雙線性插值是一種圖像縮放、旋轉或平移時進行像素值估計的插值方法。當需要對圖像進行變換時,特別是尺寸變化時,原始圖像的某些像素坐標可能不再是新圖像中的整數位置,這時就需要使用插值算法來確定這些非整數坐標的像素值。
雙線性插值的工作原理是這樣的:
-
假設要查找目標圖像上坐標為
(x', y')
的像素值,在原圖像上對應的浮點坐標為(x, y)
。 -
在原圖像上找到四個最接近
(x, y)
的像素點,通常記作P00(x0, y0)
,P01(x0, y1)
,P10(x1, y0)
,P11(x1, y1)
,它們構成一個2x2的鄰域矩陣。 -
分別在水平方向和垂直方向上做線性插值:
水平方向:根據 x
與 x0
和 x1
的關系計算出 P00
和 P10
、 P01
和 P11
之間的插值結果。
垂直方向:將第一步的結果與 y
與 y0
和 y1
的關系結合,再在垂直方向上做一次線性插值。
-
綜合上述兩次線性插值的結果,得到最終位于
(x', y')
處的新像素的估計值。
總結: 4乘4的圖像 變成6乘6的圖像 那么目標圖像的(3,3)點的像素是原圖中(1.8333,1.8333)的像素顏色,但是坐標必須是整數 它周圍有四個像素點 該取誰呢? 按照到各自的距離比例 來分配顏色值
首先要了解線性插值,而雙線性插值本質上就是在兩個方向上做線性插值。還是給出目標點與原圖像中點的計算公式
比如我們根據上述公式計算出了新圖像中的某點所對應的原圖像的點P,其周圍的點分別為Q12、Q22、Q11、Q21, 要插值的P點不在其周圍點的連線上,這時候就需要用到雙線性插值了。首先延申P點得到P和Q11、Q21的交點R1與P和Q12、Q22的交點R2,如下圖所示:
然后根據Q11、Q21得到R1的插值,根據Q12、Q22得到R2的插值,然后根據R1、R2得到P的插值即可,這就是雙線性插值。以下是計算過程:
首先計算R1和R2的插值:
然后根據R1和R2計算P的插值:
這樣就得到了P點的插值。注意此處如果先在y方向插值、再在x方向插值,其結果與按照上述順序雙線性插值的結果是一樣的。
雙線性插值的對應關系看似比較清晰,但還是有2個問題。首先是根據坐標系的不同,產生的結果不同,這張圖是左上角為坐標系原點的情況,我們可以發現最左邊x=0的點都會有概率直接復制到目標圖像中(至少原點肯定是這樣),而且就算不和原圖像中的點重合,也相當于進行了1次單線性插值(帶入到權重公式中會發現結果)。
下面這張圖是右上角為坐標系原點的情況,我們可以發現最右面的點都會有概率直接復制到目標圖像中(至少原點肯定是這樣),而且就算不和原圖像中的點重合,也相當于進行了1次單線性插值。那么當我們采用不同的坐標系時產生的結果是不一樣的,而且無論我們采用什么坐標系,最左側和最右側(最上側和最下側)的點是“不公平的”,這是第一個問題。
第二個問題時整體的圖像相對位置會發生變化。如下圖所示,左側是原圖像(3,3),右側是目標圖像(5,5),原圖像的幾何中心點是(1,1),目標圖像的幾何中心點是(2,2),根據對應關系,目標圖像的幾何中心點對應的原圖像的位置是(1.2,1.2),那么問題來了,目標圖像的原點(0,0)和原始圖像的原點是重合的,但是目標圖像的幾何中心點相對于原始圖像的幾何中心點偏右下,那么整體圖像的位置會發生偏移,所以參與計算的點相對都往右下偏移會產生相對的位置信息損失。這是第二個問題。
因此,在OpenCV中,為了解決這兩個問題,將公式進行了優化,如下所示:
使用該公式計算出原圖中的對應坐標后再進行插值計算,就不會出現上面的情況了。
數學公式
假設目標點 (x,y) 的四個相鄰像素點為 (x1?,y1?)、(x2?,y1?)、(x1?,y2?) 和 (x2?,y2?),它們的像素值分別為 f(x1?,y1?)、f(x2?,y1?)、f(x1?,y2?) 和 f(x2?,y2?)。
-
水平方向插值:
-
垂直方向插值:
代碼
3.3 像素區域插值
cv2.INTER_AREA
像素區域插值主要分兩種情況,縮小圖像和放大圖像的工作原理并不相同。
當使用像素區域插值方法進行縮小圖像時,它就會變成一個均值濾波器(濾波器其實就是一個核,這里只做簡單了解,后面實驗中會介紹),其工作原理可以理解為對一個區域內的像素值取平均值。
當使用像素區域插值方法進行放大圖像時
如果圖像放大的比例是整數倍,那么其工作原理與最近鄰插值類似;
如果放大的比例不是整數倍,那么就會調用雙線性插值進行放大。
其中目標像素點與原圖像的像素點的對應公式如下所示:
3.4 雙三次插值
cv2.INTER_CUBIC
雙三次插值是一種高級的圖像插值方法,它通過考慮目標像素點周圍的 16 個鄰近像素點,并使用三次多項式進行擬合,來預測目標像素點的值。這種方法能夠生成更高質量的圖像,尤其是在放大具有復雜紋理或精細細節的圖像時,效果尤為明顯。
原理
雙三次插值的數學模型基于三次樣條插值,其函數形式為:
BiCubic基函數也就是雙三次插值的權重函數,它決定了如何根據距離對周圍像素進行加權平均。
假如下圖中的P點就是目標圖像B在(X,Y)處根據上述公式計算出的對應于原圖像A中的位置,P的坐標位置會出現小數部分,所以我們假設P點的坐標為(x+u,y+v),其中x、y表示整數部分,u、v表示小數部分,那么我們就可以得到其周圍的最近的16個像素的位置,我們用a(i,j)(i,j=0,1,2,3)來表示,如下圖所示。
然后給出BiCubic函數:
a
一般取-0.5或-0.75,用于控制插值函數的形狀。
d
代表的是目標像素點與某個像素點之間的相對距離,d_h、d_w
我們要做的就是將上面的16個點相較于p點的位置距離算出來,獲取16像素所對應的權重W(d)。然而BiCubic函數是一維的,所以我們需要將像素點的行與列分開計算,比如a00這個點,我們需要將d_x帶入BiCubic函數中,計算a00點對于P點的x方向的權重,然后將d_y帶入BiCubic函數中,計算a00點對于P點的y方向的權重,其他像素點也是這樣的計算過程,最終我們就可以得到P所對應的目標圖像B在(X,Y)處的像素值為:
依此辦法我們就可以得到目標圖像中所有的像素點的像素值。
剛剛我們說拿到了目標點的坐標為 (x+u,y+v) ,其中 x、y 表示整數部分,u、v表示小數部分。那么我們取坐標的整數部分作為參考點,也就是(x,y),小數部分表示目標像素相對于參考點的偏移量。
比如說目標點的坐標為(3.7,2.4),那么我們的參考點就是(3,2)。
目標部分相對于參考點的偏移量:
然后我們才需要來計算相鄰像素和目標像素的距離:
比如我們的P點在行上的 四個像素(ii=-1,0,1,2),對應的距離就是:d_h=|ii-b_h|
ii=-1時: d_h=|-1-0.7|=1.7 w_i=-0.0315
ii=0時: d_h=|0-0.7|=0.7 w_i=0.2895
ii=1時: d_h=|1-0.7|=0.3 w_i=0.8155
ii=-1時: d_h=|2-0.7|=1.3 w_i=-0.0735
在列上的 四個像素(jj=-1,0,1,2),對應的距離就是:d_w=|ii-b_w|
jj=-1時: d_w=|-1-0.4|=1.4 w_j=-0.072
jj=0時: d_w=|0-0.4|=0.4 w_j=0.696
jj=1時: d_w=|1-0.4|=0.6 w_j=0.424
jj=-1時: d_w=|2-0.4|=1.6 w_j=-0.048
。
在 OpenCV 中的實現
總結
雙三次插值是一種高質量的圖像插值方法,適用于需要高分辨率和精細細節的圖像處理場景。盡管計算復雜度較高,但其生成的圖像質量通常優于雙線性插值。
3.5 Lanczos插值
cv2.INTER_LANCZOS4
Lanczos插值方法與雙三次插值的思想是一樣的,不同的就是其需要的原圖像周圍的像素點的范圍變成了8*8,并且不再使用BiCubic函數來計算權重,而是換了一個公式計算權重。
首先還是目標像素點與原圖像的像素點的對應公式如下所示:
下面我們舉例說明,假設原圖像A大小為m*n,縮放后的目標圖像B的大小為M*N。其中A的每一個像素點是已知的,B是未知的,我們想要求出目標圖像B中每一個像素點(X,Y)的值,必須先找出像素(X,Y)在原圖像A中對應的像素(x,y),再根據原圖像A距離像素(x,y)最近的64個像素點作為計算目標圖像B(X,Y)處像素值的參數,利用權重函數求出64個像素點的權重,圖B像素(x,y)的值就等于64個像素點的加權疊加。
假如下圖中的P點就是目標圖像B在(X,Y)處根據上述公式計算出的對應于原圖像A中的位置,P的坐標位置會出現小數部分,所以我們假設P點的坐標為(x+u,y+v),其中x、y表示整數部分,u、v表示小數部分,那么我們就可以得到其周圍的最近的64個像素的位置,我們用a(i,j)(i,j=0,1,2,3,4,5,6,7)來表示,如下圖所示。
然后給出權重公式:
其中a通常取2或者3,當a=2時,該算法適用于圖像縮小。a=3時,該算法適用于圖像放大。
與雙三次插值一樣,這里也需要將像素點分行和列分別帶入計算權重值,其他像素點也是這樣的計算過程,最終我們就可以得到P所對應的目標圖像B在(X,Y)處的像素值為:
其中[x]、[y]表示對坐標值向下取整,通過該方法就可以計算出新的圖像中所有的像素點的像素值。
3.6 小結
最近鄰插值的計算速度最快,但是可能會導致圖像出現鋸齒狀邊緣和失真,效果較差。雙線性插值的計算速度慢一點,但效果有了大幅度的提高,適用于大多數場景。雙三次插值、Lanczos插值的計算速度都很慢,但是效果都很好。
在OpenCV中,關于插值方法默認選擇的都是雙線性插值,且一般情況下雙線性插值已經能滿足大部分需求。
?