一文解決!opencv中的仿射變換(仿射變化的原理,使用,提升拓展的總結)

仿射變化的原理,使用及相關拓展的總結

前言

看了下原理計劃上榜的文章,沒錯,我也會寫標題了,不過本文內容無愧於題目。給大家詳細講一講opencv裏的仿射變換,也就是cv2.getAffineTransformcv2.warpAffine這兩個函數。
原本我通常會先寫原理,然後再舉個簡單的例子,之後再舉一個複雜點的拓展的例子。爲了防止大家一看原理或者數學公式這類東西就跑,我先舉個例子,大家理解了就能用,想提升的再往下看就好。

簡單的例子

這個例子看懂了,遇到圖像仿射變換的需求直接套就行。
我們來變下面這張圖:
一張圖片A
其中三個圓圈的中心點大概是,紅[316,76],黃[215,369],藍[413,371]。
那麼我們把紅點往左移,黃和藍不變。移動後,紅[215,76],黃[215,369],藍[413,371]。
代碼如下

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Mar  9 13:53:38 2020

@author: phoenix
"""

import cv2
import numpy as np

img=cv2.imread('/Users/phoenix/Documents/A.png')
rows,cols,ch=img.shape

pts1=np.float32([[316,76],[215,369],[413,371]])
pts2=np.float32([[215,76],[215,369],[413,371]])
M=cv2.getAffineTransform(pts1,pts2)

dst=cv2.warpAffine(img,M,(cols,rows))

cv2.imshow("dst",dst)
cv2.waitKey(0)

結果如下
紅點左移
再舉個例子,如果保持紅點不變,黃左移,藍右移。
那麼只要把代碼中的pts2改一下,改成

pts2=np.float32([[316,76],[115,369],[513,371]])

結果如圖
黃左移,藍右移

原理

簡單理解就是:仿射變化的關鍵在於仿射變化矩陣M
M可以通過cv2.getAffineTransform這個函數得到。
M是一個簡單的2*3的矩陣。

假設一個點A座標爲(x1,y1),經過仿射變換矩陣M,在另一個圖像上爲A1點座標爲(x2,y2)。剛剛有說過M是一個2*3的矩陣,這裏設
M=[abcdef] M= \begin{gathered} \begin{bmatrix} a & b &c \\ d & e & f \end{bmatrix} \end{gathered}
那麼
[x2y2]=[abde][x1y1]+[cf] \begin{gathered} \begin{bmatrix} x2 \\ y2 \end{bmatrix}= \begin{bmatrix} a & b\\ d & e\end{bmatrix} \begin{bmatrix} x1 \\ y1 \end{bmatrix}+ \begin{bmatrix} c \\ f \end{bmatrix} \end{gathered}
也就是說
x2=a * x1 + b * y1 + c
y2=d * x1 + e * y1 + f

如果想做更深入的瞭解的話,可以看這個網址。
仿射變換
上面👆這個網址介紹的很詳細

提升拓展

cv2.getAffineTransform這個函數沒什麼好說的,是單純用來求仿射變換矩陣M
下面說說cv2.getAffineTransform這個函數
先看下它的help。

Help on built-in function warpAffine:

warpAffine(...)
    warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) -> dst
    .   @brief Applies an affine transformation to an image.
    .   
    .   The function warpAffine transforms the source image using the specified matrix:
    .   
    .   \f[\texttt{dst} (x,y) =  \texttt{src} ( \texttt{M} _{11} x +  \texttt{M} _{12} y +  \texttt{M} _{13}, \texttt{M} _{21} x +  \texttt{M} _{22} y +  \texttt{M} _{23})\f]
    .   
    .   when the flag #WARP_INVERSE_MAP is set. Otherwise, the transformation is first inverted
    .   with #invertAffineTransform and then put in the formula above instead of M. The function cannot
    .   operate in-place.
    .   
    .   @param src input image.
    .   @param dst output image that has the size dsize and the same type as src .
    .   @param M \f$2\times 3\f$ transformation matrix.
    .   @param dsize size of the output image.
    .   @param flags combination of interpolation methods (see #InterpolationFlags) and the optional
    .   flag #WARP_INVERSE_MAP that means that M is the inverse transformation (
    .   \f$\texttt{dst}\rightarrow\texttt{src}\f$ ).
    .   @param borderMode pixel extrapolation method (see #BorderTypes); when
    .   borderMode=#BORDER_TRANSPARENT, it means that the pixels in the destination image corresponding to
    .   the "outliers" in the source image are not modified by the function.
    .   @param borderValue value used in case of a constant border; by default, it is 0.
    .   
    .   @sa  warpPerspective, resize, remap, getRectSubPix, transform

總結下就是
1.這個方程是用來做圖像仿射變化的,且是通過指定的矩陣來進行轉換。
2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) -> dst
其中
src:輸入圖像。
dst:輸出圖像,其大小爲dsize,並且與src類型相同。
M: 轉換矩陣。
dsize:輸出圖像的大小。
flags:插值方法(請參閱#InterpolationFlags)和可選方法的組合
borderMode:像素外推方法(不過看別的博客上寫的邊界像素模式?)(請參閱#BorderTypes);當borderMode =#BORDER_TRANSPARENT,表示目標圖像中的像素對應於源圖像中的“異常值”不會被該功能修改。(這裏我覺得我的翻譯沒有問題,但我實在不知道他想表達什麼
borderValue:邊界不變時使用的值;默認情況下爲0。也就是黑色。

src,dst,M,dsize:輸入圖像,輸出圖像,轉換矩陣,輸出圖像的大小。這四個比較基礎,沒什麼好說的。
後面的**flags,borderMode,borderValue,這個三個變量很容易被大家忽略,其實還是非常非常有用**的。可以區別你對這個函數的理解深度。

flags:插值方法

flages表示插值方式,默認爲flags=cv2.INTER_LINEAR(雙線性插值)。
做個彙總給大家

INTER_NEAREST-最近鄰插值
INTER_LINEAR-雙線性插值(默認使用)
INTER_AREA-使用像素面積關係進行重採樣。 這可能是首選的圖像抽取方法,因爲它可以提供無波紋的結果。 但是當圖像放大時,它類似於INTER_NEAREST方法。
INTER_CUBIC-在4x4像素鄰域上的雙三次插值
INTER_LANCZOS4-在8x8像素鄰域上的Lanczos插值

這麼多插值方法怎麼選擇,我給大家推薦這篇博客OpenCV圖像插值方法的比較

如果你看不太懂那篇博客。那麼我給個不負責任的個人建議
不在意圖片質量:使用默認的就OK。
在意圖片質量不在意代碼的運行時間:使用最後一個。
既在意質量又在意時間:看到前面寫的原理了嗎,你可以利用矩陣反推回去,因爲原圖的質量是最高的

borderMode:像素外推方法(邊界像素模式)

這個推薦大家看這個網站
BorderTypes
這是google翻譯後的網頁截圖。
bordertypes
簡單些解釋呢,borderType是指要添加在圖像周圍的某種類型的邊界,就像在圖像周圍創造一個邊框(給你的圖像加個相框的感覺)。
borderMode和borderValue加在一起,默認就是黑的。如果不理解往下看。

borderValue:邊界不變時使用的值

這裏的邊界不變指的是borderMode=cv2.BORDER_CONSTANT。
舉個例子這裏有一張圖片
abcdefgh
當我用

dst=cv2.warpAffine(img,M,(cols,rows))

,borderMode和borderValue不選擇,即爲默認時。
仿射變化的結果是
abcdefgh仿射變化結果
記住這張圖
接着我用

dst=cv2.warpAffine(img,M,(cols,rows),borderMode=cv2.BORDER_CONSTANT ,borderValue=(0,255,255))

或者

dst=cv2.warpAffine(img,M,(cols,rows) ,borderValue=(0,255,255))

結果是
被黃色填滿
沒錯原本黑色的地方被黃色(0,255,255)填滿了。
而我如果更改borderMode,我使用

dst=cv2.warpAffine(img,M,(cols,rows),borderMode=cv2.BORDER_REFLECT,borderValue=(0,255,255))

結果是如圖,右邊填充的是h的鏡像。
BORDER_REFLECT
cv2.BORDER_REFLECT的效果就是fedcba|abcdefgh|hgfedcb
此時borderValue的值也就沒什麼用了。

結尾

這篇文章花了我好長時間啊,翻了很多資料和博客,力求做到詳細正確。如果你看到了這裏,麻煩給我點了贊且留言支持一下。如果你還有關於仿射變換的問題沒有解決,麻煩發在評論裏,我會回覆幫你解決的。謝謝各位!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章