參考:https://blog.csdn.net/keith_bb/article/details/56331356
圖片位移
可手寫代碼實現矩陣搬移操作。
也可使用仿射變換方法進行操作:
例如,仿射矩陣可寫爲:
[[1 0 100]
[0 1 200]] ,即表示x位移100,y位移200像素點。
圖片縮放
1、放大、縮小,使用cv.resize(src,(new,newH))
2、常用的插值算法如下:
i.最近鄰域插值:選最近的點複製即可。
ii.雙線性插值:
首先計算A1、A2、B1、B2 。然後計算目標點(可根據A1 、A2計算,也可根據B1、B2計算)
圖片仿射
1、圖片仿射變換(位移+旋轉+縮放):
仿射變換是指在向量空間中進行一次線性變換(乘以一個矩陣)並加上一個平移(加上一個向量),變換爲另一個向量空間的過程。在有限維的情況下,每個仿射變換可以由一個矩陣A和一個向量b給出,它可以寫作A和一個附加的列b。一個仿射變換對應於一個矩陣和一個向量的乘法,而仿射變換的複合對應於普通的矩陣乘法,只要加入一個額外的行到矩陣的底下,這一行全部是0除了最右邊是一個1,而列向量的底下要加上一個1.Affine Transform描述了一種二維仿射變換的功能,它是一種二維座標之間的線性變換,保持二維圖形的“平直性”(即變換後直線還是直線,圓弧還是圓弧)和“平行性”(其實是保持二維圖形間的相對位置關係不變,平行線還是平行線,而直線上的點位置順序不變,另特別注意向量間夾角可能會發生變化)。仿射變換可以通過一系列的原子變換的複合來實現包括:平移(Translation)、縮放(Scale)、翻轉(Flip)、旋轉(Rotation)和錯切(Shear). 事實上,仿射變換代表的是兩幅圖之間的關係,我們通常使用2x3矩陣來表示仿射變換如下:
得到如下結果:
2.仿射變換求法
從上面解釋中我們得知仿射變換表示的就是兩幅圖片的一種聯繫,關於這種聯繫的信息大致可以從以下兩種場景獲得。
a. 我們已知X和T而且我們知道他們是有聯繫的,接下來的工作就是求解矩陣M
b. 我們一致M和X要求得T,我們只需要應用算式T=M·X即可。對於這種聯繫的信息可以用矩陣M清晰的表達(即給出明確的2x3矩陣)或者也可以用兩幅圖片點之間幾何關係來表達。
因爲矩陣M聯繫着兩幅圖片,我們以其表示兩圖中各三點直接的聯繫爲例,如下:
點1,2和3(在圖一中形成一個三角)與圖二中三個點一一映射,仍然形成三角形,但形狀已經大大改變。如果我們能通過這樣兩組三點求出仿射變換(你能選擇自己喜歡的點),接下來我們就能把仿射變換應用到圖像中所有的點。
openCV中有直接根據三個邊界點計算轉換矩陣的函數:
M = cv.getAffineTransform(matSrc_3Point,matDst_3Point)
3.opencv實現仿射變換
利用opencv實現仿射變換一般會涉及到warpAffine和getRotationMatrix2D兩個函數,其中warpAffine可以實現一些簡單的重映射,而getRotationMatrix2D可以獲得旋轉矩陣M。
getRotationMatrix2D函數
Matcv::getRotationMatrix2D ( Point2f center,
double angle,
double scale
)
參數解釋
. center:Point2f類型,表示原圖像的旋轉中心
. angle: double類型,表示圖像旋轉角度,角度爲正則表示逆時針旋轉,角度爲負表示逆時針旋轉(座標原點是圖像左上角)
. scale: 縮放係數
函數計算如下矩陣:
其中,
warpAffine函數
void cv::warpAffine ( InputArray src,
OutputArray dst,
InputArray M,
Size dsize,
int flags =INTER_LINEAR,
int borderMode =BORDER_CONSTANT,
const Scalar & borderValue = Scalar()
)
參數解釋
. src: 輸入圖像
. dst: 輸出圖像,尺寸由dsize指定,圖像類型與原圖像一致
. M: 2X3的變換矩陣
. dsize: 指定圖像輸出尺寸
. flags: 插值算法標識符,有默認值INTER_LINEAR,如果插值算法爲WARP_INVERSE_MAP, warpAffine函數使用如下矩陣進行圖像轉換
示例代碼
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
//全局變量
String src_windowName = "原圖像";
String warp_windowName = "仿射變換";
String warp_rotate_windowName = "仿射旋轉變換";
String rotate_windowName = "圖像旋轉";
int main()
{
Point2f srcTri[3];
Point2f dstTri[3];
Mat rot_mat(2, 3, CV_32FC1);
Mat warp_mat(2, 3, CV_32FC1);
Mat srcImage, warp_dstImage, warp_rotate_dstImage, rotate_dstImage;
//加載圖像
srcImage = imread("D://timg.jpg");
//判斷文件是否加載成功
if (srcImage.empty())
{
cout << "圖像加載失敗!" << endl;
return -1;
}
else
cout << "圖像加載成功!" << endl << endl;
//創建仿射變換目標圖像與原圖像尺寸類型相同
warp_dstImage = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
//設置三個點來計算仿射變換
srcTri[0] = Point2f(0, 0);
srcTri[1] = Point2f(srcImage.cols - 1, 0);
srcTri[2] = Point2f(0, srcImage.rows - 1);
dstTri[0] = Point2f(srcImage.cols*0.0, srcImage.rows*0.33);
dstTri[1] = Point2f(srcImage.cols*0.85, srcImage.rows*0.25);
dstTri[2] = Point2f(srcImage.cols*0.15, srcImage.rows*0.7);
//計算仿射變換矩陣
warp_mat = getAffineTransform(srcTri, dstTri);
//對加載圖形進行仿射變換操作
warpAffine(srcImage, warp_dstImage, warp_mat, warp_dstImage.size());
//計算圖像中點順時針旋轉50度,縮放因子爲0.6的旋轉矩陣
Point center = Point(warp_dstImage.cols / 2, warp_dstImage.rows / 2);
double angle = -50.0;
double scale = 0.6;
//計算旋轉矩陣
rot_mat = getRotationMatrix2D(center, angle, scale);
//旋轉已扭曲圖像
warpAffine(warp_dstImage, warp_rotate_dstImage, rot_mat, warp_dstImage.size());
//將原圖像旋轉
warpAffine(srcImage, rotate_dstImage, rot_mat, srcImage.size());
//保存結果圖像
cv::imwrite("D://仿射後圖像.jpg", warp_dstImage);
cv::imwrite("D://仿射後旋轉圖像.jpg", warp_rotate_dstImage);
cv::imwrite("D://原圖像旋轉圖像.jpg", rotate_dstImage);
//顯示變換結果
namedWindow(src_windowName, WINDOW_AUTOSIZE);
imshow(src_windowName, srcImage);
namedWindow(warp_windowName, WINDOW_AUTOSIZE);
imshow(warp_windowName, warp_dstImage);
namedWindow(warp_rotate_windowName, WINDOW_AUTOSIZE);
imshow(warp_rotate_windowName, warp_rotate_dstImage);
namedWindow(rotate_windowName, WINDOW_AUTOSIZE);
imshow(rotate_windowName, rotate_dstImage);
waitKey(0);
return 0;
}
原圖:
仿射變換後圖像:
仿射變換後旋轉圖像:
原圖像旋轉後圖像: