若該文爲原創文章,未經允許不得轉載
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105691534
各位讀者,知識無窮而人力有窮,要麼改需求,要麼找專業人士,要麼自己研究
目錄
紅胖子(紅模仿)的博文大全:開發技術集合(包含Qt實用技術、樹莓派、三維、OpenCV、OpenGL、ffmpeg、OSG、單片機、軟硬結合等等)持續更新中...(點擊傳送門).
OpenCV開發專欄(點擊傳送門)
OpenCV開發筆記(四十六):紅胖子8分鐘帶你深入瞭解仿射變化(圖文並茂+淺顯易懂+程序源碼)
前言
紅胖子來也!!!
圖像處理中的仿射,仿射其實原理與重映射類似,其可以理解爲更高級的重映射變換。
相關博客
《OpenCV開發筆記(八):OpenCV常用操作之計時、縮放、旋轉、鏡像》
該文章中,也同樣實現了部分簡單重映射效果,使用的四個函數:
- 旋轉函數1:cv::transpose,直接對矩陣進行順時鐘旋轉90°
- 旋轉函數2:cv::rotate,三個枚舉可以旋轉90°,180°,270°
- 翻轉函數:cv::flip,xy軸翻轉
- 縮放函數:cv::resize,縮放
《OpenCV開發筆記(四十五):紅胖子8分鐘帶你深入瞭解重映射(圖文並茂+淺顯易懂+程序源碼)》
Demo
仿射變換
概述
仿射變換(仿射映射)是指在幾何中,一個響亮空間進行一次線性變換並接上一個平移,變換爲另一個向量空間的過程。
一個任意的仿射都能表示爲乘以一個矩陣再加上一個向量(平移)的形式(注:重映射可以理解爲是一種簡單的仿射變換)。
- 旋轉:rotation,線性變換;
- 平移:translation,向量加;
- 縮放,scale,線性變換;
原理
仿射是用矩陣表示變換的關係,通常使用的是2 x 3的矩陣來表示仿射變換。
整個映射關係爲:
dst = src * A + B
其中:
A爲線性變換矩陣,B爲向量加矩陣;
對應的每個像素的變換關係,則是:
dst(x,y) = src(x,y) * A + B
下面上圖解釋仿射(三個點就是函數提供計算仿射矩陣需要我們輸入的點):
與重映射原理區別
仿射變換的原理與重映射不同,重映射是直接對像素點的序號進行映射,可以做些拉長,特點像素着重等等(如哈哈鏡),而仿射變換是對原矩陣的標準變換,有適合各自的場景。
void warpAffine( InputArray src,
OutputArray dst,
InputArray M,
Size dsize,
int flags = INTER_LINEAR,
int borderMode = BORDER_CONSTANT,
const Scalar& borderValue = Scalar());
- 參數一:InputArray類型的src,一般爲cv::Mat;
- 參數二:OutputArray類型的dst,目標圖像。它的大小、類型與src相同。
- 參數三:InputArray類型的M,即變換矩陣,大小爲2 x 3。
- 參數四:Size類型的size,表示輸出圖像的尺寸;
- 參數五:int類型的interpolation,使用的插值方法;
- 參數六:int類型的borderMode,邊界處理方式;
- 參數七:Scalar類型的borderValue,重映射後,離羣點的背景,需要broderMode設置爲BORDER_CONSTRANT時纔有效。
計算二維三角變換矩陣函數原型
Mat getAffineTransform( InputArray src,
InputArray dst );
- 參數一:InputArray類型的src,原來三個點的座標;
- 參數二:OutputArray類型的dst,映射後三個點的座標;
計算二維旋轉變換矩陣函數原型
Mat getRotationMatrix2D( Point2f center,
double angle,
double scale );
- 參數一:Point2f類型的center,輸入的旋轉中心點;
- 參數二:double類型的angle,旋轉的角度(逆時針旋轉角度,非弧度);
- 參數三:double類型的scale,縮放係數;
Demo源碼
void OpenCVManager::testAffineMap()
{
QString fileName1 =
"E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
cv::Mat srcMat = cv::imread(fileName1.toStdString());
cv::Mat dstMat;
int width = 400;
int height = 300;
cv::resize(srcMat, srcMat, cv::Size(width, height));
cv::String windowName = _windowTitle.toStdString();
cvui::init(windowName);
cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 2,
srcMat.rows * 4),
srcMat.type());
while(true)
{
windowMat = cv::Scalar(0, 0, 0);
cv::Mat mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, srcMat, 1.0f, 0.0f, mat);
// 第一種旋轉180度
{
cv::Mat M = cv::getRotationMatrix2D(cv::Point(srcMat.cols / 2,
srcMat.rows / 2),
180.0f,
1.0f);
dstMat = srcMat.clone();
dstMat = cv::Scalar(0, 0, 0);
cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
// 第二種旋轉45度,縮小1/2
{
cv::Mat M = cv::getRotationMatrix2D(cv::Point(srcMat.cols / 2,
srcMat.rows / 2),
45.0f,
0.5f);
dstMat = srcMat.clone();
dstMat = cv::Scalar(0, 0, 0);
cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
// 第三種旋轉315度,縮小1/2
{
cv::Mat M = cv::getRotationMatrix2D(cv::Point(srcMat.cols / 2,
srcMat.rows / 2),
315.0f,
0.5f);
dstMat = srcMat.clone();
dstMat = cv::Scalar(0, 0, 0);
cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
// 第四種旋轉135度,縮小1/2
{
cv::Mat M = cv::getRotationMatrix2D(cv::Point(srcMat.cols / 2,
srcMat.rows / 2),
135.0f,
0.5f);
dstMat = srcMat.clone();
dstMat = cv::Scalar(0, 0, 0);
cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
// 第五種旋轉225度,縮小1/2
{
cv::Mat M = cv::getRotationMatrix2D(cv::Point(srcMat.cols / 2,
srcMat.rows / 2),
225.0f,
0.5f);
dstMat = srcMat.clone();
dstMat = cv::Scalar(0, 0, 0);
cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
// 第六種使用三角點進行仿射變換,沿着對角線翻轉
{
cv::Point2f srcTraingle[3];
cv::Point2f dstTraingle[3];
srcTraingle[0] = cv::Point2f(0, 0);
srcTraingle[1] = cv::Point2f(srcMat.cols - 1, 0);
srcTraingle[2] = cv::Point2f(0, srcMat.rows - 1);
dstTraingle[0] = cv::Point2f(0, 0);
dstTraingle[1] = cv::Point2f(0, srcMat.rows - 1);
dstTraingle[2] = cv::Point2f(srcMat.cols - 1, 0);
cv::Mat M = cv::getAffineTransform(srcTraingle, dstTraingle);
dstMat = srcMat.clone();
dstMat = cv::Scalar(0, 0, 0);
cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
// 第七種使用三角點進行仿射變換
{
cv::Point2f srcTraingle[3];
cv::Point2f dstTraingle[3];
srcTraingle[0] = cv::Point2f(0, 0);
srcTraingle[1] = cv::Point2f(srcMat.cols - 1, 0);
srcTraingle[2] = cv::Point2f(0, srcMat.rows - 1);
dstTraingle[0] = cv::Point2f(srcMat.cols / 4, srcMat.rows / 4);
dstTraingle[1] = cv::Point2f(srcMat.cols / 4 * 3, srcMat.rows / 4 );
dstTraingle[2] = cv::Point2f(srcMat.cols / 2, srcMat.rows - 1);
cv::Mat M = cv::getAffineTransform(srcTraingle, dstTraingle);
dstMat = srcMat.clone();
dstMat = cv::Scalar(0, 0, 0);
cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
// 更新
cvui::update();
// 顯示
cv::imshow(windowName, windowMat);
// esc鍵退出
if(cv::waitKey(25) == 27)
{
break;
}
}
}
工程模板:對應版本號v1.41.0
對應版本號v1.41.0
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105691534