OpenCv图像处理之Mat类使用
Opencv简介
opencv是一个非常优秀的图像处理工具库,底层由C++编写,故本教程的编程语言选择的是C++。
在生活中到处都是唯美的图像组成的一幅幅生动美丽的画面,但是这些画面经过计算机剖析之后,其实是一系列数字组成。比如下面的这幅图
在我们眼里,是一只非常可爱的小猫咪,但是在计算机眼里,它只是一些亮度不同的点而已,这副图片的大小是746 * 745,也就是一个746 * 745的2维矩阵,矩阵中的每一个元素表示这个位置上像素的亮度,像素值越大表示该点的亮度越亮。这些点的亮度其实就是rgb的值,一般的图像文件格式使用的是unsigned 8bits,即3维无符号8位数(uchar),
故一个像素点为3维数组,分别对应rgb的值。但是需要注意的是,在opencv中3维数组存储的rgb值顺序并不是rgb,而是bgr。Mat矩阵对应的参数类型是CV_8UC1,CV_8UC2,CV_8UC3(C(n),n表示的是通道数,RGB3对应的参数类型就是CV_8UC3)
矩阵类cv::Mat
下面简单介绍了一下Mat类中某些关键的成员变量,详细的可以看源码
#include <iostream>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
using namespace std;
using namespace cv;
//Mat类副本
class MatTest {
public:
//flags参数包含许多关于矩阵的信息,比如Mat标识,数据是否连接,深度,通道数目
int flags;
//矩阵维度,至少2维,1维为向量
int dims;
//矩阵的行列数,若为n维矩阵(n>2),那么rows=-1,cols=-1
int rows, cols;
//无符号字符串指针,指向数据(files,images..)
uchar *data;
};
int main(){
return 0;
}
下面来看一下源码中数据的类型
Matrix Type | Type |
---|---|
CV_8U | uchar |
CV_8S | schar |
CV_16U | ushort |
CV_16S | short |
CV_32S | int |
CV_32F | float |
CV_64F | double |
源码中封装了各种矩阵函数,这里简单介绍几种,顺便增加熟练度
#include <iostream>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <d2d1.h>
#include <vector>
using namespace std;
using namespace cv;
int main() {
//cv::Mat
//默认构造函数
Mat mat;
//拷贝构造函数
Mat mat1(const Mat &mat1);
//指定行列范围的拷贝构造函数
Mat mat2(const Mat &mat2, const Range &row, const Range &cols);
//指定ROI的拷贝构造函数
Mat mat3(const Mat &mat3, const Rect &roi);
//使用多维数组中指定范围内的数据的拷贝构造函数
Mat mat5(const Mat &mat5, Range *ranges);
//指定类型和大小(行列)的2维数组(行在前,列在后)
Mat mat6(int rows, int cols, int type);
//有初始化值的置顶类型和大小的2维数组
Mat mat7(int rows, int cols, int type, const Scalar &scalar);
//指定大小和类型的2维数组
Mat mat8(Size size, int type, const Scalar &scalar);
//指定类型的多维数组
Mat mat9(int ndims, const int *sizes, int type);
//使用cv::Vec定义相同类型大小为n的1维向量
Mat mat10(const Vec<T, n> &vec, bool copy_data = true);
//使用cv::Matx定义相同类型大小为m*n的2维数组
Mat mat11(const Matx<T, m, n> &vec, bool copy_data = true);
//使用STL vector定义相同类型的1维数组
Mat mat12(const vector<T> &vec, bool copy_data = true);
//使用zeros()定义指定大小和类型的全零矩阵
Mat::zeros(int rows,int cols,int type);
//使用ones()定义指定大小和类型的全1矩阵
Mat::ones(int rows,int cols,int type);
return 0;
}
遍历图像,设置像素值
在图像处理中,设置像素值是很常见的事情,如何设置呢,通常是对图像中的全部像素进行遍历来设置
使用at(int x,int y)
#include <iostream>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <d2d1.h>
#include <vector>
using namespace std;
using namespace cv;
int main() {
//单通道灰度图
Mat gray_image = cv::imread("D://cat.jpg", CV_8UC1);
//rgb
Mat rgb_image = cv::imread("D://cat.jpg");
//遍历gray_image所有像素,设置像素值
for (int i = 0; i < gray_image.rows; ++i) {
for (int j = 0; j < gray_image.cols; ++j) {
gray_image.at<uchar>(i, j) = (i + j) % 255;
}
}
cv::imshow("gray_image", gray_image);
//遍历rgb_image所有像素,设置像素值
for (int i = 0; i < rgb_image.rows; ++i) {
for (int j = 0; j < rgb_image.cols; ++j) {
//cv::Vec3b
Vec3b pixel;
//Blue
pixel[0] = i % 255;
//Green
pixel[1] = j % 255;
//Red
pixel[2] = (i + j) % 255;
rgb_image.at<Vec3b>(i, j) = pixel;
}
}
cv::imshow("rgb_cat", rgb_image);
cv::waitKey(0);
return 0;
}
上述代码中Vec3b还可以写成cv::Vec<uchar,3> vec;
定义一个uchar类型,长度为3的数组。opencv中封装好的类型如8U类型rgb彩色图像用的是<Vec3b>
,float类型的矩阵使用<Vec3f>
。对于Vec对象而言,可以使用[]
符号像操作数据般读写Vec数据,如上述程序中的Vec3b pixel;
对象>。
效果图
上述程序中多次用到了at<>(int x,int y)
;它是cv::mat
的成员函数,at()可以用来存取图像中对应坐标为(x,y)的像素坐标,使用的时候要注意标明数据类型,比如单通道的灰度图可以写成img.at<uchar>(1,2) = 10;
而三通道的彩色图则可以这样写img.at<Vec3b>(1,2)[0] = 10; img.at<Vec3b>(1,2)[1] = 20;img.at<Vec3b>(1,2)[2] = 30;
这样是对三通道对应的像素进行了修改,opencv中对应的三原色的顺序是bgr,上面也提到过。
使用MatIterator_迭代器
#include <iostream>
#include <opencv2/highgui.hpp>
#include <vector>
#include <random>
using namespace std;
using namespace cv;
int main() {
Mat rgb_img = imread("D:/cat.jpg");
//定义迭代器对象
MatIterator_<Vec3b> rgb_start, rgb_end;
//随机数对象定义
default_random_engine random;
for (rgb_start = rgb_img.begin<Vec3b>(), rgb_end = rgb_img.end<Vec3b>();
rgb_start != rgb_end; ++rgb_start) {
//B-G-R
(*rgb_start)[0] = random();
(*rgb_start)[1] = random();
(*rgb_start)[2] = random();
}
imshow("rgb_iter", rgb_img);
waitKey(0);
return 0;
}
选择指定区域
使用构造函数截取指定区域
#include <iostream>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <vector>
using namespace std;
using namespace cv;
int main() {
Mat rgb_img = imread("D:/cat.jpg");
//Rect(x,y,width,height)
Mat roi(rgb_img, Rect(101, 201, 501, 500));
imshow("roi_img", roi);
waitKey(0);
return 0;
}
效果显示
使用operator()运算符进行截取
#include <iostream>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <vector>
using namespace std;
using namespace cv;
int main() {
Mat rgb_img = imread("D:/cat.jpg");
//第一个Range()表示rowRange(代表高度,行是竖着)
//第二个Range()表示colRange(代表宽度,列是横着)
Mat roi = rgb_img(Range(100, 550), Range(100, 600));
imshow("roi_img", roi);
waitKey(0);
return 0;
}
需要注意的是截取的范围是有界限的,若截取不当,则会报错
Assertion failed (0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <=
roi.height && roi.y + roi.height <= m.rows) in Mat
效果显示