OpenCV2:第六章 图像几何变换
一.简介
图像的几何变换有距离变换 坐标映射 平移 镜像 旋转 缩放 仿射变换等
二.重映射
把一张图像重新排列像素,比如倒置
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:第六章 图像几何变换的更多相关文章
- OpenCV2:第十一章 图像转换
一.简介 二.例子 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #inclu ...
- 【数字图像处理】六.MFC空间几何变换之图像平移、镜像、旋转、缩放具体解释
本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程<数字图像处理>及课件进行解说,主要通过MFC单文档视图实现显示BMP图片空间几何变换.包含图像平移.图形 ...
- 精通Web Analytics 2.0 (8) 第六章:使用定性数据解答”为什么“的谜团
精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第六章:使用定性数据解答"为什么"的谜团 当我走进一家超市,我不希望员工会认出我或重新为我布置商店. 然而, ...
- Laxcus大数据管理系统2.0(8)- 第六章 网络通信
第六章 网络通信 Laxcus大数据管理系统网络建立在TCP/IP网络之上,从2.0版本开始,同时支持IPv4和IPv6两种网络地址.网络通信是Laxcus体系里最基础和重要的一环,为了能够利用有限的 ...
- Android群英传》读书笔记 (3) 第六章 Android绘图机制与处理技巧 + 第七章 Android动画机制与使用技巧
第六章 Android绘图机制与处理技巧 1.屏幕尺寸信息屏幕大小:屏幕对角线长度,单位“寸”:分辨率:手机屏幕像素点个数,例如720x1280分辨率:PPI(Pixels Per Inch):即DP ...
- 第十六章——处理锁、阻塞和死锁(3)——使用SQLServer Profiler侦测死锁
原文:第十六章--处理锁.阻塞和死锁(3)--使用SQLServer Profiler侦测死锁 前言: 作为DBA,可能经常会遇到有同事或者客户反映经常发生死锁,影响了系统的使用.此时,你需要尽快侦测 ...
- Android群英传笔记——第六章:Android绘图机制与处理技巧
Android群英传笔记--第六章:Android绘图机制与处理技巧 一直在情调,时间都是可以自己调节的,不然世界上哪有这么多牛X的人 今天就开始读第六章了,算日子也刚好一个月了,一个月就读一半,这效 ...
- 第六章P2P技术及应用
第六章P2P技术及应用 P2P技术在我们日常生活中非常实用,例如我们常用的QQ.PPLive.BitTorrent就是基于P2P技术研发.下面将本章中的重点内容进行归纳. 文章中的Why表示产生的背景 ...
- o'Reill的SVG精髓(第二版)学习笔记——第六章
第六章:坐标系统变换 想要旋转.缩放或者移动图片到新的位置.可以给对应的SVG元素添加transform属性. 6.1 translate变换 可以为<use>元素使用x和y属性,以在特性 ...
随机推荐
- vs2008控制台程序运行一闪而过,不显示按任意键继续
调试运行(F5)而且没插断点.且程序没有暂停的点,就会一闪而过 直接执行(Ctrl+F5),在程序结束前会有“按任意键继续” 当你不想进入调试状态,只想看一看程序执行结果时用ctrl+f5F5会进入调 ...
- winform 屏蔽 空格键
private void call_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == (char)Keys.Space) ...
- Golang 读写锁RWMutex 互斥锁Mutex 源码详解
前言 Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现. 引子问题 我一般喜欢带着问题去 ...
- C#SuperSocket的搭建--通过配置启动
之前我们借助一个SuperSocket实现了一个简易版的服务器, 但是不管是Server还是Session都是使用框架的,本篇博客我们要实现自己的Server和Session,来重写框架原生的Serv ...
- SCUT - 261 - 对称与反对称 - 构造 - 简单数论
https://scut.online/p/261 由于M不是质数,要用扩展欧几里得求逆元,而不是费马小定理! 由于M不是质数,要用扩展欧几里得求逆元,而不是费马小定理! 由于M不是质数,要用扩展欧几 ...
- ErrorObject OpenAsync(Action<ErrorObject>arg_fnRet)
ErrorObject OpenAsync(Action<ErrorObject>arg_fnRet) public static ErrorObject Open(this ReadWr ...
- python help(int)
class int(object) | int(x=0) -> integer | int(x, base=10) -> integer | | Convert a number or s ...
- bzoj 3594: [Scoi2014]方伯伯的玉米田【二维树状数组+dp】
设f[i][j]为前i棵玉米被拔高了j(因为是单调不降所以前面越高越好,所以每次拔一个前缀),转移是f[i][j]=f[k][l]+1,l<=j,a[k]+l<=a[i]+j,然后用二维树 ...
- 黑客攻防技术宝典web实战篇:Web 应用程序技术习题
猫宁!!! 参考链接:http://www.ituring.com.cn/book/885 随书答案. 1. OPTIONS 方法有什么作用? OPTIONS 方法要求服务器报告可用于特定资源的 HT ...
- PJzhang:lijiejie的敏感目录爆破工具BBScan
猫宁!!! 参考链接: https://www.freebuf.com/sectool/85729.html https://segmentfault.com/a/1190000014539449 这 ...