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

技术博客,转载请注明。

https://www.cnblogs.com/pingwen/p/12329168.html

OpenCV3入门(九)图像几何变换的更多相关文章

  1. C#数字图像处理算法学习笔记(三)--图像几何变换

    C#数字图像处理算法学习笔记(三)--图像几何变换 几何图像处理包括 图像的平移变换,镜像变换,旋转变换,伸缩变换,在这里仅以水平镜像为例,通过代码来理解其基本操作方式: 翻转前:

  2. 网络编程懒人入门(九):通俗讲解,有了IP地址,为何还要用MAC地址?

    1.前言 标题虽然是为了解释有了 IP 地址,为什么还要用 MAC 地址,但是本文的重点在于理解为什么要有 IP 这样的东西.本文对读者的定位是知道 MAC 地址是什么,IP 地址是什么. (本文同步 ...

  3. C#基础入门 九

    C#基础入门 九 集合 对于很多应用程序,需要创建和管理相关对象组,有两种方式可以将对象分组,一是创建对象数组,如 object[] obj=new object[3]{1,2.33,"st ...

  4. cesium编程入门(九)实体 Entity

    cesium编程入门(九)实体 Entity 在cesium编程入门(五)绘制形状提到过添加实体的方法,这一节聊一聊实体相关的一些内容: 先来看 Entity 的各个属性 id 唯一标志,如果没设置, ...

  5. matlab之原始处理图像几何变换

    (一)图像几何变换理论知识 (1)图像的平移与比例 图像的平移很简单,平移前后的坐标分别为(x,y)和(x',y'),则满足的关系式为 x'= x +Tx: y'= y +Ty: 其中Tx与Ty分别为 ...

  6. OpenCV3入门(四)图像的基础操作

    1.访问图像像素 1)灰度图像 2)彩色图像 OpenCV中的颜色顺序是BGR而不是RGB. 访问图像的像素在OpenCV中就是访问Mat矩阵,常用的有三种方法. at定位符访问 Mat数据结构,操作 ...

  7. OpenCV3入门(六)图像滤波

    1.图像滤波理论 1.1图像滤波理论 图像滤波即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作.消除图像中的噪声又叫做图像滤波或平滑,滤波的目的有两个,一是突出特 ...

  8. OpenCV3入门(七)图像形态学

    1.膨胀 所谓的图片的膨胀处理,其实就是在图像的边缘添加像素值,使得整体的像素值扩张,进而达到图像的膨胀效果. 对Z2上元素集合A和结构体元素S,使用S对A进行腐蚀,记作: A⊕S={z|(S)z ∩ ...

  9. OpenCV3入门(八)图像边缘检测

    1.边缘检测基础 图像的边缘是图像的基本特征,边缘点是灰度阶跃变化的像素点,即灰度值的导数较大或极大的地方,边缘检测是图像识别的第一步.用图像的一阶微分和二阶微分来增强图像的灰度跳变,而边缘也就是灰度 ...

随机推荐

  1. python专题我对json的日常操作

    一前言 本篇文章将会阐述对json的日常操作,如何读取json文件,将json文件转为字典:如何将字典转为json,将字典写入文件等: 二 josn数据格式简要说明 json对于初学者可以理解是一种数 ...

  2. js去除字符串的前后空白

    使用gravatar生成邮箱的头像时需要对邮箱地址哈希化,其中需要去除邮箱地址的首尾空白,查找了一些资料,总结一下 使用 js 提供的函数 trim() trim() 方法会删除一个字符串两端的空白字 ...

  3. Inception V1、V2、V3和V4

    Inception模块分为V1.V2.V3和V4. V1(GoogLeNet)的介绍 论文:Going deeper with convolutions 论文链接:https://arxiv.org/ ...

  4. 《【面试突击】— Redis篇》-- Redis的线程模型了解吗?为啥单线程效率还这么高?

    能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! <[面试突击]— Redis篇>-- Redis的线程模型了解吗?为啥单线程效率还这 ...

  5. Nginx的踩坑实录

    1.昨天在为一个新项目配置地址转发,搞了很久都没生效,日志也没有问题,但就是没到转发的目标机器上. nginx.conf 配置如下: location /prism{ proxy_pass http: ...

  6. python 遍历文件夹下的所有文件

    基础 import os # 遍历文件夹 def walkFile(file): for root, dirs, files in os.walk(file): # root 表示当前正在访问的文件夹 ...

  7. 2.Java程序运行机制

    1.编译型:compile,把所有东西翻译好,若有更新需要重新全部翻译.执行速度够快,对操作系统要求比较低.开发操作系统时用到编译型语言,例如c/c++...运行时需编译整个程序 2.解释性:运行一个 ...

  8. 前端之css的基本使用(一),行内、内部、外部样式,语法格式、注释、选择符、属性等

    一.行内.内部.外部样式 1.行内样式 <!DOCTYPE html> <html lang="en"> <head> <meta cha ...

  9. Qt Installer Framework翻译(7-3)

    控制脚本 对于每个安装程序,您可以指定一个控制脚本,用来与安装程序的部分UI或功能进行交互.控制脚本可以在向导中添加和删除页面,更改现有页面,进行附加检查以及通过模拟用户单击来与UI交互.例如,这允许 ...

  10. 使用Rclone和WinFsp挂载FTP为磁盘

    介绍 Rclone:是一款的命令行工具,支持在不同对象存储.网盘间同步.上传.下载数据.官网网址:rclone.org WinFsp:是一款Windows平台下的文件系统代理软件(Windows Fi ...