Opencv——几何空间变换(仿射变换和投影变换)

几何空间变换

    • 【1】几何变换(空间变换)简述
    • 【2】变换矩阵知识简述
      • 齐次坐标的概念
      • 几何运算矩阵
    • 【3】图像的仿射变换
      • 1、平移变换
      • 2、比例缩放
      • 3、旋转
      • 4、对称变换(不做展示)
        • 1、关于X轴变换
        • 2、关于Y轴变换
        • 3、关于直线Y=X变换
        • 4、关于直线Y=-X变换
      • 5、错切变换
      • 6、复合变换
    • 【4】图像的投影变换
    • 【5】应用
    • 【6】Opencv自带的变换函数:
      • Opencv中仿射变换的函数:warpAffine()函数
      • Opencv中计算二维旋转变换矩阵: getRotationMatrix2D()函数

【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就相当于是逆操作了。
代码如下:

#include <opencv2/opencv.hpp>
#include <iostream>
#include "windows.h"
#include <stdio.h>
#define WINDOW_NAME "【程序窗口】"			//为窗口标题定义的宏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、旋转

在这里插入图片描述
在这里插入图片描述
这里的旋转是以原点为中心点的,当我们以(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轴变换

1

2、关于Y轴变换

2

3、关于直线Y=X变换

3

4、关于直线Y=-X变换

4

5、错切变换

1
2
效果展示:
错切

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;}elsecout << "图像加载成功!" << 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;
}

效果:
效果
PPT是盗用的我们李竹老师的,嘿嘿。
图像

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378661.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

probuffer java_Protocol Buffer的使用

Probotbuf简介在网络通信和通用数据交换等应用场景中经常使用的技术是 JSON 或 XML&#xff0c;这两种技术常被用于数据的结构化呈现和序列化。我们可以从两个方面来看JSON 和 XML与protobuf的异同&#xff1a;一个是数据结构化&#xff0c;一个是数据序列化。这里的数据结构化…

22-随机抽样一致算法RANSAC

随机抽样一致算法(Random sample consensus&#xff0c;RANSAC) 看似复杂&#xff0c;其基本思想就是&#xff1a;随机选取俩点&#xff0c;然后连接&#xff0c;给定一个容忍范围&#xff0c;在这个范围内的点越多越好&#xff0c;然后不断的迭代进行找两点之间容忍范围内点最…

23-背景建模

帧差法 由于场景中的目标在运动&#xff0c;目标的影像在不同图像帧中的位置不同。该类算法对时间上连续的两帧图像进行差分运算&#xff0c;不同帧对应的像素点相减&#xff0c;判断灰度差的绝对值&#xff0c;当绝对值超过一定阈值时&#xff0c;即可判断为运动目标&#xf…

DB2 9 运用开辟(733 考试)认证指南,第 3 部门: XML 数据独霸(4)

议决运用顺序存储和检索 XMLXML 编码字符编码在汗青上&#xff0c;术语 字符集、字符编码 和 码页 都有雷同的意义&#xff1a;一个字符集和一个二进制码集&#xff0c;其中每个码示意一个字符。&#xff08;码页是来自 IBM 的一个术语&#xff0c;示意一个大型主机或 IBM PC 上…

Opencv——霍夫变换以及遇到的一些问题

目录问题1 &#xff1a;颜色空间转换函数参数问题&#xff1a;CV_BGR2GRAY vs CV_GRAY2BGR问题2&#xff1a;cvRound()、cvFloor()、cvCeil()函数用法霍夫变换的含义标准霍夫直线变换霍夫线变换函数参数讲解累计概率霍夫变换霍夫变换圆变换原理和算法步骤&#xff1a;霍夫圆变换…

java ssm如何上传图片_ssm整合-图片上传功能(转)

本文介绍 ssm (SpringSpringMVCMybatis)实现上传功能。以一个添加用户的案例介绍(主要是将上传文件)。一、需求介绍我们要实现添加用户的时候上传图片(其实任何文件都可以)。文件名&#xff1a;以 博客名日期的年月日时分秒毫秒形式命名如 言曌博客2017082516403213.png路径&am…

24-光流估计

光流是空间运动物体在观测成像平面上的像素运动的“瞬间速度”&#xff0c;根据各个像素点的速度矢量特征&#xff0c;可以对图像进行动态分析&#xff0c;例如目标跟踪 亮度恒定&#xff1a;同一点随着时间的变化&#xff0c;其亮度不会发生改变 小运动&#xff1a;随着时间的…

java公平索非公平锁_java中的非公平锁不怕有的线程一直得不到执行吗

首先来看公平锁和非公平锁&#xff0c;我们默认使用的锁是非公平锁&#xff0c;只有当我们显示设置为公平锁的情况下&#xff0c;才会使用公平锁&#xff0c;下面我们简单看一下公平锁的源码&#xff0c;如果等待队列中没有节点在等待&#xff0c;则占有锁&#xff0c;如果已经…

mybatis.net - 5 嵌入资源与引用资源

在SqlMap.config文件中可以有两种方式引入外部的文件。 一种是通过资源的方式&#xff0c;在文件中表现为 resource&#xff0c;就是引用外部的文件&#xff0c;这里需要保证文件的路径正确。 <sqlMaps><sqlMap resource"Maps/ProductMap.xml"/><sqlM…

图解MySQL数据库的陈列和把持-4

泉源&#xff1a;网海拾贝 填入一些测试数据&#xff1a; 封闭“MySQL Query Browser”&#xff0c;再从头翻开它&#xff0c;切换到testtable表&#xff0c;看到了没有&#xff1f;刚刚输出的中文变成了“&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&am…

非常好友(C++)

Bessie和其他的所有奶牛的耳朵上都戴有一个射频识别&#xff08;RFID&#xff09;序列号码牌。因此农夫John可以机械化地计算他们的数量。很多奶牛都有一个“牛友”。如果奶牛A的序列号的约数之和刚好等于奶牛B的序列号&#xff0c;那么A的牛友就是B。在这里&#xff0c;一个数…

使用快捷键,快到极致

前段时间曾经写过一篇文章&#xff0c;《优秀程序员无他-善假于物也》。其中谈到一点是优秀的程序员必须要能灵活的掌握常用软件的快捷键。对于程序员来说&#xff0c;每天使用时间最长的软件恐怕就是IDE&#xff08;Integrated Development Environment&#xff09;了。如果你…

轮廓(查找和绘制轮廓、轮廓的表达与组织、轮廓的特性)

目录1、轮廓的定义2、如何在图像中找到轮廓opencv自带的查找轮廓函数:findContours()3、轮廓的表达方式1.顶点的序列2.Freeman链码4、轮廓之间的组织方式5、轮廓的特点&#xff08;这部分可以展开来详细探讨&#xff0c;这里不做过多解释&#xff09;6、轮廓的匹配7、绘制轮廓d…

01-基本配置与测试

一、开发环境 使用的是Anaconda 3&#xff0c;其中Python版本为3.6.3 首先&#xff0c;在Anaconda下的Scripts文件夹下打开命令框 使用pip install -U selenium安装selenium 打开Jupiter Notebook&#xff0c;输入from selenium import webdriver进行测试 不报错即安装成功 …

Opencv——查找并绘制凸包、凸包与轮廓的关系

定义 给定二维平面上的点集&#xff0c;凸包就是将最外层的点连接起来构成的凸多边型。 理解物体形状或轮廓的一 种比较有用的方法便是计算一个物体的凸包&#xff0c;然后计算其凸缺陷(convexity defects)。 检测凸包 opencv自带函数&#xff1a;convexHull() 参数解释&a…

java接口并发衡量_java 后端设计高并发接口总结

如何设置高并发接口一、并发队列的选择二、请求接口的合理设计三、高并发下的数据安全3.1 超发的原因3.2 悲观锁思路3.3 FIFO队列思路3.4 乐观锁思路一、并发队列的选择Java的并发包提供了三个常用的并发队列实现&#xff0c;分别是&#xff1a;ArrayBlockingQueue、Concurrent…

JAVA 取得当前目录的路径

在写java程序时不可避免要获取文件的路径...总结一下,遗漏的随时补上 1.可以在servlet的init方法里 String path getServletContext().getRealPath("/"); 这将获取web项目的全路径 例如 :E:/eclipseM9/workspace/tree/ tree是我web项目的根目录 2.你也可以随时在任意…

02.1-元素定位(find)

常用的一些方法 一、导包 from selenium import webdriver二、打开火狐&#xff08;空白页&#xff09; b webdriver.Firefox()三、跳转到指定的网站 b.get(https://www.baidu.com/)四、将浏览器页面最大化 b.maximize_window()五、通过F12可查看当前的贴吧为超链接形式 …

快速傅里叶变换(FFT)——按时间抽取DIT的基

目录【1】前言1、DIF计算量2、利用性质改善【2】公式推导1、N 到 2*N/2a、分解原序列b、分解后的DFT变换c、一系列化简操作之后d、蝶形信号流e、计算量总结2、N/2 到 2*N/4a、分解X2(k)序列b、蝶形信号流&#xff08;2列&#xff09;3、N/4 到 2*N/8a、蝶形信号流&#xff08;3…

快速傅里叶变换(FFT)——按频率抽取DIF的基

目录【1】回顾DIT【2】算法原理【3】运算特点【1】回顾DIT https://blog.csdn.net/qq_42604176/article/details/105559756 【2】算法原理 设序列点数&#xff1a;N2^M,M为正整数。将输入序列按照前一半、后一半分开。&#xff08;并非按照奇偶分&#xff09; 由于&#xf…