image processing 系列

  1. 【图像处理】直方图匹配
  2. 【图像处理】高斯滤波、中值滤波、均值滤波

图片旋转,本质上是对旋转后的图片中每一个像素点计算在原图的位置。然后照搬过来就好。

(多说一句。假设计算出来在原图中的位置不是整数而是小数,由于像素点个数都是整数,就须要小数到整数的转换。

这个转换过程是有讲究的,须要用到插值:近期邻插值、双线性插值等等。这里我使用的是最简单的近期邻插值。即对小数四舍五入成整数。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 轴坐标系是向下的)中,矩形顶点是:

可是在数组中。由于是行优先,所以四个点的下标取值为:

有没有发现,两种坐标是相反的!

总结下来,我们的图片旋转须要注意下面几点:

  1. 变换后图片中的每一个像素点(i; j),须要平移到相对旋转中心的新坐标,即(i - Mat.rows/2; j - Mat.cols/2)。计算完毕之后,须要再次还原到相对左上角原点的旧坐标。
  2. 本来须要 变换后图片 乘以 原图变换矩阵的逆矩阵 相应到原图中坐标。可是由于y轴方向向下,所以 变换后图片 乘以原图变换矩阵(无需逆矩阵) 就可以相应到原图中坐标(顺时针旋转50度,还原操作是逆时针旋转50度)。
  3. 矩阵下标与原图变换矩阵相乘之前。须要将矩阵下标两值互换。相乘之后,须要再次互换下标值还原成矩阵下标。

因此对于一个经过旋转 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底层实现的图片旋转的更多相关文章

  1. 基于OpenCv和swing的图片/视频展示Java实现

    基于OpenCv和swing实现图片/视频的展示 图片的展示 swing展示图片,多为操作BufferedImage,这里要关注的核心是将Mat转为BufferedImage. 代码如下: publi ...

  2. 【图像处理】基于OpenCV底层实现的直方图匹配

    image processing 系列: [图像处理]图片旋转 [图像处理]高斯滤波.中值滤波.均值滤波 直方图匹配算法.又称直方图规定化.简单说.就是依据某函数.或者另外一张图片的引导,使得原图改变 ...

  3. android图像处理系列之四-- 给图片添加边框(上)

    图片处理时,有时需要为图片加一些边框,下面介绍一种为图片添加简单边框的方法. 基本思路是:将边框图片裁剪成八张小图片(图片大小最好一致,不然后面处理会很麻烦),分别对应左上角,左边,左下角,下边,右下 ...

  4. android图像处理系列之五-- 给图片添加边框(中)

    前面一篇讲到给图片加边框的方式,只能给图片加一些有规则的边框,如果想加一些比较精美的效果,就有点麻烦了.下面就给出解决这个问题的思路. 思路是:一些比较精美的花边图片我们是很难用代码控制,就目前本人水 ...

  5. android图像处理系列之四--给图片添加边框(上)

    图片处理时,有时需要为图片加一些边框,下面介绍一种为图片添加简单边框的方法. 基本思路是:将边框图片裁剪成八张小图片(图片大小最好一致,不然后面处理会很麻烦),分别对应左上角,左边,左下角,下边,右下 ...

  6. android图像处理系列之五--给图片添加边框(中)

    前面一篇讲到给图片加边框的方式,只能给图片加一些有规则的边框,如果想加一些比较精美的效果,就有点麻烦了.下面就给出解决这个问题的思路. 思路是:一些比较精美的花边图片我们是很难用代码控制,就目前本人水 ...

  7. 基于 opencv 的图像处理入门教程

    前言 虽然计算机视觉领域目前基本是以深度学习算法为主,但实际上很多时候对图片的很多处理方法,并不需要采用深度学习的网络模型,采用目前成熟的图像处理库即可实现,比如 OpenCV 和 PIL ,对图片进 ...

  8. OpenCV2学习笔记(十四):基于OpenCV卡通图片处理

    得知OpenCV有一段时间.除了研究的各种算法的内容.除了从备用,据导游书籍和资料,尝试结合链接的图像处理算法和日常生活,第一桌面上(随着摄像头)完成了一系列的视频流处理功能.开发平台Qt5.3.2+ ...

  9. Python图像处理丨基于OpenCV和像素处理的图像灰度化处理

    摘要:本篇文章讲解图像灰度化处理的知识,结合OpenCV调用cv2.cvtColor()函数实现图像灰度操作,使用像素处理方法对图像进行灰度化处理. 本文分享自华为云社区<[Python图像处理 ...

随机推荐

  1. 【经验】停止Smart Card服务

    Windows+R键调出运行 输入 services.msc 有一项Smart Card的服务找到他->属性->启动类型(设置为禁用 )->确定,然后重新启动服务

  2. Java递归扫描文件路径

    import java.io.File; public class Test { public static int count = 0; public static void main(String ...

  3. 07JavaScript数组与字符串对象

    JavaScript数组与字符串对象 5.1.1数组(Array)对象 <script> //声明一个数组并赋值; var arr = new Array("aa",& ...

  4. 16第一章 ASP.Net编程基础知识

    第一章        ASP.Net编程基础知识 第一章        ASP.Net编程基础知识 本章首先介绍用ASP.Net技术编制服务器端动态网页所需的网络和HTML标记语言方面的有关知识.然后 ...

  5. 获取汉字首字母,拼音,可实现拼音字母搜索----npm js-pinyin

      npm install js-pinyin  main.js 引入 import pinyin from 'js-pinyin'   使用组件内 let pinyin = require('js- ...

  6. HDU - 4514 湫湫系列故事——设计风景线(并查集判环)

    题目: 随着杭州西湖的知名度的进一步提升,园林规划专家湫湫希望设计出一条新的经典观光线路,根据老板马小腾的指示,新的风景线最好能建成环形,如果没有条件建成环形,那就建的越长越好. 现在已经勘探确定了n ...

  7. javascript事件委托和jquery事件委托

    元旦过后,新年第一篇. 初衷:很多的面试都会涉及到事件委托,前前后后也看过好多博文,写的都很不错,写的各有千秋,自己思前想后,为了以后自己的查看,也同时为现在找工作的前端小伙伴提供一个看似更全方位的解 ...

  8. thinkphp 5.0整合阿里大于验证码短信发送接口,含完整模型验证实例DEMO

    为大家分享一个阿里大于短信发送接口: 首先创建一个发送模型(Send.php): <?php namespace app\index\model; use think\Validate; cla ...

  9. ubuntu 安装python 编程环境

    1. 安装python sudo add-apt-repository ppa:fkrull/deadsnakessudo apt-get updatesudo apt-get install pyt ...

  10. Python之面向对象元类

    Python之面向对象元类 call方法: class People: def __init__(self,name): self.name=name # def __call__(self, *ar ...