幾何變換 : 仿射變換
仿射變換
[x~y~]=[a11a21a12a22][xy]+[a13a14]
爲了計算方便(進行一些變換):齊次座標
⎣⎡x~y~1⎦⎤=⎣⎡a11a210a12a220a13a231⎦⎤⎣⎡xy1⎦⎤=A⎣⎡xy1⎦⎤
平移
(x~,y~)=(x+tx,y+ty)
⎣⎡x~y~1⎦⎤=⎣⎡100010txty1⎦⎤⎣⎡xy1⎦⎤
⎣⎡x~y~1⎦⎤=[1001txty]⎣⎡xy1⎦⎤
放大和縮小
二維空間縮放:二維空間的座標(x,y),以(0,0)爲中心在x軸方向和y軸方向上縮放.以縮放S倍爲例:
- (x~,y~)=(Sx∗x,Sy∗y)
- Sx=Sy : 等比例縮放
⎣⎡x~y~1⎦⎤=⎣⎡Sx000Sy0001⎦⎤⎣⎡xy1⎦⎤
二維空間:以任意座標x0,y0)爲中心進行縮放:(x~,y~)=(x0+Sx∗(x−x0),y0+Sy∗(y−y0))
⎣⎡x~y~1⎦⎤=⎣⎡100010x0y01⎦⎤⎣⎡Sx000Sy0001⎦⎤⎣⎡100010−x0−y01⎦⎤⎣⎡xy1⎦⎤
順時針旋轉α
計算(x,y)→(x~,y~), p爲(x,y)到原點的距離:
cosθ=px,sinθ=py
cos(θ+α)
cos(θ+α)=cosθcosα−sinθsinα=pxcosα−pysinα=px~
sin(θ+α)
sin(θ+α)=sinθcosα+cosθsinα=pycosα+pxsinα=py~
x~,y~
x~=xcosα−ysinα,y~=xsinα+ycosα
矩陣形式
⎣⎡x~y~1⎦⎤=⎣⎡cosαsinα0−sinαcosα0001⎦⎤⎣⎡xy1⎦⎤,A=⎣⎡cosαsinα0−sinαcosα0001⎦⎤
逆時針旋轉α
cos(θ−α)
cos(θ−α)=cosθcosα+sinθsinα=pxcosα+pysinα=px~
sin(θ−α)
sin(θ−α)=sinθcosα−cosθsinα=pycosα−pxsinα=py~
x~,y~
x~=xcosα+ysinα,y~=−xsinα+ycosα
矩陣形式
⎣⎡x~y~1⎦⎤=⎣⎡cosα−sinα0sinαcosα0001⎦⎤⎣⎡xy1⎦⎤,A=⎣⎡cosα−sinα0sinαcosα0001⎦⎤
從任意位置旋轉α矩陣形式
⎣⎡x~y~1⎦⎤=⎣⎡100010x0y01⎦⎤⎣⎡cosα−sinα0sinαcosα0001⎦⎤⎣⎡100010−x0−y01⎦⎤⎣⎡xy1⎦⎤=A⎣⎡xy1⎦⎤
計算放射矩陣
- getAffineTransform(src, dst)
- Point2f[]:保存放射前後的座標,數據類型CV_64F
- Mat_:保存放射前後的座標,數據類型CV_32F
- getRotationMatrix2D(center, angle, scale)
- center : 原座標
- angle : 角度
- scale : 縮放倍數
- resize(Mat src, Mat dst, Size dsize, double fx=0, double fy=0, int interpolation)
- src : 輸入圖像矩陣
- dst : 輸出圖像矩陣
- dsize : (寬,高)輸出圖像大小
- fx, fy : 水平垂直方向的縮放比例
- interpolation : INTE_NEAREST, INTE_LINEAR(默認)
- warpAffine(Mat src, Mat dst, Mat s, Size dsize)
- s : 放射變換矩陣
- rotate(Mat src, Mat dst, int rotateCode)
- src : 輸入矩陣
- dst : 輸出矩陣
- rotateCode : ROTATE_90_CLOCKWISE : 順時針旋轉90度,ROTATE_180 : 順時針旋轉180度,ROTATE_90_COUNTERCLOCKWISE : 順時針旋轉270度
#include<iostream>
#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
using namespace cv;
using namespace std;
int main()
{
Point2f src[] = { Point2f(0, 0), Point2f(20,0),Point2f(0,20) };
Point2f dst[] = { Point2f(0,0),Point2f(20,0),Point2f(0,20) };
Mat A = getAffineTransform(src, dst);
cout << A << endl;
return 0;
}
#include<iostream>
#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat src = (Mat_<float>(3, 2) << 0, 0, 20, 0, 0, 20);
Mat dst = (Mat_<float>(3, 2) << 0, 0, 10, 0, 0, 10);
Mat A = getAffineTransform(src, dst);
cout << A << endl;
return 0;
}
插值算法
把圖像看作是一個二維的圖像函數fI
z=fI(x,y)=I(x,y),0<=x<W,0<=y<H,x∈N,y∈N
- N : 整數集合
- I(x,y) : 矩陣I的x行y列的值
- fI : 圖像函數
- fI(x,y) : 圖像座標(x,y)處的值
(x,y)→(x~,y~)的空間變換關係如下:
⎣⎡x~y~1⎦⎤=A⎣⎡xy1⎦⎤,A=⎣⎡a11a210a12a220a13a231⎦⎤
圖像矩陣的放大fO(2x,2y)=fI(x,y):假設:(4,4)⟵(2,2),即fO(4,4)=fI(2,2),同理fO(3,3)=fI(1,5,1.5),但是fI在(1.5,1.5)處沒有函數值,因爲fI只在第一象限的整數座標處有函數值,但是(1.5,1.5)周圍四個臨近的座標有函數值(1,1),(2,1),(1,2),(2,2),可以根據這四個位置的函數值估算fI(1.5,1.5)的值.
最鄰近插值(輸出圖像會出現鋸齒狀)
最鄰近插值同上,也是找到目標座標四個相鄰的座標,從中找到一個距離目標最近的一個,假設爲(x^,y^)
fI(x,y)=fI(x^,y^)
線性插值
已知座標(x0,y0),(x1,y1),要計算得到兩點所在直線上x處y的值:
- x已知求y?
x−x0y−y0=x1−x0y1−y0
y=x1−x0y1−y0(x−x0)+y0
已知y計算x同上
雙線性插值
顧名思義,雙線性插值就是線性插值的double,p1,p2,p3,p4均已知,求O
-
先計算a, b
ya=xp2−xp1yp2−yp1(xa−xp1)+yp1(1)
yb=xp4−xp3yp4−yp3(xb−xp3)+yp3(2)
-
a,b 已知求O
yO=xa−xbya−yb(xO−xb)+yb(3)
圖像的縮放
#include<iostream>
#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat I = imread("./People/wave.jpg", 1);
if (!I.data)
return -1;
// 放大放射矩陣
Mat s = (Mat_<float>(2, 3) << 2, 0, 0, 0, 2, 0);
Mat dst1;
// 利用放射矩陣縮放
warpAffine(I, dst1, s, Size(I.cols * 2, I.rows * 2));
Mat dst2;
// resize 放大矩陣2倍
resize(I, dst2, Size(I.cols * 2, I.rows * 2), 2, 2);
Mat dst3;
resize(I, dst3, Size(I.cols * 10, I.rows * 10), 10, 10);
imshow("original img", I);
imshow("warpAffine", dst1);
imshow("resize x 2", dst2);
imshow("resize x 10", dst3);
waitKey(0);
}
圖像的旋轉
#include<iostream>
#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat I = imread("./People/wave.jpg", 1);
if (!I.data)
return -1;
Mat Ix10;
resize(I, Ix10, Size(I.cols * 10, I.rows * 10), 10, 10);
Mat I_90, I_180, I_270;
// 順時針旋轉90, 180, 270
rotate(Ix10, I_90, ROTATE_90_CLOCKWISE);
rotate(Ix10, I_180, ROTATE_180);
rotate(Ix10, I_270, ROTATE_90_CLOCKWISE);
imshow("順時針旋轉90", I_90);
imshow("順時針旋轉180", I_180);
imshow("順時針旋轉270", I_270);
waitKey(0);
}