一.简介

图像的几何变换有距离变换 坐标映射 平移  镜像 旋转  缩放  仿射变换等

二.重映射

把一张图像重新排列像素,比如倒置

CV_EXPORTS_W void remap( InputArray src, OutputArray dst,

InputArray map1, InputArray map2,

int interpolation, int borderMode=BORDER_CONSTANT,

const Scalar& borderValue=Scalar());

  • src

    源图像
  • dst
    目标图像
  • map1
    x坐标
  • map2
    y坐标
  • interpolation
    表示插值方法
  • borderMode
    表示边界插值类型
  • borderValue

   表示插值数值

#include <iostream>
#include "opencv2/opencv.hpp" using namespace std; int main(int argc, char* argv[]) { cv::Mat srcImage = cv::imread("a.jpg");
if(!srcImage.data)
return -1; // 输出矩阵
cv::Mat resultImage(srcImage.size(), srcImage.type()); // x与y方向矩阵
cv::Mat xMapImage(srcImage.size(), CV_32FC1);
cv::Mat yMapImage(srcImage.size(), CV_32FC1); // 取图像的宽高
int rows = srcImage.rows;
int cols = srcImage.cols; // 图像遍历
for( int j = 0; j < rows; j++ )
{
for( int i = 0; i < cols; i++ )
{ //x与y均翻转
xMapImage.at<float>(j,i) = cols - i;
yMapImage.at<float>(j,i) = rows - j;
}
} // 重映射操作
remap(srcImage, resultImage, xMapImage, yMapImage, CV_INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); // 输出结果
cv::imshow("srcImage", srcImage);
cv::imshow("resultImage", resultImage); cv::waitKey(0); return 0; }

三.平移

图像的平移操作是将图像的所有像素坐标进行水平或垂直方向移动

//图像平移并且图像大小不变和改变

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream> // 平移操作 图像大小不变
cv::Mat imageTranslation1(cv::Mat& srcImage,int xOffset,int yOffset)
{ int nRows = srcImage.rows;
int nCols = srcImage.cols; cv::Mat resultImage(srcImage.size(), srcImage.type()); // 遍历图像
for(int i = 0; i < nRows; ++i)
{
for(int j = 0; j < nCols; ++j)
{
// 映射变换
int x = j - xOffset;
int y = i - yOffset; // 边界判断
if(x >= 0 && y >= 0 && x < nCols && y < nRows)
resultImage.at<cv::Vec3b>(i,j) = srcImage.ptr<cv::Vec3b>(y)[x];
}
}
return resultImage;
} // 平移操作,图像大小改变
cv::Mat imageTranslation2(cv::Mat& srcImage, int xOffset, int yOffset)
{ // 设置平移尺寸
int nRows = srcImage.rows + abs(yOffset);
int nCols = srcImage.cols + abs(xOffset); cv::Mat resultImage(nRows, nCols, srcImage.type()); // 图像遍历
for(int i = 0; i < nRows; ++i)
{
for(int j = 0; j < nCols; ++j)
{ // 映射变换
int x = j - xOffset;
int y = i - yOffset; // 边界判断
if(x >= 0 && y >= 0 && x < nCols && y < nRows)
resultImage.at<cv::Vec3b>(i,j) = srcImage.ptr<cv::Vec3b>(y)[x];
}
}
return resultImage;
} int main()
{ // 图像读取及判定是否正确读入
cv::Mat srcImage = cv::imread("a.jpg");
if(!srcImage.data)
return -1; cv::imshow("srcImage", srcImage);
int xOffset = 50, yOffset = 80; // 图像左平移不改变大小
cv::Mat resultImage1 = imageTranslation1(srcImage, xOffset, yOffset);
cv::imshow("resultImage1", resultImage1); // 图像左平移改变大小
cv::Mat resultImage2 = imageTranslation2(srcImage, xOffset, yOffset);
cv::imshow("resultImage2", resultImage2); //图像右平移不改变大小
xOffset = -50, yOffset = -80;
cv::Mat resultImage3 = imageTranslation1(srcImage, xOffset, yOffset);
cv::imshow("resultImage3", resultImage3); cv::waitKey(0);
return 0;
}

四.缩放

图像缩放会减少或增加图像数据的像素个数,造成信息的丢失

1.基于等间隔提取图像缩放

等间隔提取图像缩放是通过对源图像进行均匀采样来完成的

2.基于区域子块提取图像缩放

区域子块提取图像缩放是通过对源图像进行区域子块划分,然后提取子块中像素值作为采样像素以构成新图像来完成

提取子块像素值常用的有计算子块像素的中值和计算子块像素的均值

对源图像的区域划分也有根据缩放因子等比例提取子块和自适应因子提取子块

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream> using namespace cv; // 基于等间隔提取图像缩放
cv::Mat imageReduction1(cv::Mat& srcImage, float kx, float ky)
{
// 获取输出图像分辨率
int nRows = cvRound(srcImage.rows * kx);
int nCols = cvRound(srcImage.cols * ky);
cv::Mat resultImage(nRows, nCols, srcImage.type());
for (int i = 0; i < nRows; ++i)
{
for (int j = 0; j < nCols; ++j)
{
// 根据水平因子计算坐标
int x = static_cast<int>((i + 1) / kx + 0.5) - 1;
// 根据垂直因子计算坐标
int y = static_cast<int>((j + 1) / ky + 0.5) - 1;
resultImage.at<cv::Vec3b>(i, j) = srcImage.at<cv::Vec3b>(x, y);
}
}
return resultImage;
} cv::Vec3b areaAverage(const cv::Mat& srcImage,Point_<int> leftPoint, Point_<int> rightPoint)
{
int temp1 = 0, temp2 = 0, temp3 = 0;
// 计算区域子块像素点个数
int nPix = (rightPoint.x - leftPoint.x + 1) * (rightPoint.y - leftPoint.y + 1);
// 区域子块各个通道对像素值求和
for (int i = leftPoint.x; i <= rightPoint.x; i++){
{
for (int j = leftPoint.y; j <= rightPoint.y; j++)
{ temp1 += srcImage.at<cv::Vec3b>(i, j)[0];
temp2 += srcImage.at<cv::Vec3b>(i, j)[1];
temp3 += srcImage.at<cv::Vec3b>(i, j)[2];
}
} // 对每个通道求均值
Vec3b vecTemp;
vecTemp[0] = temp1 / nPix;
vecTemp[1] = temp2 / nPix;
vecTemp[2] = temp3 / nPix; return vecTemp;
} } cv::Mat imageReduction2(const Mat& srcImage, double kx, double ky)
{
// 获取输出图像分辨率
int nRows = cvRound(srcImage.rows * kx);
int nCols = cvRound(srcImage.cols * ky);
cv::Mat resultImage(nRows, nCols, srcImage.type()); //区域子块的左上角行列坐标
int leftRowCoordinate = 0;
int leftColCoordinate = 0;
for (int i =0; i < nRows; ++i)
{
// 根据水平因子计算坐标
int x = static_cast<int>((i + 1) / kx + 0.5) - 1;
for (int j = 0; j < nCols; ++j)
{
// 根据垂直因子计算坐标
int y = static_cast<int>((j + 1) / ky + 0.5) - 1;
// 求解区域子块的均值
resultImage.at<Vec3b>(i, j) = areaAverage(srcImage, Point_<int>(leftRowCoordinate, leftColCoordinate), Point_<int>(x, y));
// 更新下子块左上角的列坐标,行坐标不变
leftColCoordinate = y + 1;
}
leftColCoordinate = 0;
// 更新下子块左上角的行坐标
leftRowCoordinate = x + 1;
} return resultImage;
} int main()
{ cv::Mat srcImage = cv::imread("a.jpg");
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage); cv::Mat resultImage1 = imageReduction1(srcImage, 0.5, 0.5);
cv::imshow("res1", resultImage1); cv::Mat resultImage2 = imageReduction2(srcImage, 0.5, 0.5);
cv::imshow("res2", resultImage2); cv::waitKey(0);
return 0;
}

五.旋转

1.角度旋转

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <cmath> using namespace cv;
using namespace std; cv::Mat angleRotate(cv::Mat& src, int angle)
{
// 角度变换
float alpha = angle * CV_PI / 180; // 构造旋转矩阵
float rotateMat[3][3] = {
{cos(alpha), -sin(alpha), 0},
{sin(alpha), cos(alpha), 0},
{0, 0, 1}
}; int nSrcRows = src.rows;
int nSrcCols = src.cols; // 计算旋转后图像矩阵的各个顶点位置
float a1 = nSrcCols * rotateMat[0][0];
float b1 = nSrcCols * rotateMat[1][0];
float a2 = nSrcCols * rotateMat[0][0] + nSrcRows * rotateMat[0][1];
float b2 = nSrcCols * rotateMat[1][0] + nSrcRows * rotateMat[1][1];
float a3 = nSrcRows * rotateMat[0][1];
float b3 = nSrcRows * rotateMat[1][1]; // 计算出极值点
float kxMin = min( min( min(0.0f, a1), a2), a3);
float kxMax = max( max( max(0.0f, a1), a2), a3);
float kyMin = min( min( min(0.0f, b1), b2), b3);
float kyMax = max( max( max(0.0f, b1), b2), b3); // 计算输出矩阵的尺寸
int nRows = abs(kxMax - kxMin);
int nCols = abs(kyMax - kyMin);
cv::Mat dst(nRows, nCols, src.type(), cv::Scalar::all(0));
for (int i = 0; i < nRows; ++i)
{
for (int j = 0; j < nCols; ++j)
{
// 旋转坐标转换
int x = (j + kxMin) * rotateMat[0][0] - (i + kyMin) * rotateMat[0][1];
int y = -(j + kxMin) * rotateMat[1][0] + (i + kyMin) * rotateMat[1][1]; // 区域选择
if((x >= 0) && (x < nSrcCols) && (y >= 0) && (y < nSrcRows))
{
dst.at<cv::Vec3b>(i, j) = src.at<cv::Vec3b>(y, x);
}
}
}
return dst;
} int main()
{
cv::Mat srcImage = cv::imread("a.jpg");
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage);
int angle = 30;
cv::Mat resultImage = angleRotate(srcImage, angle);
imshow("resultImage", resultImage);
cv::waitKey(0);
return 0;
}

2.直接翻转

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <cmath> using namespace cv;
using namespace std; int main()
{ cv::Mat srcImage = cv::imread("a.jpg");
if (!srcImage.data)
return -1; // 逆时针旋转90度
cv::Mat resultImage1;
transpose(srcImage, resultImage1); // 水平翻转
cv::Mat resultImage2;
flip(resultImage1, resultImage2, 1); // 垂直翻转
cv::Mat resultImage3;
flip(resultImage1, resultImage3, 0); // 垂直和水平翻转
cv::Mat resultImage4;
flip(srcImage, resultImage4, -1); cv::imshow("srcImage", srcImage);
cv::imshow("resultImage1", resultImage1);
cv::imshow("resultImage2", resultImage2);
cv::imshow("resultImage3", resultImage3);
cv::imshow("resultImage4", resultImage4); cv::waitKey(0);
return 0;
}

六.仿射变换

OpenCV2:第六章 图像几何变换的更多相关文章

  1. OpenCV2:第十一章 图像转换

    一.简介 二.例子 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #inclu ...

  2. 【数字图像处理】六.MFC空间几何变换之图像平移、镜像、旋转、缩放具体解释

    本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程<数字图像处理>及课件进行解说,主要通过MFC单文档视图实现显示BMP图片空间几何变换.包含图像平移.图形 ...

  3. 精通Web Analytics 2.0 (8) 第六章:使用定性数据解答”为什么“的谜团

    精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第六章:使用定性数据解答"为什么"的谜团 当我走进一家超市,我不希望员工会认出我或重新为我布置商店. 然而, ...

  4. Laxcus大数据管理系统2.0(8)- 第六章 网络通信

    第六章 网络通信 Laxcus大数据管理系统网络建立在TCP/IP网络之上,从2.0版本开始,同时支持IPv4和IPv6两种网络地址.网络通信是Laxcus体系里最基础和重要的一环,为了能够利用有限的 ...

  5. Android群英传》读书笔记 (3) 第六章 Android绘图机制与处理技巧 + 第七章 Android动画机制与使用技巧

    第六章 Android绘图机制与处理技巧 1.屏幕尺寸信息屏幕大小:屏幕对角线长度,单位“寸”:分辨率:手机屏幕像素点个数,例如720x1280分辨率:PPI(Pixels Per Inch):即DP ...

  6. 第十六章——处理锁、阻塞和死锁(3)——使用SQLServer Profiler侦测死锁

    原文:第十六章--处理锁.阻塞和死锁(3)--使用SQLServer Profiler侦测死锁 前言: 作为DBA,可能经常会遇到有同事或者客户反映经常发生死锁,影响了系统的使用.此时,你需要尽快侦测 ...

  7. Android群英传笔记——第六章:Android绘图机制与处理技巧

    Android群英传笔记--第六章:Android绘图机制与处理技巧 一直在情调,时间都是可以自己调节的,不然世界上哪有这么多牛X的人 今天就开始读第六章了,算日子也刚好一个月了,一个月就读一半,这效 ...

  8. 第六章P2P技术及应用

    第六章P2P技术及应用 P2P技术在我们日常生活中非常实用,例如我们常用的QQ.PPLive.BitTorrent就是基于P2P技术研发.下面将本章中的重点内容进行归纳. 文章中的Why表示产生的背景 ...

  9. o'Reill的SVG精髓(第二版)学习笔记——第六章

    第六章:坐标系统变换 想要旋转.缩放或者移动图片到新的位置.可以给对应的SVG元素添加transform属性. 6.1 translate变换 可以为<use>元素使用x和y属性,以在特性 ...

随机推荐

  1. error the @annotation pointcut expression is only supported at Java 5

    eclipse搭建环境后报错 the pointcut is supported at Java 5 错误意思大致是:注释切入点表达式只支持在Java 5版本以上,我就纳闷了我安装的是jdk1.8啊, ...

  2. 洛谷 - P2261 - 余数求和

    https://www.luogu.org/problemnew/show/P2261 看了一下题解,取模运算可以换成减法来做. $a\%b=a-b*\lfloor\frac{a}{b}\rfloor ...

  3. 洛谷 - P1012 - 拼数 - 排序

    https://www.luogu.org/problemnew/show/P1012 这道水题居然翻车了,还发现不了bug,服气了.并不是空字符一定比不空要好,要取决于替代它的字符的大小.所以还是直 ...

  4. 51nod 1119【杨辉三角】

    思路: = =杨辉三角的应用,组合数的应用: C(N+M,N); 逆元一发,费马小定理,OK. #include <stdio.h> #include <string.h> # ...

  5. 51nod 1004 【快速幂】

    思路: 掐住最后一位,快速幂一发就好了 #include<cstdio> #include <map> #include<iostream> #include< ...

  6. PJzhang:centos7上LNMP方式安装dvwa漏洞测试环境

    猫宁!!! 参考链接:https://www.jianshu.com/p/5491ce5bfbac https://www.cnblogs.com/wujuntian/p/8183952.html h ...

  7. elasticsearch 查询 query

    对于 类型是 text的字段,并且分析器指明是ik_max_word的会建立倒排索引 查询的分类: match查询:  会自动转换大小写,会分词, term查询: 不会转换和分词,只能值匹配 term ...

  8. Spring事务引发dubbo服务注册问题

    文章清单 1. 问题 2. 查找bug过程 3. 解决方案 使用spring boot+dubbo写项目,一个服务,之前是正常的,后来调用方出现空指针异常,第一反应提供方出了问题. 1. 看控制台,服 ...

  9. Hexo瞎折腾系列(7) - Coding Pages申请SSL/TLS证书错误

    问题 今天我的个人站点SSL/TLS证书到期,我的证书是由Coding Pages提供的,每次申请成功后有效期是三个月,证书到期后可以继续免费申请.但是当我登陆进入Coding Pages服务的后台并 ...

  10. RobotFramework自动化测试框架(1)- RobotFramework简介

    对于RobotFramework自动化测试框架,我这里会从三个单元进行阐述,希望能对你有帮助. RobotFramework简介 RobotFramework是什么? Robotframework 是 ...