opencv——几何变换原理与实现
摘要
图像几何变换又称为图像空间变换, 它将一幅图像中的坐标位置映射到另一幅图像中的新坐标位置。几何变换不改变图像的像素值, 只是在图像平面上进行像素的重新安排。
几何变换大致分为仿射变换、投影变换、极坐标变换,完成几何变换需要两个独立的算法过程:
1、一个用来实现空间坐标变换的算法,用它描述每个像素如何从初始位置移动到终止位置
2、一个插值算法完成输出图像的每个像素的灰度值
放射变换
首先,先来分析一下放射变换的原理:
什么是放射变换?
仿射变换是从一个二维坐标系变换到另一个二维坐标系,属于线性变换。通过已知3对坐标点可以求得变换矩阵。(不共线的三对对应点,决定了唯一的变换矩阵)
其中
就是仿射变换矩阵一般形式,根据不同的变换,比如平移、缩放、旋转等等,仿射变换矩阵的值是不一样的。
1、平移
假设空间坐标 (x,y)先沿 x轴平移tx ,在沿 y轴平移 ty,则变换后的坐标为(x+tx,y+ty) ,此时平移变换为:
2、缩放
二维空间坐标 (x,y) 以任意一点 (x0,y0) 为中心在水平方向和垂直方向上分别缩放 sx和 sy倍,缩放后坐标为 (x0+sx (x-x0),y0+sy(y-y0)) ,通俗来讲就是缩放后的坐标离中心点的水平距离变为原坐标离中心点水平距离的 sx 倍。
当 (x0,y0) 为原点 (0,0) 时,缩放变换可以表示为:
当 (x0,y0) 为任意点 时,变换过程理解为,先将中心点平移到原点,再以原点为中心进行缩放,然后移回到原来的中心点。缩放变换表示为:
注意:等式右边的运算应该从右往左看
3、旋转
顺时针绕原点(0,0)旋转变换的矩阵表示为:
若以任意一点 (x0,y0) 为中心旋转,相当于先将原点移动到旋转中心,然后绕原点旋转,最后移回坐标原点,用矩阵表示为:
注意:上面的运算顺序是从右向左的。
OpenCV提供的旋转函数,实现顺时针90°、180°、270°的旋转
rotate(InputArray src, Output dst, int rotateCode) rotateCode有以下取值:
ROTATE_90_CLOCKWISE //顺时针旋转90度
ROTATE_180 //顺时针旋转180度
ROTATE_90_COUNTERCLOCKWISE //逆时针旋转90度
flip(src, dst, int flipCode)
实现了图像的水平镜像、垂直镜像和逆时针旋转180°,不过并不是通过仿射变换实现的,而是通过行列互换,它与rotate()
、transpose()
函数一样都在core.hpp头文件中。求解放射变换矩阵
以上都是知道变换前坐标求变换后的坐标,如果我们已经知道了变换前的坐标和变换后的坐标,想求出仿射变换矩阵,可以通过解方程法或矩阵法。
解方程法
由于仿射变换矩阵
有6个未知数,所以我们只需三组坐标列出六个方程即可。OpenCV提供函数getAffineTransform(src, dst)
通过方程法求解,其中src和dst分别为前后坐标。
对于C++来说,一种方式是将坐标存在Point2f数组中,另一种方法是保存在Mat中:
// 第一种方法
Point2f src1[] = {Pointy2f(0, 0), Point2f(200, 0), Point2f(0, 200)};
Point2f dst1[] = {Pointy2f(0, 0), Point2f(100, 0), Point2f(0, 100)};
// 第二种方法
Mat src2 = (Mat_<float>(3, 2) << 0, 0, 200, 0, 0, 200);
Mat dst2 = (Mat_<float>(3, 2) << 0, 0, 100, 0, 0, 100); Mat A = getAffineTransform(src1, dst1);
矩阵法
对于等比例缩放的仿射变换,OpenCV提供函数getRotationMatrix2D
(center, angle, scale)
来计算矩阵,center是变换中心;angle是逆时针旋转的角度,(opencv中正角度代表逆时针旋转);scale是等比例缩放的系数。
我们通过下面的代码来定义这些参数,例如:
Point center = Point( src.cols/2, src.rows/2 ); //中心点
double angle = -50.0;
double scale = 0.6;
插值算法分析
在运算中,我们可能会遇到目标坐标有小数的情况,比如将坐标(3,3)缩放2倍变为了(1.5,1.5),但是对于图像来说并没有这个点,这时候我们就要用周围坐标的值来估算此位置的颜色,也就是插值。
(1)最近邻插值(INTER_NEAREST)
最近邻插值就是从(x,y)的四个相邻坐标中找到最近的那个来当作它的值,如(2.3,4.7),它的相邻坐标分别为(2,4)、(3,4)、(2,5)、(3,5),计算这几个相邻坐标与(2.3,4.7)坐标的距离,若最近的为(2,5),则取(2,5)的颜色值为的(2.3,4.7)值。
此种方法得到的图像会出现锯齿状外观,对于放大图像则更明显。
(2)双线性插值(INTER_LINEAR)(最常用)
要估计输入图像非整数坐标 的值,分为三个步骤:
第一步:先用线性关系估计输入图像中 的值
第二步:同理用线性关系估计输入图像中 的值
第三步:根据前两步得到的值,用线性关系估计输入图像中 的值
进行放射变换
将刚刚求得的仿射变换矩阵应用到原图像,OpenCV提供函数warpAffine进行放射变换。
warpAffine(src,dst,M,dsize,flags,bordMode, borderValue)
//src:输入图像
//dst:输出变换后图像,需要初始化一个空矩阵用来保存结果,不用设定矩阵尺寸
//M:2行3列的仿射变换矩阵
//dsize: 二元元组(宽,高),代表输出图像大小
//flags: 插值法 INTER_NEAREST、INTER_LINEAR(默认线性插值)
//bordMode: 边界像素模式,默认值BORDER_CONSTANT
//bordValue: 当填充模式为BORDER_CONSTANT时的填充值(默认为Scalar(),即0)
opencv实现放射变换
Mat src, dst_warp1, dst_warp2;
Point2f srcPoints[3];//原图中的三点 (一个包含三维点(x,y)的数组,其中x、y是浮点型数)
Point2f dstPoints[3];//目标图中的三点 int main(int argc, char** argv)
{
src = imread("D:/opencv练习图片/薛之谦.jpg");
namedWindow("SrcImage");
imshow("SrcImage", src);
//第一种仿射变换的调用方式:三点法
//三个点对的值,上面也说了,只要知道你想要变换后图的三个点的坐标,就可以实现仿射变换
srcPoints[0] = Point2f(0, 0);
srcPoints[1] = Point2f(0, src.rows);
srcPoints[2] = Point2f(src.cols, 0);
//映射后的三个坐标值
dstPoints[0] = Point2f(0, src.rows*0.3);
dstPoints[1] = Point2f(src.cols*0.25, src.rows*0.75);
dstPoints[2] = Point2f(src.cols*0.75, src.rows*0.25);
Mat M1 = getAffineTransform(srcPoints, dstPoints);//由三个点对计算变换矩阵
warpAffine(src, dst_warp1, M1, src.size());//仿射变换 //第二种仿射变换的调用方式:直接指定角度和比例
//旋转加缩放
Point2f center(src.cols / 2, src.rows / 2);//旋转中心
double angle = 45;//逆时针旋转45度
double scale = 0.5;//缩放比例
Mat M2 = getRotationMatrix2D(center, angle, scale);//计算旋转加缩放的变换矩阵
warpAffine(src, dst_warp2, M2, Size(src.cols, src.rows), INTER_LINEAR);//仿射变换
imshow("第一种放射变换", dst_warp1);
imshow("第二种放射变换", dst_warp2);
waitKey(0);
return 0;
}
投影变换(透视变换)

OpenCV提供函数getPerspectiveTransform(src, dst)
实现求解投影矩阵,需要输入四组对应的坐标。
getPerspectiveTransform(const Point2f* src, const Point2f* dst)
//参数const Point2f* src:原图的四个固定顶点
//参数const Point2f* dst:目标图像的四个固定顶点
//返回值:Mat型变换矩阵,可直接用于warpAffine()函数
//注意,顶点数组长度超4个,则会自动以前4个为变换顶点;数组可用Point2f[]或Point2f*表示
OpenCV提供函数warpPerspective
实现投影变换,参数说明和仿射变化类似。
warpPerspective(src,dst,M,dsize,flags,bordMode, borderValue)
//src:输入图像
//dst:输出变换后图像,需要初始化一个空矩阵用来保存结果,不用设定矩阵尺寸
//M:2行3列的仿射变换矩阵
//dsize: 二元元组(宽,高),代表输出图像大小
//flags: 插值法 INTER_NEAREST、INTER_LINEAR(默认线性插值)
//bordMode: 边界像素模式,默认值BORDER_CONSTANT
//bordValue: 当填充模式为BORDER_CONSTANT时的填充值(默认为Scalar(),即0)
opencv实现投影变换:
Mat src, dst_warp1;
Point2f srcPoints[4];//原图中的三点 (一个包含三维点(x,y)的数组,其中x、y是浮点型数)
Point2f dstPoints[4];//目标图中的三点 int main(int argc, char** argv)
{
src = imread("D:/opencv练习图片/薛之谦.jpg");
namedWindow("SrcImage");
imshow("SrcImage", src);
//变换前四点坐标
srcPoints[0] = Point2f(0, 0);
srcPoints[1] = Point2f(0, src.rows);
srcPoints[2] = Point2f(src.cols, 0);
srcPoints[3] = Point2f(src.cols, src.rows);
//变换后的四点坐标
dstPoints[0] = Point2f(src.cols*0.1, src.rows*0.1);
dstPoints[1] = Point2f(0, src.rows);
dstPoints[2] = Point2f(src.cols, 0);
dstPoints[3] = Point2f(src.cols*0.7, src.rows*0.8);
Mat M1 = getPerspectiveTransform(srcPoints, dstPoints);//由四个点对计算透视变换矩阵
warpPerspective(src, dst_warp1, M1, src.size());//投影变换
imshow("投影变换(四点法)", dst_warp1);
waitKey(0);
return 0;
}
极坐标变换
通常通过极坐标变化校正图像中的圆形物体或包含在圆环中的物体。
OpenCV 中提供了warpPolar()函数用于实现图像的极坐标变换,该函数的函数原型如下:
warpPolar( src, dst, Size dsize, Point2f center, double maxRadius, int flags )
- src:原图像,可以是灰度图像或者彩色图像
dst:极坐标变换后输出图像(与原图像具有相同的数据类型和通道数)。
dsize:输出图像大小。(自行决定)
center:极坐标变换时极坐标原点在原图像中的位置。
maxRadius:变换时边界圆的半径,它也决定了逆变换时的比例参数。
flags: 插值方法与极坐标映射方法标志,两个方法之间通过“+”或者“|”号进行连接。
warpPolar()函数极坐标映射方法标志:
WARP_POLAR_LINEAR //极坐标正变换(直角坐标变换到极坐标)
WARP_POLAR_LOG //半对数极坐标变换
WARP_INVERSE_MAP //逆变换(极坐标变换到直角坐标)
opencv实现极坐标变换:
Mat src, dst;
int main(int argc, char** argv)
{
src = imread("D:/opencv练习图片/环形字符.jpg");
namedWindow("SrcImage");
imshow("SrcImage", src);
Point2f center = Point2f(src.cols / 2, src.rows / 2); //极坐标在图像中的原点
// 圆的半径
double maxRadius = min(center.y, center.x);
//正极坐标变换
warpPolar(src, dst, Size(200, 500), center, maxRadius, INTER_LINEAR | WARP_POLAR_LINEAR);
// 改变结果方向
rotate(dst, dst, ROTATE_90_COUNTERCLOCKWISE);
imshow("极坐标变换", dst);
waitKey(0);
return 0;
}
参考链接:(8条消息) 【OpenCV学习笔记】之仿射变换(Affine Transformation)_zhu_hongji的博客-CSDN博客_仿射变换
【从零学习OpenCV 4】图像极坐标变换 - 知乎 (zhihu.com)
OpenCV算法学习笔记之几何变换 - 简书 (jianshu.com)
opencv——几何变换原理与实现的更多相关文章
- OpenCV SIFT原理与源码分析
http://blog.csdn.net/xiaowei_cqu/article/details/8069548 SIFT简介 Scale Invariant Feature Transform,尺度 ...
- opencv 边缘检测原理
只是实现一下,暂不考虑效率 import cv2 as cv import numpy as np import math # 从源码层面实现边缘检测 img = cv.imread('../imag ...
- OpenCV图像金字塔
图像金字塔 目标 本文档尝试解答如下问题: 如何使用OpenCV函数 pyrUp 和 pyrDown 对图像进行向上和向下采样. 原理 Note 以下内容来自于Bradski和Kaehler的大作: ...
- Opencv笔记(十五)——图像金字塔
参考文献 目标 学习图像金字塔 学习函数cv2.pyrUp()和cv2.pyrDown() 原理 当我们需要将图像转换到另一个尺寸的时候, 有两种可能,一种是放大图像,另一种是缩小图像.尽管在Open ...
- 学习OpenCV第0天
自2011年接触OpenCV已经有几年了,一直停留在写一些小程序,利用手冊完毕一些任务,一直没有深入研究当中代码,现在毕业,但各种原因未能进入图像处理行业,故现重学OpenCV,包含分析代码,学习算法 ...
- Python使用opencv
Python配置opencv 原理 Python调用opencv的原理是:opencv编译出共享库文件,python把这个共享库文件作为一个模块加载并使用. 通俗点就是,编译opencv的时候开启py ...
- OpenCV实现人脸检测
OpenCV实现人脸检测(转载) 原文链接:https://www.cnblogs.com/mengdd/archive/2012/08/01/2619043.html 本文介绍最基本的用OpenC ...
- opencv-图像金字塔
图像金字塔 目标 原理摘自:http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/pyramids/pyramids. ...
- 基于OpenCV进行图像拼接原理解析和编码实现(提纲 代码和具体内容在课件中)
一.背景 1.1概念定义 我们这里想要实现的图像拼接,既不是如题图1和2这样的"图片艺术拼接",也不是如图3这样的"显示拼接",而是实现类似"BaiD ...
随机推荐
- python实现通过URL下载图片到本地服务器
import os import urllib.request image_url = 'http://img.jingtuitui.com/759fa20190115144450401.jpg' f ...
- 攻防世界 reverse android-app-100
android-app-100 suctf-2016 jeb启动,找到点击事件: 验证流程: 输入作为参数 --> processObjectArrayFromNative 得到一返回值(r ...
- Ubuntu18.04安装MySQL(未设置密码或忘记密码)
一 安装MySQL sudo apt-get update sudo apt-get install mysql-server 二 密码问题 1 安装时提示设置密码 这种情况没什么问题,通过已下命令登 ...
- HashMap源码个人解读
HashMap的源码比较复杂,最近也是结合视频以及其余大佬的博客,想着记录一下自己的理解或者当作笔记 JDK1.8后,HashMap底层是数组+链表+红黑树.在这之前都是数组+链表,而改变的原因也就是 ...
- 基于react hooks,zarm组件库配置开发h5表单页面
最近使用React Hooks结合zarm组件库,基于js对象配置方式开发了大量的h5表单页面.大家都知道h5表单功能无非就是表单数据的收集,验证,提交,回显编辑,通常排列方式也是自上向下一行一列的方 ...
- Linux常用命令-基础部分
Linux介绍 Linux是一款开源的操作系统,免费,开源,安全,高效,处理高并发非常强悍,很多企业级开发项目都部署在Linux/UNIX上. 创始人:Linus Torvalds 林纳斯 Linux ...
- 前端开发面试题 — css篇
1.介绍一下标准的CSS的盒子模型?低版本IE的盒子模型有什么不同的? (1)有两种, IE 盒子模型.W3C 盒子模型: (2)盒模型: 内容(content).填充(padding).边界(mar ...
- "Unmapped Spring configuration files found.Please configure Spring facet."解决办法
最近在学习使用IDEA工具,觉得与Eclipse相比,还是有很多的方便之处. 但是,当把自己的一个项目导入IDEA之后,Event Log提示"Unmapped Spring configu ...
- 自动化kolla-ansible部署ubuntu20.04+openstack-victoria之镜像制作ubuntu16.04-16
自动化kolla-ansible部署ubuntu20.04+openstack-victoria之镜像制作ubuntu16.04-16 欢迎加QQ群:1026880196 进行交流学习 制作Ope ...
- 自动化kolla-ansible部署ubuntu20.04+openstack-victoria之文件配置-08
自动化kolla-ansible部署ubuntu20.04+openstack-victoria之文件配置-08 欢迎加QQ群:1026880196 进行交流学习 文件配置 #controller ...