1. 图像连接
1.1 vconcat函数介绍(竖向连接)
void cv::vconcat(const Mat * src, size_t nsrc, OutputArray dst )
src:Mat矩阵类型的数组。
nsrc:数组中 Mat 类型数据的个数。
dst:连接后的 Mat类矩阵。
该函数对存放在数组矩阵中的Mat 类型数据进行纵向连接。第一个参数是存放多个 Mat 类型数据的数组,要求数组中所有的 Mat 类型具有相同的列数并且具有相同的数据类型和通道数。第二个参数是数组中含有的 Mat 类型数据的个数。最后一个参数是拼接后输出的结果,结果的宽度与第一个 Mat 类型数据相同,高度为数组中所有 Mat 类型数据高度的总和,并且与第一个 Mat 类型数据具有相同的数据类型和通道数
void cv::vconcat(InputArray src1, InputArray src2, OutputArray dst )
src1:第一个需要连接的 Mat 类矩阵。
src2:第二个需要连接的 Mat 类矩阵,与第一个参数具有相同的宽度、数据类型和通道数。
dst:连接后的 Mat 类矩阵
该函数直接对两个 Mat 类型的数据进行竖向连接。前两个参数分别是需要连接的两个 Mat 类型变量,两者需要具有相同的宽度、数据类型及通道数,第三个参数是连接后的输出结果,在拼接结果中第一个参数在上方,第二个参数在下方。
1.2 hconcat函数原型
void cv::hconcat(const Mat * src, size_t nsrc, OutputArray dst )
void cv::hconcat(InputArray src1, InputArray src2, OutputArray dst )
1.3 示例代码
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
//矩阵数组的横竖连接
Mat matArray[] = { Mat(1, 2, CV_32FC1, cv::Scalar(1)),
Mat(1, 2, CV_32FC1, cv::Scalar(2)) };
Mat vout, hout;
cout << "matArray[0] == " << matArray[0] << "matArray[1] == " << matArray[1] << endl;
vconcat(matArray, 2, vout);
cout << "图像数组竖向连接:" << endl << vout << endl;
hconcat(matArray, 2, hout);
cout << "图像数组横向连接:" << endl << hout << endl;
//矩阵的横竖拼接
Mat A = (cv::Mat_<float>(2, 2) << 1, 7, 2, 8);
Mat B = (cv::Mat_<float>(2, 2) << 4, 10, 5, 11);
cout << "A : \n"<< A << "\nB : \n"<< B << endl;
Mat vC, hC;
vconcat(A, B, vC);
cout << "多个图像竖向连接:" << endl << vC << endl;
hconcat(A, B, hC);
cout << "多个图像横向连接:" << endl << hC << endl;
//读取 4 个子图像,00 表示左上角、01 表示右上角、10 表示左下角、11 表示右下角
Mat img00 = imread("lena00.png");
Mat img01 = imread("lena01.png");
Mat img10 = imread("lena10.png");
Mat img11 = imread("lena11.png");
if (img00.empty()||img01.empty()||img10.empty()||img11.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
//显示 4 个子图像
imshow("img00", img00);
imshow("img01", img01);
imshow("img10", img10);
imshow("img11", img11);
//图像连接
Mat img, img0, img1;
//图像横向连接
hconcat(img00, img01, img0);
hconcat(img10, img11, img1);
//横向连接结果再进行竖向连接
vconcat(img0, img1, img);
//显示连接图像的结果
imshow("img0", img0);
imshow("img1", img1);
imshow("img", img);
waitKey(0);
return 0;
}
1.4 结果
2. 图像尺寸变换
2.1 resize函数介绍
void cv::resize(InputArray src, OutputArray dst, Size dsize, double fx = 0, double fy = 0, int interpolation = INTER_LINEAR )
src:输入图像。
dst:输出图像,图像的数据类型与 src 相同。
dsize:输出图像的尺寸。
fx:水平轴的比例因子,如果将水平轴变为原来的两倍,则赋值为 2。
fy:垂直轴的比例因子,如果将垂直轴变为原来的两倍,则赋值为 2。
interpolation:插值方法的标志
该函数主要用来对图像尺寸进行缩放,前两个参数分别是输入图像和尺寸缩放之后的输出图像。该函数的 dsize 和 fx ( fy )同时可以调整输出图像的参数,因此两类参数在实际使用时只需要使用一类,当根据两个参数计算出来的输出图像尺寸不一致时,以 dsize 设置的图像尺寸为准。这两类调整图像尺寸的参数的关系如下:
最后一个参数是选择图像插值方法的标志。在图像缩放相同的尺寸时,选择不同的插值方法会具有不同效果 。一般来讲,如果要缩小图像,那么通常使用 INTER_AREA 标志会有较好的效果。而在放大图像时,采用 INTER_CUBIC 和 INTER_LINEAR 标志通常会有比较好的效果,这两个标志,前者计算速度较慢,后者速度较快,虽然前者效果较好,但是后者效果也相对比较理想。
示例程序中首先以灰度图像的形式读入一幅图像,之后利用 INTER_AREA 将图像缩小,并分别利用INTER_CUBIC、INTER_NEAREST 和 INTER_LINEAR 这 3 种方法将图像放大到相同的尺寸,根据结果比较两种插值方法效果的差异。
2.2 示例代码
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat gray = imread("../pic/gril.jpg", IMREAD_GRAYSCALE);
if (gray.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
cout << " width == " << gray.cols << " height == " << gray.rows << endl;
Mat smallImg, bigImg0, bigImg1, bigImg2;
resize(gray, smallImg, Size(300, 300), 0, 0, INTER_AREA); //先将图像缩小
resize(smallImg, bigImg0, Size(300, 300), 0, 0, INTER_NEAREST); //最近邻插值
resize(smallImg, bigImg1, Size(300, 300), 0, 0, INTER_LINEAR); //双线性插值
resize(smallImg, bigImg2, Size(300, 300), 0, 0, INTER_CUBIC); //双三次插值
//namedWindow("smallImg", WINDOW_NORMAL); //图像尺寸太小,一定要设置可以调节窗口大小标志
imshow("gray", gray);
imshow("smallImg", smallImg);
//namedWindow("bigImg0", WINDOW_NORMAL);
imshow("bigImg0", bigImg0);
//namedWindow("bigImg1", WINDOW_NORMAL);
imshow("bigImg1", bigImg1);
//namedWindow("bigImg2", WINDOW_NORMAL);
imshow("bigImg2", bigImg2);
waitKey(0);
return 0;
}
2.3 结果:
只需要弄前3个参数就好,一般只需要做裁剪就好了
3. 图像翻转
3.1 flip函数介绍(翻转)
void cv::flip(InputArray src, OutputArray dst, int flipCode )
src:输入图像。
dst:输出图像,与 src 具有相同的大小、数据类型及通道数。
flipCode:翻转方式标志。数值大于 0 表示绕 y 轴进行翻转;数值等于 0,表示绕 x 轴进行翻转;数值小于 0,表示绕两个轴翻转。
3.2 代码示例:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("../pic/gril_1.jpg");
if (img.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat img_x, img_y, img_xy;
flip(img, img_x, 0); //以 x 轴对称
flip(img, img_y, 1); //以 y 轴对称
flip(img, img_xy, -1); //先以 x 轴对称,再以 y 轴对称
imshow("img", img);
imshow("img_x", img_x);
imshow("img_y", img_y);
imshow("img_xy", img_xy);
waitKey(0);
return 0;
}
3.3 结果:
4. 图像仿射变换
4.1 getRotationMatrix2D()函数介绍
Mat cv::getRotationMatrix2D (Point2f center, double angle, double scale )
center:图像旋转的中心位置。
angle:图像旋转的角度,单位为度,正值为逆时针旋转。
scale:两个轴的比例因子,可以实现旋转过程中的图像缩放,不缩放则输入1。
4.2 warpAffine()函数介绍
void cv::warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize,
int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, const Scalar&
borderValue = Scalar() )
src:输入图像。
dst:仿射变换后输出图像,与src数据类型相同,尺寸与dsize相同。
M: 2×3的变换矩阵。
dsize:输出图像的尺寸。
flags:插值方法标志,。
borderMode:像素边界外推方法标志。
borderValue:填充边界使用的数值,默认情况下为0。
插值方法标志
边界外推的方法标志
这个函数使用仿射变换来将输入图像映射到输出图像。仿射变换包括旋转、缩放、平移等操作,但不包括扭曲和剪切。这个函数非常有用,特别是在需要将图像映射到另一个大小或以特定方式旋转或倾斜图像时。
仿射变换又称为三点变换。如果知道变换前后两幅图像中 3 个像素点坐标的对应关系,就可以 求得仿射变换中的变换矩阵 M。OpenCV 4 提供了利用 3 个对应像素点来确定变换矩阵 M 的函数 getAffineTransform(),
warpAffine函数可以参考:
【C++】【Opencv】cv::warpAffine()仿射变换函数详解,实现平移、缩放和旋转等功能-CSDN博客
4.3 getAffineTransform函数介绍
Mat cv::getAffineTransform(const Point2f src[], const Point2f dst[] )
src[]:源图像中的 3 个像素坐标。
dst[] :目标图像中的 3 个像素坐标
4.4 代码示例:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("../pic/gril_1.jpg");
if (img.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat rotation0, rotation1, img_warp0, img_warp1,img_warp2;
double angle = -90; //设置图像旋转的角度
Size dst_size(img.rows, img.cols); //设置输出图像的尺寸
Point2f center(img.rows / 2.0, img.cols / 2.0); //设置图像的旋转中心
rotation0 = getRotationMatrix2D(center, angle, 1); //计算仿射变换矩阵
//cout << "rotation0 : \n" << rotation0 << endl;
warpAffine(img, img_warp0, rotation0, dst_size); //进行仿射变换
imshow("img_warp0", img_warp0);
//根据定义的 3 个点进行仿射变换
Point2f src_points[3];
Point2f dst_points[3];
src_points[0] = Point2f(0, 0); //原始图像中的 3 个点
src_points[1] = Point2f(0, (float)(img.cols - 1));
src_points[2] = Point2f((float)(img.rows - 1), (float)(img.cols - 1));
//仿射变换后图像中的 3 个点
dst_points[0] = Point2f((float)(img.rows)*0.1, (float)(img.cols)*0.10);
dst_points[1] = Point2f((float)(img.rows)*0.1, (float)(img.cols)*0.80);
dst_points[2] = Point2f((float)(img.rows)*0.81, (float)(img.cols)*0.8);
rotation1 = getAffineTransform(src_points, dst_points); //根据对应点求取仿射变换矩阵
//cout << "rotation1 : \n" << rotation1 << endl;
warpAffine(img, img_warp1, rotation1, dst_size); //进行仿射变换
warpAffine(img, img_warp2, rotation1, dst_size,WARP_FILL_OUTLIERS,BORDER_REPLICATE,10); //进行仿射变换
imshow("img_warp1", img_warp1);
imshow("img_warp2", img_warp2);
waitKey(0);
return 0;
}
4.5 结果:
5. 图像透视变换
透视变换是按照物体成像投影规律进行变换,即将物体重新投影到新的成像平面,示意图如下图所示。透视变换常用于机器人视觉导航研究中,由于相机视场与地面存在倾斜角使得物体成像产生畸变,通常通过透视变换实现对物体图像的校正。在透视变换中,透视前的图像和透视后的图像之间的变换关系可以用一个 3×3 的变换矩阵表示,该矩阵可以通过两幅图像中 4 个对应点的坐标求取,因此透视变换又称作“四点变换”。与仿射变换一样,OpenCV 4 中提供了根据 4 个对应点求取变换矩阵的 getPerspectiveTransform()函数和进行透视变换的 warpPerspective()函数。
透视变换原理示意图
5.1 getPerspectiveTransform()函数原型
Mat cv::getPerspectiveTransform (const Point2f src[], const Point2f dst[], int solveMethod = DECOMP_LU )
src[]:原图像中的 4 个像素坐标。
dst[]:目标图像中的 4 个像素坐标。
solveMethod:选择计算透视变换矩阵方法的标志
5.2 warpPerspective()函数原型
void cv::warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT,const Scalar & borderValue = Scalar() )
src:输入图像。
dst:透视变换后输出图像,与 src 数据类型相同,但是尺寸与 dsize 相同。
M:3×3 的变换矩阵。
dsize:输出图像的尺寸。
flags:插值方法标志。
borderMode:像素边界外推方法的标志。
borderValue:填充边界使用的数值,默认情况下为 0。
该函数所有参数的含义与 warpAffine()函数的参数含义相同,这里不再赘述 。示例代码中平面图片经过透视变换有立体感觉。
5.3 示例代码
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img = imread("../pic/gril.jpg");
if (img.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Point2f src_points[4];
Point2f dst_points[4];
//通过 Image Watch 查看的二维码 4 个角点坐标
src_points[0] = Point2f(0, 0);
src_points[1] = Point2f(647, 0);
src_points[2] = Point2f(0, 324);
src_points[3] = Point2f(647, 324);
//期望透视变换后二维码 4 个角点的坐标
dst_points[0] = Point2f(100.0, 150.0);
dst_points[1] = Point2f(527.0, 150.0);
dst_points[2] = Point2f(0.0, 300);
dst_points[3] = Point2f(627.0, 300);
Mat rotation, img_warp;
rotation = getPerspectiveTransform(src_points, dst_points); //计算透视变换矩阵
warpPerspective(img, img_warp, rotation, img.size()); //透视变换投影
imshow("img", img);
imshow("img_warp", img_warp);
waitKey(0);
return 0;
}
5.4 结果