OpenCV4机器学习(四):图像的几何变换、仿射变换

本文介绍了如何使用OpenCV4进行图像的几何变换,包括仿射变换、尺度变换、平移变换、旋转变换和剪切变换。通过`cv::warpAffine`函数和`cv::resize`函数,详细解释了各种变换的实现方法和矩阵运算原理,并提供了C++代码示例。
摘要由CSDN通过智能技术生成

前言:

本专栏主要结合OpenCV4(C++版本),来实现一些基本的图像处理操作、经典的机器学习算法(比如K-Means、KNN、SVM、决策树、贝叶斯分类器等),以及常用的深度学习算法。

系列文章:


一、基本原理

图像的平移、缩放、镜像和旋转等都属于几何变换。在数字图像处理中,几何变换由两个基本操作构成:坐标变换和灰度内插。坐标变换公式可表示为: ( x , y ) = T [ ( v , w ) ] (x, y) = T[(v, w)] (x,y)=T[(v,w)]

其中,(v, w)是源图像中的像素坐标;(x, y)是变换后图像中的像素坐标。

最常见的空间变换之一是仿射变换,其一般形式如下:
在这里插入图片描述
根据矩阵T中元素的取值不同,仿射变换又可分为:恒等变换、尺度变换、旋转变换、平移变换、垂直变换或者水平剪切变换。具体如下表所示:

变换名称仿射矩阵T坐标公式
恒等变换 [ 1 0 0 0 1 0 0 0 1 ] \left[ \begin{matrix}1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{matrix} \right] 100010001 x = v , y = w x=v, y=w x=v,y=w
尺度变换 [ c x 0 0 0 c y 0 0 0 1 ] \left[ \begin{matrix}c_{x} & 0 & 0 \\ 0 & c_{y} & 0 \\ 0 & 0 & 1 \end{matrix} \right] cx000cy0001 x = c x v , y = c y w x =c_{x}v, y=c_{y}w x=cxv,y=cyw
旋转变换 [ c o s θ s i n θ 0 − s i n θ c o s θ 0 0 0 1 ] \left[ \begin{matrix}cos\theta & sin\theta & 0 \\ -sin\theta &cos\theta & 0 \\ 0 & 0 & 1 \end{matrix} \right] cosθsinθ0sinθcosθ0001 x = v c o s θ − w s i n θ , y = v s i n θ + w c o s θ x=vcos\theta-wsin\theta, y=vsin\theta+wcos\theta x=vcosθwsinθ,y=vsinθ+wcosθ
平移变换 [ 1 0 0 0 1 0 t x t y 1 ] \left[ \begin{matrix}1 & 0 & 0 \\ 0 & 1 & 0 \\ t_{x} & t_{y} & 1 \end{matrix} \right] 10tx01ty001 x = v + t x , y = w + t y x=v+t_{x}, y=w+t_{y} x=v+tx,y=w+ty
垂直变换 [ 1 0 0 s h 1 0 0 0 1 ] \left[ \begin{matrix}1 & 0 & 0 \\ s_{h} & 1 & 0 \\ 0 & 0 & 1 \end{matrix} \right] 1sh0010001 x = v + w s h , y = w x=v+ws_{h}, y=w x=v+wsh,y=w
水平剪切变换 [ 1 s v 0 0 1 0 0 0 1 ] \left[ \begin{matrix}1 & s_{v} & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{matrix} \right] 100sv10001 x = v , y = w + v s v x=v, y=w+vs_v x=v,y=w+vsv

二、仿射变换 warpAfine

在 OpenCV 中,实现仿射变换的函数为:cv::warpAffine 函数。其定义如下:

void cv::warpAffine(InputArray src,
					OutputArray dst,
					InputArray M,
					Size dsize,
					int flags = INTER_LINEAR,
					int borderMode = BORDER_CONSTANT,
					const Scalar= & boaderValue = Scalar())

各参数介绍:

  • src:输入图像
  • dst:输出图像
  • M:变换矩阵
  • dsize:输出图像尺寸;如果dsize=(0,0),则由下式计算:dsize=Size(round(fx * src.cols), round(fy * src.rows))
  • flags:组合标志位
  • borderMode:像素外推方法
  • borderValue:在边界位恒定值的情况下使用的数值,默认为0

通过改变 warpAffine 函数中的变换矩阵 M ,可以轻松实现:恒等变换、尺度变换、旋转变换、平移变换、垂直变换或者水平剪切变换

三、尺度变换

(1)resize 函数实现

尺度变换又称图像缩放,在OpenCV中,可使用resize函数来实现尺度变换。resize函数定义如下:

void cv::resize(InputArray src,
				OutputArray dst,
				Size dsize,
				Double fx=0,
				Double fy=0,
				Int interpolation = INTER_LINEAR)

各参数介绍:

  • src:输入图像
  • dst:输出图像
  • dsize:输出图像尺寸;如果dsize=(0,0),则由下式计算:dsize=Size(round(fx * src.cols), round(fy * src.rows))
  • fx:水平轴缩放比例因子;fx、fy不能全部为0。
  • fy:垂直轴缩放比例因子
  • interpolation:插值算法

通过调用 resize 函数,可将原图缩放为原来的一半大小:

resize(img, img_resize, cv::Size(0, 0), 0.5, 0.5, 1);

(2)warpAffine 函数实现

所谓尺度变换,就是要将图像中像素的原坐标(v, w)按照 x = c x v , y = c y w x =c_{x}v, y=c_{y}w x=cxv,y=cyw 的规则,转换到(x, y)。它的简化形式可由下式表示:
在这里插入图片描述
因此,通过 warpAffine 函数,将变换矩阵 M 设置为:
在这里插入图片描述
也可以轻松实现尺度变换,具体实现方法如下:

warpAffine(img, img_warpAffine, M, size, INTER_LINEAR, 1); //仿射变换

(3)实战小结

下面分别使用 resize 函数和 warpAfine 函数,来实现尺度变换:

#include <iostream>
#include <opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
	Mat img = imread("D:\\CSDN\\dog.jpg", 1);
	imshow("original", img);

	/*************** 尺度变换 ***************/
	//方法1:resize
	Mat img_resize;
	resize(img, img_resize, cv::Size(0, 0), 0.5, 0.5, 1);
	imshow("resize", img_resize);

	//方法2:仿射变换
	Mat img_warpAffine;
	Mat M = Mat::zeros(2, 3, CV_32F); //声明尺度变换M矩阵(2x3)
	float cx = 0.5, cy = 0.5;
	M.at<float>(0, 0) = cx;
	M.at<float>(1, 1) = cy;
	cv::Size size = img.size();
	size.width *= cx;
	size.height *= cy;

	warpAffine(img, img_warpAffine, M, size, INTER_LINEAR, 1); //仿射变换
	imshow("warpAffine", img_warpAffine);

	waitKey(0);
	return 0;
}

显示如下所示:
在这里插入图片描述

四、平移变换

对于平移变换,就是要将图像中像素的原坐标(v, w)按照 x = v + t x , y = w + t y x=v+t_{x}, y=w+t_{y} x=v+tx,y=w+ty 的规则,转换到(x, y)。它的简化形式可由下式表示:
在这里插入图片描述
因此,通过 warpAffine 函数,将变换矩阵 M 设置为:
在这里插入图片描述
可以轻松实现平移变换,具体实现方法如下:

#include <iostream>
#include <opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
	Mat img = imread("D:\\CSDN\\lena.jpeg", 1);
	imshow("original", img);
	
	Mat img_trans;
	Mat M1 = cv::Mat::eye(2, 3, CV_32F); //声明平移变换矩阵M1(2x3),并初始化对角线元素为1
	float tx = 40, ty = 20; //设置平移变换参数
	M1.at<float>(0, 2) = tx;
	M1.at<float>(1, 2) = ty;
	
	//为了让平移后的图像能完整地显示,此处做扩边处理,将原图在上、下、左、右四个方向分别扩充相应的元素,并对扩充区域填充恒定灰度值200
	int top = 0, bottom = ty, left = 0, right = tx;
	cv::copyMakeBorder(img, img_trans, top, bottom, left, right, BORDER_CONSTANT, cv::Scalar(200)); //扩边
	cv::warpAffine(img_trans, img_trans, M1, img_trans.size(), INTER_LINEAR, BORDER_TRANSPARENT);
	imshow("translation", img_trans);
	waitKey(0);
	return 0;
}

原图和平移变换之后的效果如下图所示:

在这里插入图片描述

五、旋转变换

对于旋转变换,就是要将图像中像素的原坐标(v, w)按照 x = v c o s θ − w s i n θ , y = v s i n θ + w c o s θ x=vcos\theta-wsin\theta, y=vsin\theta+wcos\theta x=vcosθwsinθ,y=vsinθ+wcosθ 的规则,转换到(x, y)。它的简化形式可由下式表示:
在这里插入图片描述
因此,通过 warpAffine 函数,将变换矩阵 M 设置为:

在这里插入图片描述
可以轻松实现旋转变换,具体实现方法如下:

#include <iostream>
#include <opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
	Mat img = imread("D:\\CSDN\\lena.jpeg", 1);
	
	Mat img_rotate;
	double angle = 45; //设定旋转角度
	int border = 0.207 * img.cols;
	cv::copyMakeBorder(img, img_rotate, border, border, border, border, BORDER_CONSTANT, cv::Scalar(0)); //扩边
	cv::Point2f center(img_rotate.cols / 2., img_rotate.rows / 2.); //指定旋转中心
	cv::Mat M2 = cv::getRotationMatrix2D(center, angle, 1.0); //获取旋转变换矩阵M2
	
	cv::warpAffine(img_rotate, img_rotate, M2, img_rotate.size(), INTER_LINEAR, BORDER_REPLICATE);
	cv::imshow("rotate", img_rotate);
	waitKey(0);
	return 0;
}

经过绕中心点45度,旋转变换之后的效果图所下所示:

在这里插入图片描述

六、剪切变换

剪切变换包含垂直剪切和水平剪切,下面以垂直剪切为例,就是要将图像中像素的原坐标(v, w)按照 x = v + w s h , y = w x=v+ws_{h}, y=w x=v+wsh,y=w 的规则,转换到(x, y)。它的简化形式可由下式表示:

在这里插入图片描述
因此,通过 warpAffine 函数,将变换矩阵 M 设置为:

在这里插入图片描述
可以轻松实现垂直旋转变换,具体实现方法如下:

#include <iostream>
#include <opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
	Mat img = imread("D:\\CSDN\\lena.jpeg", 1);
	
	Mat img_shear_vertical;
	int border = 40;
	cv::copyMakeBorder(img, img_shear_vertical, 10, 10, 10, 10+4* border, BORDER_CONSTANT, cv::Scalar(0)); //扩边
	Mat M3 = cv::Mat::eye(2, 3, CV_32F); //声明垂直剪切变换M3矩阵(2x3),初始化对角线为1
	float sv = 0.3; // 垂直剪切系数
	M3.at<float>(0, 1) = sv;
	
	cv::warpAffine(img_shear_vertical, img_shear_vertical, M3, img_shear_vertical.size(), INTER_LINEAR, BORDER_REPLICATE);
	cv::imshow("shear_vertical", img_shear_vertical);
	waitKey(0);
	return 0;
}

原图经过垂直旋转变换狗的效果如下图所示:

在这里插入图片描述


本专栏所有完整的代码将在我的GitHub仓库上更新,欢迎大家前往学习:

进入GitHub仓库,点击 star (红色箭头所示),第一时间获取干货:
在这里插入图片描述

最好的关系是互相成就,各位的「三连」就是【AI 菌】创作的最大动力,我们下期见!

在这里插入图片描述

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI 菌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值