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属性,以在特性 ...
 
随机推荐
- Codechef LOCAUG17
			
做完题目很少有写题解的习惯,强行PO一组吧. 比赛链接:https://www.codechef.com/LOCAUG17 PRINCESS 给定字符串s,问s是否存在长度大于1的回文子串. 解:分两 ...
 - C. Vanya and Scales
			
time limit per test 1 second memory limit per test 256 megabytes input standard input output standar ...
 - easyui  动态添加标签页,总结
			
步骤 1:创建 Tabs <div style="margin-bottom:10px"> <a href="#" class="e ...
 - 个人项目开发PSP实践-MyWCprj
			
MyWCprj.exe Github仓库地址 1. What is MyWCprj.exe? wc是linux下一个非常好用的代码统计小工具,可以通过 -c .-w .-l等选项分别进行对指定文件的代 ...
 - 洛谷 - P1141 - 01迷宫 - dfs
			
https://www.luogu.org/problemnew/show/P1141 能互相到达的格子的答案自然是一样的,第一次dfs标记联通块,第二次dfs把cnt传递到整个联通卡并顺手消除vis ...
 - hdu 3038 How Many Answers Are Wrong【带权并查集】
			
带权并查集,设f[x]为x的父亲,s[x]为sum[x]-sum[fx],路径压缩的时候记得改s #include<iostream> #include<cstdio> usi ...
 - bzoj 3573: [Hnoi2014]米特运输【树形dp+瞎搞】
			
阅读理解题,题意是以1为根的有根树,每个点有点权,求修改最少点权能使每个点的权值等于其所有子节点权值之和并且每个点的所有子节点权值相等的个数 然后就比较简单了,就是有个技巧是数太大,需要对所有操作都取 ...
 - USACO Training3.3亚瑟王的宫殿【搜索】By cellur925
			
题目传送门 因为太蒟了,所以参考了dalao@zbtrs == 对此表示感谢并侵删. 看起来我们就知道这是搜索题. 最后的情况分两种:有骑士背国王/国王自食其力走到集合点. 首先,我们不知道大家 ...
 - python实现基数排序
			
# 基数排序有着局限性,只能是整数,# 排序的时候要先排后面一个条件的(多条件排序)#如本例中,先从个位开始排起# 多关键字排序# 从低关键字开始排序 # @File: radix_sort #### ...
 - Poj 3666 Making the Grade  (排序+dp)
			
题目链接: Poj 3666 Making the Grade 题目描述: 给出一组数,每个数代表当前位置的地面高度,问把路径修成非递增或者非递减,需要花费的最小代价? 解题思路: 对于修好的路径的每 ...