0.概述
圖像變換的基本原理都是找到原圖和目標圖的像素位置的映射關系,這個可以用坐標系來思考,在opencv中,
圖像的坐標系是從左上角開始(0,0)
,向右是x增加方向(cols
),向下時y增加方向(rows
)。
普通坐標關系:
圖像坐標關系:
1.圖像的平移
圖像的平移是比較簡單的映射關系,對于原圖像的某個像素點位置(X0,Y0)
,向右平移100個像素的話,變換之后的目標像素點位置(X = X0+100,Y)
,然后用原圖像的像素值填充目標位置就可,因此我們需要將這種映射關系轉換一下,方便獲得原圖像素值,也就是X0 = X-100
,這里X是已知的。
具體代碼如下:
void translation(cv::Mat & src, cv::Mat & dst, int dx, int dy)
{const int rows = src.rows; // 獲得原圖的高度(y)const int cols = src.cols; // 獲得原圖的寬度(x)dst.create(rows, cols, src.type()); // 按照原圖大小和格式創建一個空白圖Vec3b *p; for (int Y = 0; Y < rows; ++Y) // 按行掃描{p = dst.ptr<Vec3b>(Y);for (int X = 0; X < cols; ++X){int X0 = X - dx; // 逆映射關系,求得原圖的位置int Y0 = Y - dy;if (X0 >= 0 && Y0 >= 0 && X0 < cols && Y0 < rows) // 防止越界{p[X] = src.ptr<Vec3b>(Y0)[X0]; // 將原圖的像素值賦給目標位置}}}}
2.圖像的縮放
這里暫時只貼出opencv的縮放接口:
void resize(InputArray src, //輸入圖像
OutputArray dst, // 輸出圖像
Size dsize, // 指定的輸出圖像的大小
double fx=0, // 橫向縮放比例
double fy=0, // 縱向縮放比例
int interpolation=INTER_LINEAR // 指定插值方式);
3.圖像的旋轉
圖像旋轉矩陣的原理可以參考這里
基本映射關系:
我們只需要根據這個映射關系寫就好,其中的dx和dy主要用來計算旋轉中心的,如果都是0的話圖像就是圍繞
圖像坐標(0,0)
來旋轉,該公式中的W'
和H'
指的是目標圖像的寬度和高度。
代碼:
void rotation(cv::Mat & src, cv::Mat & dst, int angle, cv::Point center = cv::Point(0, 0))
{// 計算角度的正余弦float sint = sin(angle*3.141592653 / 180); float cost = cos(angle*3.141592653 / 180);const int rows = src.rows; // rows == H (Y--->)const int cols = src.cols; // cols == W (X--->)// 計算旋轉中心的偏移float centerxScale = (float)center.x / cols; float centeryScale = (float)center.y / rows;float dx = -centerxScale * cols*cost - centeryScale * rows*sint + centerxScale * cols; // 根據映射公式float dy = centerxScale * cols*sint - centeryScale * rows*cost + centeryScale * rows;dst.create(rows, cols, src.type());Vec3b *p;for (int Y = 0; Y < rows; ++Y){p = dst.ptr<Vec3b>(Y);for (int X = 0; X < cols; ++X){int X0 = X*cost + Y*sint + dx; // 根據映射公式int Y0 = -X*sint + Y*cost + dy;if (X0 >= 0 && Y0 >= 0 && X0 < cols && Y0 < rows){p[X] = src.ptr<Vec3b>(Y0)[X0];}}}}
4.圖像的翻轉
這里也只貼opencv的接口:
void flip(InputArray src, // 原圖像OutputArray dst, //目標圖像
int flipCode // 翻轉方式,1:水平,0:垂直,-1:水平垂直
);
5.圖像的錯切
圖像的錯切效果可以想象伸縮門中的菱形的變化:
不過對于x方向的錯切,y方向的高度并不會變化。
貼代碼:
void shear(cv::Mat & src, cv::Mat & dst, float dx = 0,float dy = 0) // dx,dy為錯切率
{const int rows = src.rows; // rows == H (Y--->)const int cols = src.cols; // cols == W (X--->)dst.create(rows, cols, src.type());Vec3b *p;for (int Y = 0; Y < rows; ++Y){p = dst.ptr<Vec3b>(Y);for (int X = 0; X < cols; ++X){int X0 = X + dx*Y;int Y0 = Y + dy*X;if (X0 >= 0 && Y0 >= 0 && X0 < cols && Y0 < rows){ p[X] = src.ptr<Vec3b>(Y0)[X0];}}}}
效果圖(dx = 0.1,dy=0.1
):
6.圖像的仿射變換
圖像的仿射變換其實就是以上基本變換的組合,仿射變換可以維持原圖的點線關系,例如平行和比例等。
示例代碼:
#include <opencv.hpp>
#include <iostream>
#include <imgproc.hpp>using namespace std;
using namespace cv;int main()
{Mat img = imread("img.jpg");Mat dst;Point2f affinePoints0[3] = { Point2f(100, 50), Point2f(100, 390), Point2f(600, 50) }; // 選取原圖像的映射點Point2f affinePoints1[3] = { Point2f(200, 100), Point2f(200, 300), Point2f(500, 50) }; // 選取目標圖像的映射點Mat trans = getAffineTransform(affinePoints0, affinePoints1); // 獲得變換矩陣warpAffine(img, dst, trans, Size(img.cols, img.rows)); // 仿射變換for (int i = 0; i < 3; ++i) // 描點{circle(img, affinePoints0[i], 5, Scalar(0, 255, 255), -1);circle(dst, affinePoints1[i], 5, Scalar(0, 255, 255), -1);}imshow("src", img);imshow("dst", dst);waitKey(0);return 0;
}
效果圖:
7.圖像的透視變換
圖像的透視變換和放射變換類似,不過選取的映射點為四個。
示例代碼:
#include <opencv.hpp>
#include <iostream>
#include <imgproc.hpp>using namespace std;
using namespace cv;int main()
{Mat img = imread("img.jpg");Mat dst;Point2f perspectivePoints0[4] = { Point2f(100, 50), Point2f(100, 390), Point2f(600, 50),Point2f(600, 800) }; // 選取原圖像的映射點Point2f perspectivePoints1[4] = { Point2f(200, 100), Point2f(200, 300), Point2f(500, 50), Point2f(600, 800) }; // 選取目標圖像的映射點Mat trans = getPerspectiveTransform(perspectivePoints0, perspectivePoints1); // 獲得變換矩陣warpPerspective(img, dst, trans, Size(img.cols, img.rows)); // 透視變換for (int i = 0; i < 4; ++i) // 描點{circle(img, perspectivePoints0[i], 5, Scalar(0, 255, 255), -1);circle(dst, perspectivePoints1[i], 5, Scalar(0, 255, 255), -1);}imshow("src", img);imshow("dst", dst);waitKey(0);return 0;
}
效果圖(額。):