一.简介

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

二.重映射

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

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. CF 8D two friends

    独立想的好开心呀(然而是一道水题). 可以看出这道题的答案是满足单调性的,然后可以考虑二分. 对于当前二分出的mid值,我们考虑这个过程. 假设他们能共同走到shop然后共同会home $$Ans = ...

  2. 编程 MD(d)、MT(d)编译选项的区别

    转:http://blog.csdn.net/nodeathphoenix/article/details/7550546 1.各个选项代表的含义 编译选项 包含 静态链接的lib 说明 /MD _M ...

  3. C - Soldier and Cards

    Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Description Two bo ...

  4. 任务27:Middleware管道介绍

    任务27:Middleware管道介绍 HttpContext 图中注册了三个中间件,所有httpContext就会在这三个管道上都执行HttpContext,会在Reponse里面写一些东西.所有东 ...

  5. 设计模式——模板模式(Template Pattern)

    在读Spring源码的时候,发现Spring代码中运用了大量的模板模式,比如根据文件系统目录加载配置文件(FileSystemXmlApplicationContext),类路径加载配置文件(Clas ...

  6. JDK8 Lamdba表达式转换成Map,value为null问题

    // 将list转换成Map类型 Map<String, String> map = list.stream().collect(Collectors.toMap(Person::getI ...

  7. angular源码剖析之Provider系列--QProvider

    QProvider 简介 源码里是这么描述的: A service that helps you run functions asynchronously, and use their return ...

  8. (水题)洛谷 - P1478 - 陶陶摘苹果(升级版)

    https://www.luogu.org/problemnew/show/P1478 没啥好说的…… 居然还漏写一个等于号WA了一发. #include<bits/stdc++.h> u ...

  9. C# 中使用Image.FromFile(string path)后,提示该文件正在被另一进程使用XXX的问题

    C# 中使用Image.FromFile(string path)后,提示该文件正在被另一进程使用XXX的问题 C# 中使用Image.FromFile(string path)后,提示该文件正在被另 ...

  10. 害死人不偿命的(3n+1)猜想 (15)

    #include <iostream> #include <algorithm> using namespace std; int main(){ int n; while ( ...