当前位置:网站首页>Opencv——几何空间变换(仿射变换和投影变换)
Opencv——几何空间变换(仿射变换和投影变换)
2022-06-28 00:38:00 【51CTO】
几何空间变换
- 【3】图像的仿射变换
- 1、平移变换
- 2、比例缩放
- 3、旋转
- 4、对称变换(不做展示)
- 1、关于X轴变换
- 2、关于Y轴变换
- 3、关于直线Y=X变换
- 4、关于直线Y=-X变换
【1】几何变换(空间变换)简述
图像的几何变换,又称空间变换,是图形处理的一个方面,是各种图形处理算法的基础。它将一幅图像中的坐标位置映射到另一幅图像中的新坐标位置,其实质是改变像素的空间位置,估算新空间位置上的像素值。
几何变换算法一般包括空间变换运算和插值算法。
【2】变换矩阵知识简述
齐次坐标的概念
图像一般是二维的,坐标形式为(x,y)。
这里我们将其扩展为3维形式的齐次坐标。形式如下:
第三个参数是尺度参数,控制尺度缩放。(1的时候表示尺度不变)
齐次坐标使用n+1维,来表示n维的坐标。它的优点如下所示:
●统一坐标的加法运算和乘法运算, 运算时提高效率。
●表示无穷远的点。 当z=0的时候,表示无穷远的点。
( x,y,z) ----->( x/z, y/z) ;齐次坐标和二维坐标的换算
如,(2,2,1),(4,4,2 )表示同样的点。
几何运算矩阵
最左边是变换后的齐次坐标,中间的是原图点的其次坐标,最右边是变换矩阵,有9个参数,分为4个子矩阵,每个子矩阵具有特殊意义。
T1:比例、旋转、对称、错切
T2:平移
T3:投影
T4:整体缩放(通常我们通过T1实现缩放,所以这里通常为1)
所谓的仿射变换其实就是通过T1、T2进行变换。
所谓的投影变换就是在仿射变换上多用到了T3。
这里我们忽略T4。
【3】图像的仿射变换
为了能够直观地了解参数对于变换的各种影响,我编写了一个程序,通过滑动条来控制参数,同时显示参数改变后的图像。
这里的参数我都是设的正的,你把滑动条从正最大移到0就相当于是逆操作了。
代码如下:
//为窗口标题定义的宏
using namespace cv;
using namespace std;
//*--------------------------【全局变量声明】-------------------------------------*/
//*--------------------------【T1】-------------------------------------*/
int g_nValueA = 100;
int g_nValueB = 0;
int g_nValueC = 0;
int g_nValueD = 100;
//*--------------------------【T2】-------------------------------------*/
int g_nValueL = 50;
int g_nValueM = 50;
//*--------------------------【T3】-------------------------------------*/
int g_nValueP = 0;
int g_nValueQ = 0;
//*--------------------------【T4】-------------------------------------*/
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); //WINDOW_NORMAL允许用户自由伸缩窗口
imshow( "原图", g_srcImage);
//【4】创建滑动条来控制阈值
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); //初始化回调函数
//【7】轮询等待用户按键,如果ESC键按下则退出程序
while ( 1)
{
if ( waitKey( 10) == 27) break; //按下Esc 退出
}
return 0;
}
//*--------------------------【on_Threshold 函数】-------------------------------------*/
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);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
原图如下:
接下来看具体变换:
1、平移变换
效果展示:
2、比例缩放
效果展示:
3、旋转
这里的旋转是以原点为中心点的,当我们以(center_x,center_y)为中点,则需要修改公式为:
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、对称变换(不做展示)
1、关于X轴变换
2、关于Y轴变换
3、关于直线Y=X变换
4、关于直线Y=-X变换
5、错切变换
效果展示:
6、复合变换
【4】图像的投影变换
点共线特性:原本是一条直线,变换后还是一条直线
效果展示:
【5】应用
由原理可知,变换的本质就是通过对应点组的坐标来求解方程。一个变换是否理想,在公式不做调整的情况下就要看对应点的选择。
这里我们一般选择图像的特征点。这些知识会在以后展开讲,哲理不做过多扩展。(像上面的二维码变换,我们选取的特征点考虑那三个定位点,当然还要再找一个特征点。以后掌握了这方面知识再补充。)
【6】Opencv自带的变换函数:
Opencv中仿射变换的函数:warpAffine()函数
公式依据:
C++: void warpAffine (InputArray src, OutputArray dst, InputArray M, Size
dsize, int flags=INTER_LINEAR,intborderMode=BORDER_CONSTANT, const
Scalar& borderValue=Scalar() )
第一个参数,InputArray 类型的src,输入图像,即源图像,填Mat类的对
象即可。
第二个参数,OutputArray 类型的dst, 函数调用后的运算结果存在这里,
需和源图片有一样的尺寸和类型。
第三个参数,InputArray 类型的M,2x3 的变换矩阵。
第四个参数,Size 类型的dsize,表示输出图像的尺寸。
第五个参数,int 类型的flags, 插值方法的标识符。此参数有默认值
INTER_ LINEAR(线性插值),可选的插值方式如下图所示。
第六个参数,int类型的borderMode,边界像素模式,默认值为
BORDER CONSTANT。
第七个参数,const Scalar&类型的borderValue, 在恒定的边界情况下取的
值,默认值为Scalar(), 即0。
Opencv中计算二维旋转变换矩阵: getRotationMatrix2D()函数
C++: Mat getRotationMatrix2D (Point2fcenter, double angle, double scale)
第一个参数,Point2f 类型的center,表示源图像的旋转中心。
第二个参数,double类型的angle,旋转角度。角度为正值表示向逆时针旋转(坐标原点是左上角)。
第三个参数,double 类型的scale,缩放系数。
int main()
{
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN); //字体为绿色
//【1】参数准备
//定义两组点,代表两个三角形
Point2f srcTriangle[ 3];
Point2f dstTriangle[ 3];
//定义Mat变量(变换矩阵)
Mat rotMat( 2, 3, CV_32FC1); //CV_32FC1代表多少?
Mat warpMat( 2, 3, CV_32FC1); //CV_32FC1代表多少?
Mat srcImage, dstImage_warp, dstImage_warp_roate;
//原图,仿射变换后的图,旋转变换后的图
srcImage = imread( "D:\\opencv_picture_test\\形态学操作\\黑白.jpg");
//判断图像是否加载成功
if ( srcImage. empty())
{
cout << "图像加载失败!" << endl;
return - 1;
}
else
cout << "图像加载成功!" << endl << endl;
dstImage_warp = Mat::zeros( srcImage. rows, srcImage. cols, srcImage. type()); //转换图和原图像类型一样大小一样
//【2】利用三组对应点来计算参数
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);
//【3】求得仿射变换参数
warpMat = getAffineTransform( srcTriangle, dstTriangle); //利用对应点求得6个参数
//【4】对原图进行仿射变换
warpAffine( srcImage, dstImage_warp, warpMat, dstImage_warp. size());
//【5】获取旋转信息
Point center = Point( dstImage_warp. cols / 2, dstImage_warp. rows / 2); //中心点
double angle = - 30.0; //顺时针30度
double scale = 0.8;
//【6】通过上面的旋转细节信息求得旋转矩阵
rotMat = getRotationMatrix2D( center, angle, scale);
//【7】对缩放后的图像进行旋转
warpAffine( dstImage_warp, dstImage_warp_roate, rotMat, dstImage_warp. size());
//【8】显示结果
namedWindow( "原图像", WINDOW_NORMAL); //定义窗口显示属性
imshow( "原图像", srcImage);
namedWindow( "缩放图", WINDOW_NORMAL); //定义窗口显示属性
imshow( "缩放图", dstImage_warp);
namedWindow( "缩放旋转图", WINDOW_NORMAL); //定义窗口显示属性
imshow( "缩放旋转图", dstImage_warp_roate);
//创建三个窗口
waitKey( 0);
return 0;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
效果:
PPT是盗用的我们李竹老师的,嘿嘿。
边栏推荐
- The system administrator has set the system policy to prohibit this installation. Solution
- MySQL optimization tips
- JS实现滑动拼图验证
- AAVE launches lens protocol, a Web3 social media platform
- ScheduledThreadPoolExecutor源码解读(二)
- Cesium 抗锯齿(线,边框等)
- 系统管理员设置了系统策略,禁止进行此安装。解决方案
- 技术人员如何成为技术领域专家
- Fundamentals of scala (3): operators and process control
- Jenkins - Pipeline 概念及创建方式
猜你喜欢
Complex and inefficient logistics? Three steps to solve problems in enterprise administration
Using redis bitmap to realize personnel online monitoring
文件传输协议--FTP
Mysql大合集,你要内容的这里全都有
Numpy----np.meshgrid()
SQL 注入绕过(四)
Numpy----np. meshgrid()
Embedded must learn! Detailed explanation of hardware resource interface - based on arm am335x development board (Part 2)
SQL injection bypass (2)
SQL 注入繞過(二)
随机推荐
JS 数组随机取值(随机数组取值)
SQL injection bypass (3)
Locust performance test - parameterization, no repetition of concurrent cyclic data sampling
Numpy----np. meshgrid()
Numpy----np.reshape()
Cesium color color (assignment) random color
Geojson format description (detailed format)
CVPR22收录论文|基于标签关系树的层级残差多粒度分类网络
毕业总结
启牛开户安全吗?怎么线上开户?
Machine learning (x) reinforcement learning
云平台kvm迁移本地虚拟机记录
The system administrator has set the system policy to prohibit this installation. Solution
Keil “st-link usb communication error“解决方法
geojson 格式說明(格式詳解)
Jenkins - 内置变量访问
Jenkins - Groovy Postbuild 插件丰富 Build History 信息
Ti am3352/54/59 industrial core board hardware specification
Numpy----np. reshape()
JS implementation of Slide Puzzle verification