前言
在数字图像处理中,OpenCV 是一个强大的工具库,它为开发者提供了丰富的功能来操作和处理图像数据。作为初学者,掌握 OpenCV 中的基础概念和常用类是非常重要的,而 cv::Mat
对象作为 OpenCV 的核心类,是进行图像处理工作的基础。在这篇博客中,我们将从小白的角度出发,详细介绍 cv::Mat
对象的常用方法及其应用场景,辅以完整的 C++ 代码示例,快速入门 OpenCV 的图像处理。无论你是希望进行简单的图像读取和显示,还是希望掌握更复杂的图像变换与滤波操作,这篇文章都将为你打下坚实的基础。
什么是Mat类?
Mat类分为矩阵头和指向存储数据的矩阵指针。矩阵头中包含矩阵的尺寸、存储方法、地址和引用次数等,只存放这几个固定的数据类型,所以矩阵头所占空间是固定的。图像复制和传递过程中主要的开销是存放矩阵数据。
(1)图像数据类型
数据类型 | 通道位数 | 描述 |
---|---|---|
CV_8UC1 | 8 位 | 单通道灰度图,8 位无符号整数 |
CV_8UC3 | 8 位 | 三通道彩色图(BGR),8 位无符号整数 |
CV_16UC1 | 16 位 | 单通道灰度图,16 位无符号整数 |
CV_16SC1 | 16 位 | 单通道灰度图,16 位有符号整数 |
CV_32FC1 | 32 位 | 单通道图像,32 位浮点数 |
CV_32FC3 | 32 位 | 三通道图像,32 位浮点数 |
图像数据类型的选择取决于像素深度和通道数,最常见的类型包括 CV_8UC1
(单通道灰度图)、CV_8UC3
(三通道彩色图)、CV_16UC1
(16 位无符号灰度图)、CV_16SC1
(16 位有符号灰度图)、CV_32FC1
和 CV_32FC3
(分别为单通道和三通道的 32 位浮点图像)。
1.创建 Mat对象
先读取图像用于后面的操作
图片链接实验图片下载链接
Mat src;
src = cv::imread("sherlock.jpg");
Mat image;
image = cv::imread("sherlock.jpg");
if (src.empty())
{
cout << "could not load image..." << endl;
return;
}
namedWindow("input iamge", cv::WINDOW_AUTOSIZE);
cv::imshow("input iamge", src);
cv::Mat
对象,可以是空的,也可以是预定义大小和类型的图像或矩阵。通过使用不同的构造函数和函数(如 zeros
、ones
、eye
),你可以生成特定类型的矩阵。
// 创建一个空的 Mat 对象
cv::Mat mat1;
// 创建一个 3x3 单通道的 8位图像(灰度图)
cv::Mat mat2(3, 3, CV_8UC1, cv::Scalar(0));
// 创建一个 3x3 三通道的图像(彩色图)
cv::Mat mat3(3, 3, CV_8UC3, cv::Scalar(255, 0, 0));
// 使用 `zeros`, `ones`, `eye` 函数创建特殊矩阵
cv::Mat mat4 = cv::Mat::zeros(3, 3, CV_8UC1); // 全零矩阵
cv::Mat mat5 = cv::Mat::ones(3, 3, CV_8UC1); // 全一矩阵
cv::Mat mat6 = cv::Mat::eye(3, 3, CV_8UC1); // 单位矩阵
2. 访问和修改像素值
at
方法可以用于获取或设置某个像素的值。对于灰度图像,你可以直接访问单个通道的值;对于彩色图像,可以分别访问 BGR 三个通道的值。
//访问单个像素(灰度图)
uchar pixelValue = src.at<uchar>(0, 0);
//访问和修改单个像素(彩色图像)
cv::Vec3b& color = src.at<cv::Vec3b>(0, 0);
uchar blue = color[0];
uchar green = color[1];
uchar red = color[2];
// 修改像素值
src.at<cv::Vec3b>(0, 0) = cv::Vec3b(255, 255, 255); // 将像素设为白色
3.图像的复制、截取和拼接
clone
方法用于深度复制图像,Rect
用于定义感兴趣区域 (ROI),而 hconcat
和 vconcat
则分别用于水平和垂直拼接图像。
//图像克隆拼接
Mat dst1, dst2;
dst1 = src.clone();
namedWindow("copyimage1", cv::WINDOW_AUTOSIZE);
cv::imshow("copyimage1", dst1);
src.copyTo(dst2);
namedWindow("copyimage2", cv::WINDOW_AUTOSIZE);
cv::imshow("copyimage2", dst2);
//图像截取
cv::Rect roi(400, 50, 400, 400); // (x,y,w,h)左上角起始点(x,y),截取范围宽高(h,w)
cv::Mat croppedImage = dst1(roi);
namedWindow("croppedImage", cv::WINDOW_AUTOSIZE);
cv::imshow("croppedImage", croppedImage);
//图像拼接
// 水平拼接
cv::Mat imageCopy = image.clone();
cv::Mat hConcat;
cv::hconcat(image,imageCopy,hConcat);
namedWindow("hConcat", cv::WINDOW_AUTOSIZE);
cv::imshow("hConcat", hConcat);
//垂直拼接
cv::Mat vConcat;
cv::vconcat(image, imageCopy, vConcat);
namedWindow("vConcat", cv::WINDOW_AUTOSIZE);
cv::imshow("vConcat", vConcat);
水平拼接 result
4.图像转换(灰度、HSV转换)
在不同颜色空间之间通过cvtColor进行转换常见的转换如 BGR 到灰度图、HSV 等。
Mat grayimage, hsvimage;
cvtColor(src, grayimage, cv::COLOR_BGR2GRAY); //灰度图
cvtColor(src, hsvimage, cv::COLOR_BGR2HSV); //HSV图
namedWindow("grayimage", cv::WINDOW_AUTOSIZE);
cv::imshow("grayimage", grayimage);
namedWindow("hsvimage", cv::WINDOW_AUTOSIZE);
cv::imshow("hsvimage", hsvimage);
HSVimage result
5.图像尺寸、通道数、数据类型等信息
获取图像的基本属性,如尺寸、通道数和数据类型,可以帮助动态调整图像处理方式。
int cols = dst1.cols; //行数
int rows = dst1.rows; //列数
int channels = dst1.channels(); //通道数
int type = dst1.type(); //数据类型
printf("cols: %d rows: %d\n", cols, rows);
printf("图像通道数:%d\n", channels);
printf("数据类型:%d\n", type);
result
6.图像滤波(模糊处理)
滤波是图像处理中常见的操作,主要用于去除噪声或平滑图像。这是常用的几种滤波操作:平均模糊、高斯模糊和中值模糊。
/******************图像滤波(模糊处理)***********************/
cv::Mat blurredImage1, blurredImage2, blurredImage3;
cv::blur(image, blurredImage1, cv::Size(5, 5)); //平均模糊
cv::GaussianBlur(image, blurredImage2, cv::Size(5, 5), 0); //高斯模糊
cv::medianBlur(image, blurredImage3, 5); // 中值模糊
namedWindow("平均模糊", cv::WINDOW_AUTOSIZE); // 显示平均模糊图像
cv::imshow("平均模糊", blurredImage1);
7.图像算术运算
OpenCV上直接对图像进行加、减、乘、除等操作,实现图像的亮度调节、图像融合等操作。
/******************图像算术运算***********************/
cv::Mat result;
// 图像加法
cv::add(image, imageCopy, result);
namedWindow("addmage", cv::WINDOW_AUTOSIZE);
cv::imshow("addmage", result);
// 图像减法
cv::subtract(image, imageCopy, result);
// 图像乘法(标量乘法)
cv::multiply(image, 2.0, result);
// 图像除法
cv::divide(image, 2.0, result);
add result 明显感觉图像变亮
8.阈值处理
阈值处理是将图像中的像素值分为两类,通常用于图像的二值化。通过设置一个阈值,图像中大于该值的像素会被设置为最大值(通常是白色),而小于该值的像素会被设置为最小值(通常是黑色)。
cv::Mat binaryImage;
cv::threshold(grayimage, binaryImage, 128, 255, cv::THRESH_BINARY);
namedWindow("binaryImage", cv::WINDOW_AUTOSIZE); //show image
cv::imshow("binaryImage", binaryImage);
binaryImage result
9.边缘检测
Canny 边缘检测是一种常用的边缘检测算法,它能够有效地检测出图像中的边缘,并且具有良好的抗噪声性能。
/******************边缘检测***********************/
cv::Mat edges;
cv::Canny(grayimage, edges, 50, 150); //双阈值方法
namedWindow("edges", cv::WINDOW_AUTOSIZE);
cv::imshow("edges", edges);
edges result
10.图像变换(缩放、旋转、翻转)
resize
用于调整图像的大小,getRotationMatrix2D
和 warpAffine
结合用于旋转图像,flip
用于翻转图像(水平或垂直)。
/******************图像变换(缩放、旋转、翻转)***********************/
cv::Mat resizedImage, rotatedImage, flippedImage;
// 缩放图像
cv::resize(image, resizedImage, cv::Size(100, 100));
namedWindow("resizedImage", cv::WINDOW_AUTOSIZE);
cv::imshow("resizedImage", resizedImage);
// 旋转图像
cv::Point2f center(image.cols / 2.0, image.rows / 2.0); //旋转中心点
cv::Mat rotMatrix = cv::getRotationMatrix2D(center, 45, 1.0);//中心点、角度、缩放因子
cv::warpAffine(image, rotatedImage, rotMatrix, image.size());
namedWindow("rotatedImage", cv::WINDOW_AUTOSIZE);
cv::imshow("rotatedImage", rotatedImage);
// 翻转图像
cv::flip(image, flippedImage, 1); // 水平翻转
namedWindow("flippedImage", cv::WINDOW_AUTOSIZE);
cv::imshow("flippedImage", flippedImage);
rotatedImage result
完整代码
#include<opencv2/opencv.hpp>
#include<highgui.hpp>
using namespace cv;
using namespace std;
void mat_learn()
{
//input image
Mat src;
src = cv::imread("sherlock.jpg");
Mat image;
image = cv::imread("sherlock.jpg");
if (src.empty())
{
cout << "could not load image..." << endl;
return;
}
namedWindow("input iamge", cv::WINDOW_AUTOSIZE);
cv::imshow("input iamge", src);
/******************创建Mat对象***********************/
// 创建一个空的 Mat 对象
cv::Mat mat1;
// 创建一个 3x3 单通道的 8位图像(灰度图)
cv::Mat mat2(3, 3, CV_8UC1, cv::Scalar(0));
// 创建一个 3x3 三通道的图像(彩色图)
cv::Mat mat3(3, 3, CV_8UC3, cv::Scalar(255, 0, 0));
// 使用 `zeros`, `ones`, `eye` 函数创建特殊矩阵
cv::Mat mat4 = cv::Mat::zeros(3, 3, CV_8UC1); // 全零矩阵
cv::Mat mat5 = cv::Mat::ones(3, 3, CV_8UC1); // 全一矩阵
cv::Mat mat6 = cv::Mat::eye(3, 3, CV_8UC1); // 单位矩阵
/******************访问和修改像素值***********************/
//访问单个像素(灰度图)
uchar pixelValue = src.at<uchar>(0, 0);
//访问和修改单个像素(彩色图像)
cv::Vec3b& color = src.at<cv::Vec3b>(0, 0);
uchar blue = color[0];
uchar green = color[1];
uchar red = color[2];
// 修改像素值
src.at<cv::Vec3b>(0, 0) = cv::Vec3b(255, 255, 255); // 将像素设为白色
//创建一张空白图像
Mat dst;
dst = Mat(src.size(), src.type());
dst = Scalar(0, 0, 255); // 红色
namedWindow("outimage", cv::WINDOW_AUTOSIZE);
cv::imshow("outimage", dst);
/******************图像的复制、截取和拼接***********************/
//图像克隆拼接
Mat dst1, dst2;
dst1 = src.clone();
namedWindow("copyimage1", cv::WINDOW_AUTOSIZE);
cv::imshow("copyimage1", dst1);
src.copyTo(dst2);
namedWindow("copyimage2", cv::WINDOW_AUTOSIZE);
cv::imshow("copyimage2", dst2);
//图像截取
cv::Rect roi(400, 50, 400, 400); // (x,y,w,h)左上角起始点(x,y),截取范围宽高(h,w)
cv::Mat croppedImage = dst1(roi);
namedWindow("croppedImage", cv::WINDOW_AUTOSIZE);
cv::imshow("croppedImage", croppedImage);
//图像拼接
// 水平拼接
cv::Mat imageCopy = image.clone();
cv::Mat hConcat;
cv::hconcat(image,imageCopy,hConcat);
namedWindow("hConcat", cv::WINDOW_AUTOSIZE);
cv::imshow("hConcat", hConcat);
//垂直拼接
cv::Mat vConcat;
cv::vconcat(image, imageCopy, vConcat);
namedWindow("vConcat", cv::WINDOW_AUTOSIZE);
cv::imshow("vConcat", vConcat);
/******************图像转换(灰度、HSV转换)***********************/
Mat grayimage, hsvimage;
cvtColor(src, grayimage, cv::COLOR_BGR2GRAY); //灰度图
cvtColor(src, hsvimage, cv::COLOR_BGR2HSV); //HSV图
namedWindow("grayimage", cv::WINDOW_AUTOSIZE);
cv::imshow("grayimage", grayimage);
namedWindow("hsvimage", cv::WINDOW_AUTOSIZE);
cv::imshow("hsvimage", hsvimage);
printf("iamge channels: %d\n", src.channels());
printf("grayimage channels: %d\n", grayimage.channels());
/******************图像尺寸、通道数、数据类型等信息***********************/
int cols = dst1.cols; //行数
int rows = dst1.rows; //列数
int channels = dst1.channels(); //通道数
int type = dst1.type(); //数据类型
printf("cols: %d rows: %d\n", cols, rows);
printf("图像通道数:%d\n", channels);
printf("数据类型:%d\n", type);
/******************图像滤波(模糊处理)***********************/
cv::Mat blurredImage1, blurredImage2, blurredImage3;
cv::blur(image, blurredImage1, cv::Size(5, 5)); //平均模糊
namedWindow("平均模糊", cv::WINDOW_AUTOSIZE);
cv::imshow("平均模糊", blurredImage1);
cv::GaussianBlur(image, blurredImage2, cv::Size(5, 5), 0); //高斯模糊
cv::medianBlur(image, blurredImage3, 5); // 中值模糊
/******************图像算术运算***********************/
cv::Mat result;
// 图像加法
cv::add(image, imageCopy, result);
namedWindow("addmage", cv::WINDOW_AUTOSIZE);
cv::imshow("addmage", result);
// 图像减法
cv::subtract(image, imageCopy, result);
// 图像乘法(标量乘法)
cv::multiply(image, 2.0, result);
// 图像除法
cv::divide(image, 2.0, result);
/******************阈值处理***********************/
cv::Mat binaryImage;
cv::threshold(grayimage, binaryImage, 128, 255, cv::THRESH_BINARY);
namedWindow("binaryImage", cv::WINDOW_AUTOSIZE);
cv::imshow("binaryImage", binaryImage);
/******************边缘检测***********************/
cv::Mat edges;
cv::Canny(grayimage, edges, 50, 150);
namedWindow("edges", cv::WINDOW_AUTOSIZE);
cv::imshow("edges", edges);
/******************图像变换(缩放、旋转、翻转)***********************/
cv::Mat resizedImage, rotatedImage, flippedImage;
// 缩放图像
cv::resize(image, resizedImage, cv::Size(100, 100));
namedWindow("resizedImage", cv::WINDOW_AUTOSIZE);
cv::imshow("resizedImage", resizedImage);
// 旋转图像
cv::Point2f center(image.cols / 2.0, image.rows / 2.0);
cv::Mat rotMatrix = cv::getRotationMatrix2D(center, 45, 1.0);
cv::warpAffine(image, rotatedImage, rotMatrix, image.size());
namedWindow("rotatedImage", cv::WINDOW_AUTOSIZE);
cv::imshow("rotatedImage", rotatedImage);
// 翻转图像
cv::flip(image, flippedImage, 1); // 水平翻转
namedWindow("flippedImage", cv::WINDOW_AUTOSIZE);
cv::imshow("flippedImage", flippedImage);
waitKey(0);
}
int main()
{
mat_learn();
return 0;
}