0.概述

图像变换的基本原理都是找到原图和目标图的像素位置的映射关系,这个可以用坐标系来思考,在opencv中,

图像的坐标系是从左上角开始(0,0),向右是x增加方向(cols),向下时y增加方向(rows)。

普通坐标关系:

图像坐标关系:

1.图像的平移

图像的平移是比较简单的映射关系,对于原图像的某个像素点位置(X0,Y0),向右平移100个像素的话,变换之后的目标像素点位置(X = X0+100,Y),然后用原图像的像素值填充目标位置就可,因此我们需要将这种映射关系转换一下,方便获得原图像素值,也就是X0 = X-100,这里X是已知的。

具体代码如下:

void translation(cv::Mat & src, cv::Mat & dst, int dx, int dy)
{
const int rows = src.rows; // 获得原图的高度(y)
const int cols = src.cols; // 获得原图的宽度(x) dst.create(rows, cols, src.type()); // 按照原图大小和格式创建一个空白图 Vec3b *p;
for (int Y = 0; Y < rows; ++Y) // 按行扫描
{
p = dst.ptr<Vec3b>(Y); for (int X = 0; X < cols; ++X)
{
int X0 = X - dx; // 逆映射关系,求得原图的位置
int Y0 = Y - dy;
if (X0 >= 0 && Y0 >= 0 && X0 < cols && Y0 < rows) // 防止越界
{
p[X] = src.ptr<Vec3b>(Y0)[X0]; // 将原图的像素值赋给目标位置
}
}
} }

2.图像的缩放

这里暂时只贴出opencv的缩放接口:

void resize(InputArray src,  //输入图像
OutputArray dst, // 输出图像
Size dsize, // 指定的输出图像的大小
double fx=0, // 横向缩放比例
double fy=0, // 纵向缩放比例
int interpolation=INTER_LINEAR // 指定插值方式
);

3.图像的旋转

图像旋转矩阵的原理可以参考这里

基本映射关系:

我们只需要根据这个映射关系写就好,其中的dx和dy主要用来计算旋转中心的,如果都是0的话图像就是围绕

图像坐标(0,0)来旋转,该公式中的W'H'指的是目标图像的宽度和高度。

代码:

void rotation(cv::Mat & src, cv::Mat & dst, int angle, cv::Point center = cv::Point(0, 0))
{
// 计算角度的正余弦
float sint = sin(angle*3.141592653 / 180);
float cost = cos(angle*3.141592653 / 180); const int rows = src.rows; // rows == H (Y--->)
const int cols = src.cols; // cols == W (X--->) // 计算旋转中心的偏移
float centerxScale = (float)center.x / cols;
float centeryScale = (float)center.y / rows;
float dx = -centerxScale * cols*cost - centeryScale * rows*sint + centerxScale * cols; // 根据映射公式
float dy = centerxScale * cols*sint - centeryScale * rows*cost + centeryScale * rows; dst.create(rows, cols, src.type()); Vec3b *p;
for (int Y = 0; Y < rows; ++Y)
{
p = dst.ptr<Vec3b>(Y); for (int X = 0; X < cols; ++X)
{
int X0 = X*cost + Y*sint + dx; // 根据映射公式
int Y0 = -X*sint + Y*cost + dy;
if (X0 >= 0 && Y0 >= 0 && X0 < cols && Y0 < rows)
{
p[X] = src.ptr<Vec3b>(Y0)[X0];
}
}
} }

4.图像的翻转

这里也只贴opencv的接口:

void flip(InputArray src, // 原图像
OutputArray dst, //目标图像
int flipCode // 翻转方式,1:水平,0:垂直,-1:水平垂直
);

5.图像的错切

图像的错切效果可以想象伸缩门中的菱形的变化:

不过对于x方向的错切,y方向的高度并不会变化。

贴代码:

void shear(cv::Mat & src, cv::Mat & dst, float dx = 0,float dy = 0) // dx,dy为错切率
{ const int rows = src.rows; // rows == H (Y--->)
const int cols = src.cols; // cols == W (X--->) dst.create(rows, cols, src.type()); Vec3b *p;
for (int Y = 0; Y < rows; ++Y)
{
p = dst.ptr<Vec3b>(Y); for (int X = 0; X < cols; ++X)
{
int X0 = X + dx*Y;
int Y0 = Y + dy*X;
if (X0 >= 0 && Y0 >= 0 && X0 < cols && Y0 < rows)
{
p[X] = src.ptr<Vec3b>(Y0)[X0];
}
}
} }

效果图(dx = 0.1,dy=0.1):

6.图像的仿射变换

图像的仿射变换其实就是以上基本变换的组合,仿射变换可以维持原图的点线关系,例如平行和比例等。

示例代码:

#include <opencv.hpp>
#include <iostream>
#include <imgproc.hpp> using namespace std;
using namespace cv; int main()
{
Mat img = imread("img.jpg");
Mat dst; Point2f affinePoints0[3] = { Point2f(100, 50), Point2f(100, 390), Point2f(600, 50) }; // 选取原图像的映射点
Point2f affinePoints1[3] = { Point2f(200, 100), Point2f(200, 300), Point2f(500, 50) }; // 选取目标图像的映射点 Mat trans = getAffineTransform(affinePoints0, affinePoints1); // 获得变换矩阵 warpAffine(img, dst, trans, Size(img.cols, img.rows)); // 仿射变换 for (int i = 0; i < 3; ++i) // 描点
{
circle(img, affinePoints0[i], 5, Scalar(0, 255, 255), -1);
circle(dst, affinePoints1[i], 5, Scalar(0, 255, 255), -1);
} imshow("src", img);
imshow("dst", dst);
waitKey(0);
return 0;
}

效果图:

7.图像的透视变换

图像的透视变换和放射变换类似,不过选取的映射点为四个。

示例代码:

#include <opencv.hpp>
#include <iostream>
#include <imgproc.hpp> using namespace std;
using namespace cv; int main()
{
Mat img = imread("img.jpg");
Mat dst; Point2f perspectivePoints0[4] = { Point2f(100, 50), Point2f(100, 390), Point2f(600, 50),Point2f(600, 800) }; // 选取原图像的映射点
Point2f perspectivePoints1[4] = { Point2f(200, 100), Point2f(200, 300), Point2f(500, 50), Point2f(600, 800) }; // 选取目标图像的映射点 Mat trans = getPerspectiveTransform(perspectivePoints0, perspectivePoints1); // 获得变换矩阵 warpPerspective(img, dst, trans, Size(img.cols, img.rows)); // 透视变换 for (int i = 0; i < 4; ++i) // 描点
{
circle(img, perspectivePoints0[i], 5, Scalar(0, 255, 255), -1);
circle(dst, perspectivePoints1[i], 5, Scalar(0, 255, 255), -1);
} imshow("src", img);
imshow("dst", dst);
waitKey(0);
return 0;
}

效果图(额。):

opencv:图像的基本变换的更多相关文章

  1. OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放

    这篇已经写得很好,真心给作者点个赞.题目都是直接转过来的,直接去看吧. Reference Link : http://blog.csdn.net/poem_qianmo/article/detail ...

  2. 【OpenCV新手教程之十三】OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26157633 作者:毛星云(浅墨) ...

  3. Opencv 图像叠加 添加水印

    Opencv 图像叠加 添加水印 C++: void Mat::copyTo(OutputArray m) const C++: void Mat::copyTo(OutputArray m, Inp ...

  4. opencv图像读取-imread

    前言 图像的读取和保存一定要注意imread函数的各个参数及其意义,尽量不要使用默认参数,否则就像数据格式出现错误(here)一样,很难查找错误原因的: re: 1.opencv图像的读取与保存; 完

  5. 学习 opencv---(12)OpenCV 图像金字塔:高斯金字塔,拉普拉斯金字塔与图片尺寸缩放

    在这篇文章里,我们一起学习下 图像金字塔 的一些基本概念,如何使用OpenCV函数pyrUp和pyrDown 对图像进行向上和向下采样,以及了解专门用于缩放图像尺寸的resize函数的用法.此博文一共 ...

  6. [OpenCV Qt教程] 在Qt图形界面中显示OpenCV图像的OpenGL Widget(第二部分)

    本文译自:http://www.robot-home.it/blog/en/software/tutorial-opencv-qt-opengl-widget-per-visualizzare-imm ...

  7. [OpenCV Qt教程] 在Qt图形界面中显示OpenCV图像的OpenGL Widget (第一部分)

    本文译自:http://www.robot-home.it/blog/en/software/tutorial-opencv-qt-opengl-widget-per-visualizzare-imm ...

  8. 关于OpenCV图像操作的默认参数问题

    本系列文章由 @yhl_leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/51559490 在使用OpenCV以及其 ...

  9. OpenCV:OpenCV图像旋转的代码

    OpenCV图像旋转的代码 cv::transpose( bfM, bfM ) 前提:使用两个矩阵Mat型进行下标操作是不行的,耗费的时间太长了.直接使用两个指针对拷贝才是王道.不知道和OpenCV比 ...

随机推荐

  1. HDU - 5909 Tree Cutting (树形dp+FWT优化)

    题意:树上每个节点有权值,定义一棵树的权值为所有节点权值异或的值.求一棵树中,连通子树值为[0,m)的个数. 分析: 设\(dp[i][j]\)为根为i,值为j的子树的个数. 则\(dp[i][j\o ...

  2. sqlite常用的命令-增删改查

    一.查看版本信息: #sqlite3 -version 二.sqlite3常用命令 1.当前目录下建立或打开test.db数据库文件,并进入sqlite命令终端,以sqlite>前缀标识: 2. ...

  3. java中静态变量,静态代码块,静态方法,实例变量,匿名代码块等的加载顺序

    转自:http://blog.csdn.net/mrzhoug/article/details/51581994 一.在Java中,使用”{}”括起来的代码称为代码块,代码块可以分为以下四种: 1.普 ...

  4. Linux下mysql允许远程连接怎么设置

    1.root用户登录到mysql数据库代码示例:/usr/local/mysql/bin/mysql -u root -p (输入密码进入mysql)2.进入mysql,输入:代码示例:use mys ...

  5. MySQL多版本并发控制机制(MVCC)-源码浅析

    MySQL多版本并发控制机制(MVCC)-源码浅析 前言 作为一个数据库爱好者,自己动手写过简单的SQL解析器以及存储引擎,但感觉还是不够过瘾.<<事务处理-概念与技术>>诚然 ...

  6. golang中文字符编码转换

    golang 有很多需要将中文转成utf8的 网上搜到一个直接转的,记录下,备用 package main import "golang.org/x/text/encoding/simpli ...

  7. 20145322何志威 《Java程序设计》第8周学习总结

    教材学习内容总结 第十四章 NIO使用频道(channel)来衔接数据节点,对数据区的标记提供了clear(),rewind(),flip(),compact()等高级操作. 想要取得channel的 ...

  8. 20145329《Java程序设计》实验四总结

    实验四 Android环境搭建 实验内容 1.搭建Android环境 2.运行Android 3.修改代码,能输出学号 实验步骤 1.搭建Android环境 2.安装Android,核心是配置JDK. ...

  9. Calling Convention的总结

    因为经常需要和不同的Calling Convention打交道,前段时间整理了一下它们之间的区别,如下: 清理堆栈 参数压栈顺序 命名规则 (MSVC++) 备注 Cdecl 调用者 (Caller) ...

  10. Leetcode(93): Restore IP Addresses

    Given a string containing only digits, restore it by returning all possible valid IP address combina ...