【图像处理】基于OpenCV底层实现的图片旋转
image processing 系列
图片旋转,本质上是对旋转后的图片中每一个像素点计算在原图的位置。然后照搬过来就好。
(多说一句。假设计算出来在原图中的位置不是整数而是小数,由于像素点个数都是整数,就须要小数到整数的转换。
这个转换过程是有讲究的,须要用到插值:近期邻插值、双线性插值等等。这里我使用的是最简单的近期邻插值。即对小数四舍五入成整数。C/C++ 实现四舍五入见这里)
完整 github 代码:image-processing (里面同一时候包括OSTU / 大津算法、直方图均衡化、滤波器等算法。还包括两种測试图片)。
图形图像课上通常会介绍旋转变换矩阵,当中 t 为须要旋转的角度,[x'; y']是变换后坐标(当中分号表示上下关系):
即表示为:[x'; y'] = [cos(t) sin(t); -sin(t) cos(t)][x; y]
由于我个人兴趣爱好(放P就是老师逼的。。。)。不同意使用 OpenCV 封装好的旋转函数。仅仅能自己实现,我開始的想法是:先求变换矩阵逆矩阵。然后将一张全黑图中每一个点一一相应插值到原图中。
结果发现转换后图片全黑了……
后来发现原点设置不正确。用OpenCV中的Mat格式存储(或二维数组)的图片。原点在左上角。
可是想要实现的旋转原点在图片中心。
同一时候。Mat格式存储(或二维数组)的坐标系中y轴正方向向下。这样人类视觉上的顺时针旋转,在二维数组的坐标系中是逆时针旋转。
最重要的一点,也是二维数组操作中极易忽略的一点:数组操作的是数组下标,不是坐标系(数组的行数rows是矩形的宽width。列数cols是矩形的长length)。比方坐标系(此时为了更贴近数组布局,我们如果
y 轴坐标系是向下的)中,矩形顶点是:
可是在数组中。由于是行优先,所以四个点的下标取值为:
有没有发现,两种坐标是相反的!
总结下来,我们的图片旋转须要注意下面几点:
- 变换后图片中的每一个像素点(i; j),须要平移到相对旋转中心的新坐标,即(i - Mat.rows/2; j - Mat.cols/2)。计算完毕之后,须要再次还原到相对左上角原点的旧坐标。
- 本来须要 变换后图片 乘以 原图变换矩阵的逆矩阵 相应到原图中坐标。可是由于y轴方向向下,所以 变换后图片 乘以原图变换矩阵(无需逆矩阵) 就可以相应到原图中坐标(顺时针旋转50度,还原操作是逆时针旋转50度)。
- 矩阵下标与原图变换矩阵相乘之前。须要将矩阵下标两值互换。相乘之后,须要再次互换下标值还原成矩阵下标。
因此对于一个经过旋转 t 度之后数组下标为[m‘, n’]的像素值,还原成原图中的数组下标[m; n]计算为:
[cos(t) -sin(t); sin(t) cos(t)] ([m'; n'] - [Mat.rows/2; Mat.cols/2]) = [m; n] - [Mat.rows/2; Mat.cols/2]
源码附上:
Mat nearestNeighRotate(cv::Mat img, float angle)
{
int len = (int)(sqrtf(pow(img.rows, 2) + pow(img.cols, 2)) + 0.5); Mat retMat = Mat::zeros(len, len, CV_8UC3);
float anglePI = angle * CV_PI / 180;
int xSm, ySm; for(int i = 0; i < retMat.rows; i++)
for(int j = 0; j < retMat.cols; j++)
{
xSm = (int)((i-retMat.rows/2)*cos(anglePI) - (j-retMat.cols/2)*sin(anglePI) + 0.5);
ySm = (int)((i-retMat.rows/2)*sin(anglePI) + (j-retMat.cols/2)*cos(anglePI) + 0.5);
xSm += img.rows / 2;
ySm += img.cols / 2; if(xSm >= img.rows || ySm >= img.cols || xSm <= 0 || ySm <= 0){
retMat.at<Vec3b>(i, j) = Vec3b(0, 0);
}
else{
retMat.at<Vec3b>(i, j) = img.at<Vec3b>(xSm, ySm);
}
} return retMat;
}
好,我们来測试看看:
int main()
{
Mat img = imread("../HelloWorld.png");
retImg = nearestNeighRotate(img, -20.f);
namedWindow("nearNeigh", CV_WINDOW_AUTOSIZE);
imshow("nearNeigh", retImg); waitKey();
cvDestroyAllWindows();
return 0;
}
结果(旋转了20度)为
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXJvbnlvdW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
【图像处理】基于OpenCV底层实现的图片旋转的更多相关文章
- 基于OpenCv和swing的图片/视频展示Java实现
基于OpenCv和swing实现图片/视频的展示 图片的展示 swing展示图片,多为操作BufferedImage,这里要关注的核心是将Mat转为BufferedImage. 代码如下: publi ...
- 【图像处理】基于OpenCV底层实现的直方图匹配
image processing 系列: [图像处理]图片旋转 [图像处理]高斯滤波.中值滤波.均值滤波 直方图匹配算法.又称直方图规定化.简单说.就是依据某函数.或者另外一张图片的引导,使得原图改变 ...
- android图像处理系列之四-- 给图片添加边框(上)
图片处理时,有时需要为图片加一些边框,下面介绍一种为图片添加简单边框的方法. 基本思路是:将边框图片裁剪成八张小图片(图片大小最好一致,不然后面处理会很麻烦),分别对应左上角,左边,左下角,下边,右下 ...
- android图像处理系列之五-- 给图片添加边框(中)
前面一篇讲到给图片加边框的方式,只能给图片加一些有规则的边框,如果想加一些比较精美的效果,就有点麻烦了.下面就给出解决这个问题的思路. 思路是:一些比较精美的花边图片我们是很难用代码控制,就目前本人水 ...
- android图像处理系列之四--给图片添加边框(上)
图片处理时,有时需要为图片加一些边框,下面介绍一种为图片添加简单边框的方法. 基本思路是:将边框图片裁剪成八张小图片(图片大小最好一致,不然后面处理会很麻烦),分别对应左上角,左边,左下角,下边,右下 ...
- android图像处理系列之五--给图片添加边框(中)
前面一篇讲到给图片加边框的方式,只能给图片加一些有规则的边框,如果想加一些比较精美的效果,就有点麻烦了.下面就给出解决这个问题的思路. 思路是:一些比较精美的花边图片我们是很难用代码控制,就目前本人水 ...
- 基于 opencv 的图像处理入门教程
前言 虽然计算机视觉领域目前基本是以深度学习算法为主,但实际上很多时候对图片的很多处理方法,并不需要采用深度学习的网络模型,采用目前成熟的图像处理库即可实现,比如 OpenCV 和 PIL ,对图片进 ...
- OpenCV2学习笔记(十四):基于OpenCV卡通图片处理
得知OpenCV有一段时间.除了研究的各种算法的内容.除了从备用,据导游书籍和资料,尝试结合链接的图像处理算法和日常生活,第一桌面上(随着摄像头)完成了一系列的视频流处理功能.开发平台Qt5.3.2+ ...
- Python图像处理丨基于OpenCV和像素处理的图像灰度化处理
摘要:本篇文章讲解图像灰度化处理的知识,结合OpenCV调用cv2.cvtColor()函数实现图像灰度操作,使用像素处理方法对图像进行灰度化处理. 本文分享自华为云社区<[Python图像处理 ...
随机推荐
- TCP/IP 协议分层
协议分层 可能大家对OSI七层模型并不陌生,它将网络协议很细致地从逻辑上分为了7层.但是实际运用中并不是按七层模型,一般大家都只使用5层模型.如下: 物理层:一般包括物理媒介,电信号,光信号等,主要对 ...
- Perl语言入门:第七章习题:输出文件中包含一个大写字母的所有行,不输出一行的内容全是大写的
文件内容: bash-2.03$ cat file_4_ex_ch7.txt anonymous attribute demolition grammar rules indices refernce ...
- ionic提供的配色方案
.light #ffffff .stable #f8f8f8 .positive #387ef5 .calm #11c1f3 .balanced #33cd5f .energized #ffc900 ...
- Layui下拉选渲染
下拉选渲染有很多方式,这个比较简单,记录一下: HTML代码如下: <div class="layui-input-inline"> <input type=&q ...
- (error) MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on d
出现redis错误: (error) MISCONF Redis is configured to save RDB snapshots, but is currently not able to p ...
- zabbix源码安装后,设置为服务启动和关闭
zabbix源码安装,使用service启动与关闭服务 1. zabbix客户端的系统服务脚本 1.1 拷贝启动脚本 zabbix的源码提供了系统服务脚本,在/usr/local/src/zabbix ...
- JSON对象与JSON字符串之间的转换
JSON引用包:import net.sf.json 1.JSON字符串转JSON对象(例子中的AgencyExpand是java对象) JSONObject json = JSONObject.f ...
- 测试第一个Oracle存储过程
存储过程语句 //简单存储过程的例子 //每调用一次打印一次hello world create or replace procedure sayhelloworld as begin dbms_ou ...
- 对SpringMVC框架的理解(转)
SpringMVC概念: 他是一个轻量级的开源框架,应用于表现层,基于MVC的设计模式. SpringMVC的特点: 1.他是单例的可以设置成多例. 2.他的线程是安全的 ...
- 节点回来shard仍然delayed原因
1:es2 fetch shard data时,存在节点刚加入集群,还没有收到cluster metadata的情况.此时,节点因为没有该索引,返回的sharddata为empty,主节点缓存了该sh ...