生活随笔
收集整理的這篇文章主要介紹了
Opencv——几何空间变换(仿射变换和投影变换)
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
幾何空間變換
- 【1】幾何變換(空間變換)簡(jiǎn)述
- 【2】變換矩陣知識(shí)簡(jiǎn)述
- 【3】圖像的仿射變換
- 1、平移變換
- 2、比例縮放
- 3、旋轉(zhuǎn)
- 4、對(duì)稱(chēng)變換(不做展示)
- 1、關(guān)于X軸變換
- 2、關(guān)于Y軸變換
- 3、關(guān)于直線Y=X變換
- 4、關(guān)于直線Y=-X變換
- 5、錯(cuò)切變換
- 6、復(fù)合變換
- 【4】圖像的投影變換
- 【5】應(yīng)用
- 【6】Opencv自帶的變換函數(shù):
- Opencv中仿射變換的函數(shù):warpAffine()函數(shù)
- Opencv中計(jì)算二維旋轉(zhuǎn)變換矩陣: getRotationMatrix2D()函數(shù)
【1】幾何變換(空間變換)簡(jiǎn)述
圖像的幾何變換,又稱(chēng)空間變換,是圖形處理的一個(gè)方面,是各種圖形處理算法的基礎(chǔ)。它將一幅圖像中的坐標(biāo)位置映射到另一幅圖像中的新坐標(biāo)位置,其實(shí)質(zhì)是改變像素的空間位置,估算新空間位置上的像素值。
幾何變換算法一般包括空間變換運(yùn)算和插值算法。
【2】變換矩陣知識(shí)簡(jiǎn)述
齊次坐標(biāo)的概念
圖像一般是二維的,坐標(biāo)形式為(x,y)。
這里我們將其擴(kuò)展為3維形式的齊次坐標(biāo)。形式如下:
第三個(gè)參數(shù)是尺度參數(shù),控制尺度縮放。(1的時(shí)候表示尺度不變)
齊次坐標(biāo)使用n+1維,來(lái)表示n維的坐標(biāo)。它的優(yōu)點(diǎn)如下所示:
●統(tǒng)一坐標(biāo)的加法運(yùn)算和乘法運(yùn)算, 運(yùn)算時(shí)提高效率。
●表示無(wú)窮遠(yuǎn)的點(diǎn)。 當(dāng)z=0的時(shí)候,表示無(wú)窮遠(yuǎn)的點(diǎn)。
( x,y,z) ----->( x/z, y/z) ;齊次坐標(biāo)和二維坐標(biāo)的換算
如,(2,2,1),(4,4,2 )表示同樣的點(diǎn)。
幾何運(yùn)算矩陣
最左邊是變換后的齊次坐標(biāo),中間的是原圖點(diǎn)的其次坐標(biāo),最右邊是變換矩陣,有9個(gè)參數(shù),分為4個(gè)子矩陣,每個(gè)子矩陣具有特殊意義。
T1:比例、旋轉(zhuǎn)、對(duì)稱(chēng)、錯(cuò)切
T2:平移
T3:投影
T4:整體縮放(通常我們通過(guò)T1實(shí)現(xiàn)縮放,所以這里通常為1)
所謂的仿射變換其實(shí)就是通過(guò)T1、T2進(jìn)行變換。
所謂的投影變換就是在仿射變換上多用到了T3。
這里我們忽略T4。
【3】圖像的仿射變換
為了能夠直觀地了解參數(shù)對(duì)于變換的各種影響,我編寫(xiě)了一個(gè)程序,通過(guò)滑動(dòng)條來(lái)控制參數(shù),同時(shí)顯示參數(shù)改變后的圖像。
這里的參數(shù)我都是設(shè)的正的,你把滑動(dòng)條從正最大移到0就相當(dāng)于是逆操作了。
代碼如下:
#include <opencv2/opencv.hpp>
#include <iostream>
#include "windows.h"
#include <stdio.h>
#define WINDOW_NAME "【程序窗口】" using namespace cv
;
using namespace std
;
int g_nValueA
= 100;
int g_nValueB
= 0;
int g_nValueC
= 0;
int g_nValueD
= 100;
int g_nValueL
= 50;
int g_nValueM
= 50;
int g_nValueP
= 0;
int g_nValueQ
= 0;
int I_max
= 400;
int g_nValueS
= 100;
int theta
= 0;
int change_switch
= 0;
int center_x
= I_max
/ 2;
int center_y
= I_max
/ 2;
Mat g_srcImage
,g_dstImage
;void on_change(int, void*); int main()
{SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE
), FOREGROUND_INTENSITY
| FOREGROUND_GREEN
); g_srcImage
= Mat
::zeros(I_max
, I_max
, CV_8UC1
);g_dstImage
= Mat
::zeros(I_max
, I_max
, CV_8UC1
);for (int i
= I_max
/2;i
< I_max
/2+50;i
++) {for (int j
= I_max
/ 2;j
< I_max
/ 2 + 50;j
++) {g_srcImage
.at
<uchar
>(i
, j
) = 255;}}namedWindow(WINDOW_NAME
, WINDOW_NORMAL
);imshow("原圖", g_srcImage
);createTrackbar("a", WINDOW_NAME
, &g_nValueA
,150, on_change
);createTrackbar("b", WINDOW_NAME
, &g_nValueB
, 150, on_change
);createTrackbar("c", WINDOW_NAME
, &g_nValueC
, 150, on_change
);createTrackbar("d", WINDOW_NAME
, &g_nValueD
, 150, on_change
);createTrackbar("l", WINDOW_NAME
, &g_nValueL
, 150, on_change
);createTrackbar("m", WINDOW_NAME
, &g_nValueM
, 150, on_change
);createTrackbar("p", WINDOW_NAME
, &g_nValueP
, 150, on_change
);createTrackbar("q", WINDOW_NAME
, &g_nValueQ
, 150, on_change
);createTrackbar("s", WINDOW_NAME
, &g_nValueS
, 150, on_change
);createTrackbar("角度", WINDOW_NAME
, &theta
, 360, on_change
);createTrackbar("switch", WINDOW_NAME
, &change_switch
, 1, on_change
);on_change(0,0); while (1){if (waitKey(10) == 27) break; }return 0;}
void on_change(int, void*)
{g_dstImage
= Mat
::zeros(I_max
, I_max
, CV_8UC1
);float a
= g_nValueA
* 0.01;float b
= g_nValueB
* 0.01;float c
= g_nValueC
* 0.01;float d
= g_nValueD
* 0.01;int l
= g_nValueL
;int m
= g_nValueM
;float p
= g_nValueP
* 0.0005;float q
= g_nValueQ
* 0.0005;float s
= g_nValueS
* 0.01;int x_change
, y_change
;if (change_switch
== 0){for (int x
= I_max
/ 2;x
< I_max
/ 2 + 50;x
++) {for (int y
= I_max
/ 2;y
< I_max
/ 2 + 50;y
++) {x_change
= (a
* x
+ c
* y
+ l
) / (p
* x
+ q
* y
+ 1);y_change
= (b
* x
+ d
* y
+ m
) / (p
* x
+ q
* y
+ 1);if (x_change
>= I_max
) x_change
= I_max
- 1;else if (x_change
<= 0) x_change
= 0;else{}if (y_change
>= I_max
) y_change
= I_max
- 1;else if (y_change
<= 0) y_change
= 0;else{}g_dstImage
.at
<uchar
>(x_change
, y_change
) = 255;}}}else{a
= cos(theta
);b
= sin(theta
);c
= -1 * sin(theta
);d
= cos(theta
);for (int x
= I_max
/ 2;x
< I_max
/ 2 + 50;x
++) {for (int y
= I_max
/ 2;y
< I_max
/ 2 + 50;y
++) {x_change
= (x
- center_x
) * cos(theta
) - (y
- center_y
) * sin(theta
) + center_x
;y_change
= (x
- center_x
) * sin(theta
) + (y
- center_y
) * cos(theta
)+ center_y
;if (x_change
>= I_max
) x_change
= I_max
- 1;else if (x_change
<= 0) x_change
= 0;else{}if (y_change
>= I_max
) y_change
= I_max
- 1;else if (y_change
<= 0) y_change
= 0;else{}g_dstImage
.at
<uchar
>(x_change
, y_change
) = 255;}}}imshow("效果圖", g_dstImage
);
}
原圖如下:
接下來(lái)看具體變換:
1、平移變換
效果展示:
2、比例縮放
效果展示:
3、旋轉(zhuǎn)
這里的旋轉(zhuǎn)是以原點(diǎn)為中心點(diǎn)的,當(dāng)我們以(center_x,center_y)為中點(diǎn),則需要修改公式為:
X’=(X-center_x)*cos(theta)-(Y-center_y)*sin(theta) + center_x;
Y’=(X-center_x)*sin(theta)+(Y-center_y)*cos(theta) +center_y ;
效果展示:
4、對(duì)稱(chēng)變換(不做展示)
1、關(guān)于X軸變換
2、關(guān)于Y軸變換
3、關(guān)于直線Y=X變換
4、關(guān)于直線Y=-X變換
5、錯(cuò)切變換
效果展示:
6、復(fù)合變換
【4】圖像的投影變換
點(diǎn)共線特性:原本是一條直線,變換后還是一條直線
效果展示:
【5】應(yīng)用
由原理可知,變換的本質(zhì)就是通過(guò)對(duì)應(yīng)點(diǎn)組的坐標(biāo)來(lái)求解方程。一個(gè)變換是否理想,在公式不做調(diào)整的情況下就要看對(duì)應(yīng)點(diǎn)的選擇。
這里我們一般選擇圖像的特征點(diǎn)。這些知識(shí)會(huì)在以后展開(kāi)講,哲理不做過(guò)多擴(kuò)展。(像上面的二維碼變換,我們選取的特征點(diǎn)考慮那三個(gè)定位點(diǎn),當(dāng)然還要再找一個(gè)特征點(diǎn)。以后掌握了這方面知識(shí)再補(bǔ)充。)
【6】Opencv自帶的變換函數(shù):
Opencv中仿射變換的函數(shù):warpAffine()函數(shù)
公式依據(jù):
C++: void warpAffine (InputArray src, OutputArray dst, InputArray M, Size
dsize, int flags=INTER_LINEAR,intborderMode=BORDER_CONSTANT, const
Scalar& borderValue=Scalar() )
第一個(gè)參數(shù),InputArray 類(lèi)型的src,輸入圖像,即源圖像,填Mat類(lèi)的對(duì)
象即可。
第二個(gè)參數(shù),OutputArray 類(lèi)型的dst, 函數(shù)調(diào)用后的運(yùn)算結(jié)果存在這里,
需和源圖片有一樣的尺寸和類(lèi)型。
第三個(gè)參數(shù),InputArray 類(lèi)型的M,2x3 的變換矩陣。
第四個(gè)參數(shù),Size 類(lèi)型的dsize,表示輸出圖像的尺寸。
第五個(gè)參數(shù),int 類(lèi)型的flags, 插值方法的標(biāo)識(shí)符。此參數(shù)有默認(rèn)值
INTER_ LINEAR(線性插值),可選的插值方式如下圖所示。
第六個(gè)參數(shù),int類(lèi)型的borderMode,邊界像素模式,默認(rèn)值為
BORDER CONSTANT。
第七個(gè)參數(shù),const Scalar&類(lèi)型的borderValue, 在恒定的邊界情況下取的
值,默認(rèn)值為Scalar(), 即0。
Opencv中計(jì)算二維旋轉(zhuǎn)變換矩陣: getRotationMatrix2D()函數(shù)
C++: Mat getRotationMatrix2D (Point2fcenter, double angle, double scale)
第一個(gè)參數(shù),Point2f 類(lèi)型的center,表示源圖像的旋轉(zhuǎn)中心。
第二個(gè)參數(shù),double類(lèi)型的angle,旋轉(zhuǎn)角度。角度為正值表示向逆時(shí)針旋轉(zhuǎn)(坐標(biāo)原點(diǎn)是左上角)。
第三個(gè)參數(shù),double 類(lèi)型的scale,縮放系數(shù)。
int main()
{SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE
), FOREGROUND_INTENSITY
| FOREGROUND_GREEN
); Point2f srcTriangle
[3];Point2f dstTriangle
[3];Mat
rotMat(2, 3, CV_32FC1
); Mat
warpMat(2, 3, CV_32FC1
); Mat srcImage
, dstImage_warp
, dstImage_warp_roate
;srcImage
= imread("D:\\opencv_picture_test\\形態(tài)學(xué)操作\\黑白.jpg");if (srcImage
.empty()){cout
<< "圖像加載失敗!" << endl
;return -1;}elsecout
<< "圖像加載成功!" << endl
<< endl
;dstImage_warp
= Mat
::zeros(srcImage
.rows
, srcImage
.cols
, srcImage
.type()); srcTriangle
[0] = Point2f(0, 0); srcTriangle
[1] = Point2f(0, 0);srcTriangle
[2] = Point2f(0, 0);dstTriangle
[0] = Point2f(0, 0);dstTriangle
[1] = Point2f(0, 0);dstTriangle
[2] = Point2f(0, 0);warpMat
= getAffineTransform(srcTriangle
, dstTriangle
); warpAffine(srcImage
,dstImage_warp
,warpMat
,dstImage_warp
.size());Point center
= Point(dstImage_warp
.cols
/ 2, dstImage_warp
.rows
/ 2); double angle
= -30.0; double scale
=0.8;rotMat
= getRotationMatrix2D(center
, angle
,scale
);warpAffine(dstImage_warp
,dstImage_warp_roate
, rotMat
,dstImage_warp
.size());namedWindow("原圖像", WINDOW_NORMAL
); imshow("原圖像", srcImage
);namedWindow("縮放圖", WINDOW_NORMAL
); imshow("縮放圖", dstImage_warp
);namedWindow("縮放旋轉(zhuǎn)圖", WINDOW_NORMAL
); imshow("縮放旋轉(zhuǎn)圖", dstImage_warp_roate
);waitKey(0);return 0;
}
效果:
PPT是盜用的我們李竹老師的,嘿嘿。
總結(jié)
以上是生活随笔為你收集整理的Opencv——几何空间变换(仿射变换和投影变换)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。