OpenCV3入门(九)图像几何变换
1、图像缩放
假设图像x轴的缩放因子Sx, y轴方向的缩放因子Sy,相应的变换表达式为:

函数原型为:
CV_EXPORTS_W void resize( InputArray src, OutputArray dst,
Size dsize, double fx = , double fy = ,
int interpolation = INTER_LINEAR );
示例程序如下。
img = imread("D:\\WORK\\5.OpenCV\\LeanOpenCV\\pic_src\\pic9.bmp");
imshow("原图", img);
resize(img, img2, Size(), 0.5, 0.5);
imshow("缩放图1", img2);
resize(img, img3, Size(), 0.8, 0.5);
imshow("缩放图2", img3);
运行效果如下图。

resize(img, img2, Size(), 1.2, 1.2);

2、图像平移
假设图像x轴的平移量Tx, y轴方向的平移量Ty,相应的变换表达式为:

仿射变换的原理为:
dst(x,y)=src(M11x+M12y+M13,M21x+M22y+M23)
平移操作可以使用OpenCV的仿射变换函数来实现,使用的变换矩阵为:

函数原型为:
CV_EXPORTS_W void warpAffine( InputArray src, OutputArray dst,
InputArray M, Size dsize,
int flags = INTER_LINEAR,
int borderMode = BORDER_CONSTANT,
const Scalar& borderValue = Scalar());
图像平移示例。
img = imread("D:\\WORK\\5.OpenCV\\LeanOpenCV\\pic_src\\pic9.bmp", IMREAD_GRAYSCALE);
imshow("原图", img);
// x轴平移20,y轴平移10, 2 * 3矩阵
Mat M = Mat::zeros(, , CV_32FC1);
M.at<float>(, ) = ;
M.at<float>(, ) = ;
M.at<float>(, ) = ;
M.at<float>(, ) = ;
warpAffine(img, img2, M, img.size());
imshow("平移图", img2);

3、图像旋转
假设点P0(x0,y0),角度为a,令L=|OP|=sqrt(x*x + y*y).
P0旋转b度到P1(x1,y1),则
x1=L*cos(a+b)=L* cos(a)*cos(b) – L*sin(a)*sin(b) = x0*cos(b) - y0*sin(b)
y1=L*sin(a+b)=L* sin(a)*cos(b) + L*cos(a)*sin(b) = y0*cos(b) + x0*sin(b)
OpenCV内置仿射变换的旋转函数,支持任意点为中心的图像旋转,函数原型为:
CV_EXPORTS_W Mat getRotationMatrix2D( Point2f center, double angle, double scale );
示例代码如下。
img = imread("D:\\WORK\\5.OpenCV\\LeanOpenCV\\pic_src\\pic9.bmp");
imshow("原图", img);
Point center = Point(img.cols / , img.rows / );
Mat m1= getRotationMatrix2D(center, , 1.0);
Mat m2 = getRotationMatrix2D(center, , 0.7);
Mat m3 = getRotationMatrix2D(center, , 1.2);
warpAffine(img, img1, m1, img.size());
warpAffine(img, img2, m2, img.size());
warpAffine(img, img3, m3, img.size());
imshow("img1", img1);
imshow("img2", img2);
imshow("img3", img3);

修改旋转角度效果如下图。
Mat m1= getRotationMatrix2D(center, 180, 1.0);
Mat m2 = getRotationMatrix2D(center, 270, 0.7);

如果旋转点的坐标原点不在图片中心,则图片绕着指定点旋转。
Point center = Point(, ); Mat m1= getRotationMatrix2D(center, , 1.0); Mat m2 = getRotationMatrix2D(center,-, 1.0);
对应的矩阵为:
m1=
[0.8660254037844387, 0.4999999999999999, 0;
-0.4999999999999999, 0.8660254037844387, 0]
m2=
[0.7071067811865476, -0.7071067811865476, 0;
0.7071067811865476, 0.7071067811865476, 0]
输出效果如下图。

4、图像重映射
重映射就是把一个图像中一个为之的像素放置到另一个图片指定位置过程。由于映射后的图像在原图中可能没有对应的整数坐标点像素,所以为了完成重映射需要做一些插值作为非整数像素坐标。我们通过重映射来表达每个像素的位置(x, y):g(x, y)=f(h(x,y))
OpenCV使用remap函数完成重映射功能,函数原型为:
CV_EXPORTS_W void remap( InputArray src, OutputArray dst,
InputArray map1, InputArray map2,
int interpolation, int borderMode = BORDER_CONSTANT,
const Scalar& borderValue = Scalar());
测试代码如下。
void update_map(int ind, Mat &map_x, Mat &map_y)
{
for (int i = ; i < map_x.rows; i++)
{
for (int j = ; j < map_x.cols; j++)
{
switch (ind)
{
case :
if (j > map_x.cols*0.25 && j < map_x.cols*0.75 && i > map_x.rows*0.25 && i < map_x.rows*0.75)
{
map_x.at<float>(i, j) = * (j - map_x.cols*0.25f) + 0.5f;
map_y.at<float>(i, j) = * (i - map_x.rows*0.25f) + 0.5f;
}
else
{
map_x.at<float>(i, j) = ;
map_y.at<float>(i, j) = ;
}
break;
case :
map_x.at<float>(i, j) = (float)j;
map_y.at<float>(i, j) = (float)(map_x.rows - i);
break;
case :
map_x.at<float>(i, j) = (float)(map_x.cols - j);
map_y.at<float>(i, j) = (float)i;
break;
case :
map_x.at<float>(i, j) = (float)(map_x.cols - j);
map_y.at<float>(i, j) = (float)(map_x.rows - i);
break;
default:
break;
} // end of switch
}
}
} int main() {
Mat src = imread("D:\\WORK\\5.OpenCV\\LeanOpenCV\\pic_src\\pic9.bmp");
imshow("原图", src); Mat dst(src.size(), src.type());
Mat map_x(src.size(), CV_32FC1);
Mat map_y(src.size(), CV_32FC1); update_map(, map_x, map_y);
remap(src, img1, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(, , ));
imshow("img1", img1); update_map(, map_x, map_y);
remap(src, img2, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(, , ));
imshow("img2", img2); update_map(, map_x, map_y);
remap(src, img3, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(, , ));
imshow("img3", img3); update_map(, map_x, map_y);
remap(src, img4, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(, , ));
imshow("img4", img4); waitKey();
}
输入图像:

代码实现四种remap效果。

remap后的图像:

例子2:x轴不压缩,y轴按照一元二次曲线进行压缩,对称抽为src.rows / 2。当y= src.rows / 2,对应变换前的图坐标src.rows,所以图像被压缩。当y=[ src.rows / 2,src.rows]时,y轴被反转。
for (int i = ; i < src.rows; i++)
{
for (int j = ; j < src.cols; j++)
{
map_x.at<float>(i, j) = j;
map_y.at<float>(i, j) = (float)((- * pow(i- src.rows / , ) / pow(src.rows / , )) + ) * src.rows;
}
}

5、参考文献
1、《OpenCV3 编程入门》,电子工业出版社,毛星雨著
2、《学习OpenCV》,清华大学出版社,Gary Bradski, Adrian kaehler著
3、Remapping
https://docs.opencv.org/3.4/d1/da0/tutorial_remap.html
4、OpenCV图像旋转
https://www.cnblogs.com/konglongdanfo/p/9135501.html
技术博客,转载请注明。
OpenCV3入门(九)图像几何变换的更多相关文章
- C#数字图像处理算法学习笔记(三)--图像几何变换
C#数字图像处理算法学习笔记(三)--图像几何变换 几何图像处理包括 图像的平移变换,镜像变换,旋转变换,伸缩变换,在这里仅以水平镜像为例,通过代码来理解其基本操作方式: 翻转前:
- 网络编程懒人入门(九):通俗讲解,有了IP地址,为何还要用MAC地址?
1.前言 标题虽然是为了解释有了 IP 地址,为什么还要用 MAC 地址,但是本文的重点在于理解为什么要有 IP 这样的东西.本文对读者的定位是知道 MAC 地址是什么,IP 地址是什么. (本文同步 ...
- C#基础入门 九
C#基础入门 九 集合 对于很多应用程序,需要创建和管理相关对象组,有两种方式可以将对象分组,一是创建对象数组,如 object[] obj=new object[3]{1,2.33,"st ...
- cesium编程入门(九)实体 Entity
cesium编程入门(九)实体 Entity 在cesium编程入门(五)绘制形状提到过添加实体的方法,这一节聊一聊实体相关的一些内容: 先来看 Entity 的各个属性 id 唯一标志,如果没设置, ...
- matlab之原始处理图像几何变换
(一)图像几何变换理论知识 (1)图像的平移与比例 图像的平移很简单,平移前后的坐标分别为(x,y)和(x',y'),则满足的关系式为 x'= x +Tx: y'= y +Ty: 其中Tx与Ty分别为 ...
- OpenCV3入门(四)图像的基础操作
1.访问图像像素 1)灰度图像 2)彩色图像 OpenCV中的颜色顺序是BGR而不是RGB. 访问图像的像素在OpenCV中就是访问Mat矩阵,常用的有三种方法. at定位符访问 Mat数据结构,操作 ...
- OpenCV3入门(六)图像滤波
1.图像滤波理论 1.1图像滤波理论 图像滤波即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作.消除图像中的噪声又叫做图像滤波或平滑,滤波的目的有两个,一是突出特 ...
- OpenCV3入门(七)图像形态学
1.膨胀 所谓的图片的膨胀处理,其实就是在图像的边缘添加像素值,使得整体的像素值扩张,进而达到图像的膨胀效果. 对Z2上元素集合A和结构体元素S,使用S对A进行腐蚀,记作: A⊕S={z|(S)z ∩ ...
- OpenCV3入门(八)图像边缘检测
1.边缘检测基础 图像的边缘是图像的基本特征,边缘点是灰度阶跃变化的像素点,即灰度值的导数较大或极大的地方,边缘检测是图像识别的第一步.用图像的一阶微分和二阶微分来增强图像的灰度跳变,而边缘也就是灰度 ...
随机推荐
- Ncverilog 仿真quartus generate IP的要点
Ncverilog 仿真quartus generate IP的要点 最近利用quartus II 生成plll 的IP,利用nclaunch 仿真的时候老是报错, 提示unresolved in w ...
- 自定义实现的ArrayList以及自定义实现的Iterator迭代器
ArrayList的底层是长度可动态变化的数组,其适用于查找多,修改少的情况,原因是数组的元素的增加删除元素会涉及大量元素的移动,效率比较低,ArrayList中的元素可以重复,与插入时的顺序相同,可 ...
- linux下解压缩文件中文乱码问题的解决
在windows上压缩的文件,是以系统默认编码中文来压缩文件.由于zip文件中没有声明其编码,所以linux上的unzip一般以默认编码解压,中文文件名会出现乱码. 虽然2005年就有人把这报告为bu ...
- 通过ArcGIS将数据存储到SQL Server2012中
一.软件安装: ARCGIS 10.3安装 SQLserver2012安装 ARCGIS 10.3 安装(注意ARCGIS10.3并不用安装配置ARCSDE). https://wenku.baidu ...
- 2018 Multi-University Training Contest 10
Recently, TeaTree acquire new knoledge gcd (Greatest Common Divisor), now she want to test you. As ...
- hive 动态分区
非常重要的动态分区属性: hive.exec.dynamic.partition 是否启动动态分区.false(不开启) true(开启)默认是 false hive.exec.dynamic.pa ...
- 20190918Java课堂记录
1. EnumTest.java public class EnumTest { public static void main(String[] args) { Size s=Size.SMALL; ...
- 系列免费课程汇总(Java、单体应用、微服务、物联网、SaaS)
概述 2020年春节尽在眼前,又忙碌了一年的你一定有很多收获:是升职加薪,还是收获爱情?是买房置业,还是新添人口? 我在2019年的最大收获是:我的第二枚千金诞生,使我顺利加入富豪行列! 新年伊始我们 ...
- spring boot配置spring-data-jpa的时候报错CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.NoSuchMethodError
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager f ...
- 异步查询转同步加redis业务实现的BUG分享
在最近的性能测试中,某一个查询接口指标不通过,开发做了N次优化,最终的优化方案如下:异步查询然后转同步,再加上redis缓存.此为背景. 在测试过程中发现一个BUG:同样的请求在第一次查询结果是OK的 ...