日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

图像几何变换C++实现--镜像,平移,旋转,错切,缩放

發布時間:2023/11/27 生活经验 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 图像几何变换C++实现--镜像,平移,旋转,错切,缩放 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一.圖像幾何變換介紹

????圖像的幾何空間變換是圖像處理中的最基礎的算法,是指對原始圖像按需要改變其大小、形狀和位置的變化,原始圖像與目標函數之間的坐標變換函數為線性函數。二維圖像的基本幾何變換主要包括鏡像、平移、縮放、旋轉、錯切(偏移)等操作,上述變換又稱為仿射變換,在岡薩雷斯的數字圖像處理第三版的第二章就做了相關介紹,數字圖像處理第三版下載地址。

????下面逐一介紹上述幾種放射變換。

二.變換詳解

????常見幾種變換公式如下表所示

????在進行變換時,其中鏡像、平移、縮放均以圖像像素坐標系(圖像左上頂點為坐標系原點)進行。
????對于旋轉和錯切一般以圖像中心為原點,此時在操作中需要加上坐標系轉換公式。圖像坐標系和旋轉坐標系如下圖所示,

????旋轉和錯切變換分為3步,

  1. 將圖像坐標轉換到旋轉坐標系;
  2. 根據上述表格公式進行旋轉或錯切變換;
  3. 將旋轉坐標系轉換到圖像坐標系;
    實際實現時可將三個矩陣合并進行計算,本文的實現即采用此種方法。

變換方式

  1. 前向映射

  2. 反向映射

    ????由于旋轉、縮放、變形變換中會出現漏點、不規則點問題,如果用前向映射,會導致得到的圖像中有很多黑點,對于這種情況,通常采用反向映射
    ????反向映射需通過插值方法決定輸出圖像該位置的值,因此需要選擇插值算法。通常有最近鄰插值、雙線性插值,雙三次插值等,具體的差值方法、原理及優缺點自行百度吧,我們這里采用雙線性插值

三.C++實現

????實現功能:鏡像、旋轉、平移、縮放、錯切、組合變換。利用了OpenCV的Mat,指針實現的后期有時間再補充。編程環境:VS2013+OpenCV2.4.13

#include <opencv2/opencv.hpp>
#include <iostream>using namespace std;
using namespace cv;#define PI 3.1415927
#define MAX(a,b) (((a)>(b))?(a):(b))// 單點雙線性插值
// [輸入]	ii--dst的行索引
//			jj--dst的列索引	
//			u_src--jj反向映射到src中對應的列索引
//			v_src--ii反向映射到src中對應的行索引
int Bilinear_interpolation_img(Mat src, Mat& dst, int ii, int jj, double u_src, double v_src)
{if (src.rows <= 0 || src.cols <= 0 || (src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U){printf("輸入圖像有誤!\n");return 0;}if (u_src >= 0 && u_src <= src.cols - 1 && v_src >= 0 && v_src <= src.rows - 1){int x1 = int(u_src), x2 = (int)(u_src + 0.5), y1 = (int)v_src, y2 = (int)(v_src + 0.5);double pu = fabs(u_src - x1), pv = fabs(v_src - y2);if (src.channels() == 1){dst.at<uchar>(ii, jj) = (1 - pv)*(1 - pu)*src.at<uchar>(y2, x1) + (1 - pv)*pu*src.at<uchar>(y2, x2) +pv*(1 - pu)*src.at<uchar>(y1, x1) + pv*pu*src.at<uchar>(y1, x2);}else{dst.at<Vec3b>(ii, jj)[0] = (1 - pv)*(1 - pu)*src.at<Vec3b>(y2, x1)[0] + (1 - pv)*pu*src.at<Vec3b>(y2, x2)[0] +pv*(1 - pu)*src.at<Vec3b>(y1, x1)[0] + pv*pu*src.at<Vec3b>(y1, x2)[0];dst.at<Vec3b>(ii, jj)[1] = (1 - pv)*(1 - pu)*src.at<Vec3b>(y2, x1)[1] + (1 - pv)*pu*src.at<Vec3b>(y2, x2)[1] +pv*(1 - pu)*src.at<Vec3b>(y1, x1)[1] + pv*pu*src.at<Vec3b>(y1, x2)[1];dst.at<Vec3b>(ii, jj)[2] = (1 - pv)*(1 - pu)*src.at<Vec3b>(y2, x1)[2] + (1 - pv)*pu*src.at<Vec3b>(y2, x2)[2] +pv*(1 - pu)*src.at<Vec3b>(y1, x1)[2] + pv*pu*src.at<Vec3b>(y1, x2)[2];}}return 1;
}//水平鏡像、垂直鏡像變換
// [輸入]	way_mirror鏡像方法:0水平鏡像 1垂直鏡像
int affine_mirrorImg(Mat src, Mat& dst, int way_mirror)
{if (src.rows <= 0 || src.cols <= 0 || (src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U){printf("輸入圖像有誤!\n");return 0;}if (way_mirror != 0 && way_mirror != 1){printf("輸入鏡像方法不為1或0,way_mirror: %d!\n",way_mirror);return 0;}int dst_h = src.rows, dst_w = src.cols;//目標圖像寬高 初始化為原圖寬高int ii = 0, jj = 0;double u_src = 0, v_src = 0;Mat M_mirr = (Mat_<double>(3, 3) << -1, 0, 0, 0, 1, 0, 0, 0, 1);if (way_mirror){M_mirr.at<double>(0,0) = 1;M_mirr.at<double>(1, 1) = -1;}Mat M_corrToSrc = (Mat_<double>(3, 3) << 1, 0, src.cols, 0, 1, 0, 0, 0, 1);if (way_mirror){M_corrToSrc.at<double>(0, 2) = 0;M_corrToSrc.at<double>(1, 2) = src.rows;}Mat M_trans = M_corrToSrc*M_mirr;Mat M_trans_inv = M_trans.inv();Mat dst_uv(3, 1, CV_64F);dst_uv.at<double>(2, 0) = 1;Mat src_uv(dst_uv);if (src.channels() == 3)dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC3); //RGB圖初始elsedst = cv::Mat::zeros(dst_h, dst_w, CV_8UC1);//反向映射for (ii = 0; ii < dst_h; ++ii){for (jj = 0; jj < dst_w; ++jj){dst_uv.at<double>(0, 0) = jj;dst_uv.at<double>(1, 0) = ii;src_uv = M_trans_inv*dst_uv;u_src = src_uv.at<double>(0, 0);v_src = src_uv.at<double>(1, 0);// 邊界問題if (u_src < 0) u_src = 0;if (v_src < 0) v_src = 0;if (u_src>src.cols - 1) u_src = src.cols - 1;if (v_src>src.rows - 1) v_src = src.rows - 1;//雙線性插值Bilinear_interpolation_img(src, dst, ii, jj, u_src, v_src);}}return 1;
}// 圖像旋轉(繞圖像中心) 逆時針旋轉為正
// 可處理8位單通道或三通道圖像
int affine_rotateImg(Mat src, Mat& dst, double Angle)
{if (src.rows <= 0 || src.cols <= 0 || (src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U){printf("輸入圖像有誤!\n");return 0;}double angle =0,cos_a=0,sin_a=0;//旋轉角度int dst_h = src.rows, dst_w = src.cols;//目標圖像寬高 初始化為原圖寬高int ii = 0, jj = 0;double u_src = 0, v_src = 0;angle = Angle / 180 * CV_PI;cos_a = cos(angle);sin_a = sin(angle);dst_h = (int)(fabs(src.rows*cos_a) + fabs(src.cols*sin_a) + 0.5);dst_w = (int)(fabs(src.rows*sin_a) + fabs(src.cols*cos_a) + 0.5);if (src.channels()==3){dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC3); //RGB圖初始}else{dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC1);}Mat M_toPhysics = (Mat_<double>(3, 3) << 1, 0, -0.5*src.cols, 0, -1, 0.5*src.rows, 0, 0, 1);Mat M_rotate = (Mat_<double>(3, 3) << cos_a, -sin_a, 0, sin_a, cos_a, 0, 0, 0, 1);Mat M_toPixel = (Mat_<double>(3, 3) << 1, 0, 0.5*dst.cols, 0, -1, 0.5*dst.rows, 0,0,1);Mat M_trans = M_toPixel*M_rotate*M_toPhysics;Mat M_trans_inv = M_trans.inv();Mat dst_uv(3, 1, CV_64F);dst_uv.at<double>(2, 0) = 1;Mat src_uv(dst_uv);//反向映射for (ii = 0; ii < dst_h; ++ii){for (jj = 0; jj < dst_w; ++jj){dst_uv.at<double>(0, 0) = jj;dst_uv.at<double>(1, 0) = ii;src_uv = M_trans_inv*dst_uv;u_src = src_uv.at<double>(0, 0);v_src = src_uv.at<double>(1, 0);//處理邊界問題if (int(Angle) % 90 == 0){if (u_src < 0) u_src = 0;if (v_src < 0) v_src = 0;if (u_src>src.cols - 1) u_src = src.cols - 1;if (v_src>src.rows - 1) v_src = src.rows - 1;}//雙線性插值Bilinear_interpolation_img(src, dst, ii, jj, u_src, v_src);}}return 1;
}// 圖像平移 在像素坐標系下進行 圖像左頂點為原點,x軸為圖像列,y軸為圖像行
// tx: x方向(圖像列)平移量,向右平移為正
// ty: y方向(圖像行)平移量,向下平移為正
int affine_moveImg(Mat src, Mat& dst, double tx, double ty)
{if (src.rows <= 0 || src.cols <= 0 || (src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U){printf("輸入圖像有誤!\n");return 0;}int dst_h = src.rows, dst_w = src.cols;int ii = 0, jj = 0;double u_src = 0, v_src = 0;if (src.channels() == 3){dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC3); //RGB圖初始}else{dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC1);}Mat M_toPhysics = (Mat_<double>(3, 3) << 1, 0, tx, 0, 1, ty, 0, 0, 1);Mat M_trans_inv = M_toPhysics.inv();Mat dst_uv(3, 1, CV_64F);dst_uv.at<double>(2, 0) = 1;Mat src_uv(dst_uv);//反向映射for (ii = 0; ii < dst_h; ++ii){for (jj = 0; jj < dst_w; ++jj){dst_uv.at<double>(0, 0) = jj;dst_uv.at<double>(1, 0) = ii;src_uv = M_trans_inv*dst_uv;u_src = src_uv.at<double>(0, 0);v_src = src_uv.at<double>(1, 0);//雙線性插值Bilinear_interpolation_img(src, dst, ii, jj, u_src, v_src);}}return 1;
}// 縮放 以圖像左頂點為原點
// cx: 水平縮放尺度
// cy: 垂直縮放尺度
int affine_scalingImg(Mat src, Mat& dst, double cx, double cy)
{if (src.rows <= 0 || src.cols <= 0 || (src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U){printf("輸入圖像有誤!\n");return 0;}int dst_h = (int)(cy*src.rows+0.5), dst_w =(int)(cx* src.cols+0.5);int ii = 0, jj = 0;double u_src = 0, v_src = 0;if (src.channels() == 3){dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC3); //RGB圖初始}else{dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC1);}Mat M_scale = (Mat_<double>(3, 3) << cx, 0, 0, 0, cy, 0, 0, 0, 1);Mat M_trans_inv = M_scale.inv();Mat dst_uv(3, 1, CV_64F);dst_uv.at<double>(2, 0) = 1;Mat src_uv(dst_uv);//反向映射for (ii = 0; ii < dst_h; ++ii){for (jj = 0; jj < dst_w; ++jj){dst_uv.at<double>(0, 0) = jj;dst_uv.at<double>(1, 0) = ii;src_uv = M_trans_inv*dst_uv;u_src = src_uv.at<double>(0, 0);v_src = src_uv.at<double>(1, 0);// 邊界問題if (u_src < 0) u_src = 0;if (v_src < 0) v_src = 0;if (u_src>src.cols - 1) u_src = src.cols - 1;if (v_src>src.rows - 1) v_src = src.rows - 1;//雙線性插值Bilinear_interpolation_img(src, dst, ii, jj, u_src, v_src);}}return 1;
}// 錯切變換 以圖像中心為偏移中心
// [輸入]	sx--水平錯切系數
//			sy--垂直錯切系數		
int affine_miscut(Mat src, Mat& dst, double sx, double sy)
{if (src.rows <= 0 || src.cols <= 0 || (src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U){printf("輸入圖像有誤!\n");return 0;}int dst_h = fabs(sy)*src.cols + src.rows, dst_w = fabs(sx)*src.rows + src.cols;int ii = 0, jj = 0;double u_src = 0, v_src = 0;if (src.channels() == 3){dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC3); //RGB圖初始}else{dst = cv::Mat::zeros(dst_h, dst_w, CV_8UC1);}Mat M_toPhysics = (Mat_<double>(3, 3) << 1, 0, -0.5*src.cols, 0, -1, 0.5*src.rows, 0, 0, 1);Mat M_rotate = (Mat_<double>(3, 3) << 1, sx, 0, sy, 1, 0, 0, 0, 1);Mat M_toPixel = (Mat_<double>(3, 3) << 1, 0, 0.5*dst.cols, 0, -1, 0.5*dst.rows, 0, 0, 1);Mat M_trans = M_toPixel*M_rotate*M_toPhysics;Mat M_trans_inv = M_trans.inv();Mat dst_uv(3, 1, CV_64F);dst_uv.at<double>(2, 0) = 1;Mat src_uv(dst_uv);//反向映射for (ii = 0; ii < dst_h; ++ii){for (jj = 0; jj < dst_w; ++jj){dst_uv.at<double>(0, 0) = jj;dst_uv.at<double>(1, 0) = ii;src_uv = M_trans_inv*dst_uv;u_src = src_uv.at<double>(0, 0);v_src = src_uv.at<double>(1, 0);//雙線性插值Bilinear_interpolation_img(src, dst, ii, jj, u_src, v_src);}}return 1;
}// 組合變換示例
// 縮放->旋轉->錯切(即偏移)
// [輸入]	
int affine_srm_combImg(Mat src, Mat& dst, double cx, double cy, double Angle, double sx, double sy)
{if (src.rows <= 0 || src.cols <= 0 || (src.channels() != 1 && src.channels() != 3) || src.depth() != CV_8U){printf("輸入圖像有誤!\n");return 0;}double angle, cos_a, sin_a;int dst_s_h, dst_s_w, dst_sr_h, dst_sr_w, dst_srm_h, dst_srm_w;angle = Angle / 180 * CV_PI;cos_a = cos(angle);sin_a = sin(angle);dst_s_h = (int)(cy*src.rows + 0.5);dst_s_w = (int)(cx* src.cols + 0.5);dst_sr_h = (int)(fabs(dst_s_h*cos_a) + fabs(dst_s_w*sin_a) + 0.5);dst_sr_w = (int)(fabs(dst_s_h*sin_a) + fabs(dst_s_w*cos_a) + 0.5);dst_srm_h = fabs(sy)*dst_sr_w + dst_sr_h;dst_srm_w = fabs(sx)*dst_sr_h + dst_sr_w;int ii = 0, jj = 0;double u_src = 0, v_src = 0;if (src.channels() == 3){dst = cv::Mat::zeros(dst_srm_h, dst_srm_w, CV_8UC3); //RGB圖初始}else{dst = cv::Mat::zeros(dst_srm_h, dst_srm_w, CV_8UC1);}Mat M_scale = (Mat_<double>(3, 3) << cx, 0, 0, 0, cy, 0, 0, 0, 1);Mat M_toPhysics = (Mat_<double>(3, 3) << 1, 0, -0.5*dst_s_w, 0, -1, 0.5*dst_s_h, 0, 0, 1);Mat M_rotate = (Mat_<double>(3, 3) << cos_a, -sin_a, 0, sin_a, cos_a, 0, 0, 0, 1);Mat M2 = M_rotate*M_toPhysics;Mat M_mis = (Mat_<double>(3, 3) << 1, sx, 0, sy, 1, 0, 0, 0, 1);Mat M_toPixel = (Mat_<double>(3, 3) << 1, 0, 0.5*dst.cols, 0, -1, 0.5*dst.rows, 0, 0, 1);Mat M3 = M_toPixel*M_mis;Mat M_trans = M3*M2*M_scale;Mat M_trans_inv = M_trans.inv();Mat dst_uv(3, 1, CV_64F);dst_uv.at<double>(2, 0) = 1;Mat src_uv(dst_uv);//反向映射for (ii = 0; ii < dst_srm_h; ++ii){for (jj = 0; jj < dst_srm_w; ++jj){dst_uv.at<double>(0, 0) = jj;dst_uv.at<double>(1, 0) = ii;src_uv = M_trans_inv*dst_uv;u_src = src_uv.at<double>(0, 0);v_src = src_uv.at<double>(1, 0);//處理邊界問題if (int(Angle) % 90 == 0){if (u_src < 0) u_src = 0;if (v_src < 0) v_src = 0;if (u_src>src.cols - 1) u_src = src.cols - 1;if (v_src>src.rows - 1) v_src = src.rows - 1;}//雙線性插值Bilinear_interpolation_img(src, dst, ii, jj, u_src, v_src);}}return 1;
}void main()
{Mat src = imread("autumn.jpg", 1),dst;//水平、垂直鏡像int way_mirror = 1;//affine_mirrorImg(src, dst, way_mirror);//旋轉double angle_r = 250;//int flag = affine_rotateImg(src, dst, angle_r);//if (flag == 0)//{//	return;//}//平移double tx = 50, ty = -50;
//	affine_moveImg(src, dst, tx, ty);//尺度變換(縮放)double cx = 1.5, cy = 1.5;affine_scalingImg(src, dst, cx, cy);//錯切(偏移)double sx = 0.2, sy = 0.2;
//	affine_trans_deviation(src, dst, sx, sy);
//	affine_miscut(src, dst, sx, sy);//組合變換 縮放->旋轉->錯切(即偏移)
//	affine_srm_combImg(src, dst, cx, cy, angle_r, sx, sy);// 顯示 Mat src_resize, dst_resize;//affine_scalingImg(src, src_resize, 0.4, 0.3);//affine_scalingImg(dst, dst_resize, 0.4, 0.3);namedWindow("src", 0);namedWindow("dst", 0);imshow("src", src);imshow("dst", dst);waitKey(0);system("pause");
}

程序運行結果如下:
注:圖片顯示部分是截圖效果,為了可能看起來不夠標準,實際程序運行是沒有問題的~
水平鏡像

垂直鏡像

平移

縮放

錯切

旋轉

組合變換

參考鏈接
????博客及程序寫作過程中難免出錯,歡迎指正~

總結

以上是生活随笔為你收集整理的图像几何变换C++实现--镜像,平移,旋转,错切,缩放的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。