若该文为原创文章,未经允许不得转载
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/100583521
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中..
OpenCV开发专栏(点击传送门)
上一篇:《OpenCV开发笔记(二):cvui交互界面》
下一篇:《OpenCV开发笔记(四):OpenCV图片和视频数据的读取与存储》
前言
OpenCV最主要的功能是用于操作图像,所以图像的概念贯穿整个OpenCV,与其相关的核心类就是cv::Mat。
图像与矩阵
图像指数字图像。
以上看到的是一张可视化的图片,但是在计算机中这副图像知识一系列亮度各异的店,该图片的像素为300 x 200像素的图,可以用一个300*200的矩阵来表示,举证元素的值表示这个位置上的像素的亮度,一般来说像素值越大表示该店越亮。
放大“C”后如下图所示:
每个点对应的亮度可以理解为rgb的值,无符号8位数3维,则一个像素点为3维数组,分别对应RGB的值,在OpenCV中数据类型为:CV_8U3C。
假设M x N,Iij表示第j行j列,对应上图就是M = 300, N = 200。
注意:在Opencv中三维数组存储RGB值,存储颜色通道的顺序不是RGB,而是BGR,如下图:
OpenCV中的矩阵类cv::Mat
定义
Mat贯穿了整个OpenCV。
Mat类的关键定义如下:
class CV_EXPORTS Mat
{
public:
// 一系列函数
...
/* flag 参数中包含许多关于矩阵的信息,如:
-Mat 的标识
-数据是否连续
-深度
-通道数目
*/
int flags;
// 矩阵的维数,取值应该大于或等于 2
int dims;
// 矩阵的行数和列数,如果矩阵超过 2 维,这两个变量的值都为-1
int rows, cols;
// 指向数据的指针
uchar* data;
// 指向引用计数的指针
// 如果数据是由用户分配的,则为 NULL
int* refcount;
// 其他成员变量和成员函数
...
}
初始化方式
Mat是一个非常优秀的图像类,它同时也是一个通用的矩阵类,可以用来创建和操作多维矩阵。有多种方法创建一个Mat对象。
首先了解下数据的类型(举例:CV_8UC3):
初始化函数
// 默认形式
cv::Mat mat;
// 拷贝构造形式
cv::Mat mat(const cv::Mat& mat);
// 指定行列范围的拷贝构造
cv::Mat mat(const cv::Mat& mat, const cv::Range& rows, const cv::Range& cols);
// 指定ROI的拷贝构造
cv::Mat mat(const cv::Mat& mat, const cv::Rect& roi);
// 使用多维数组中指定范围内的数据的拷贝构造
cv::Mat mat(const cv::Mat& mat, const cv::Range* ranges);
// 指定类型和大小(行列)的二维数组(注意:是行在前,列在后)
cv::Mat mat(int rows, int cols, int type);
// 有初始化值的置顶类型和大小(行列)的二维数据
cv::Mat mat(int rows, int cols, int type, const Scalar& s);
// 使用预先存在数据定义的指定类型和大小(行列)的二维数组
cv::Mat mat(int rows, int cols, int type, void *data, size_t step = AUTO_STEP);
// 指定大小和类型的二维数组
cv::Mat mat(cv::Size sz, int type, const Scalar& s);
// 使用预先存在的数据定义的指定大小和类型的二维数组
cv::Mat mat(cv::Size sz, int type, void *data, size_t step = AUTO_STEP);
// 指定类型的多维数据
cv::Mat mat(int ndims, const int *sizes, int type);
// 使用预先存在的数据定义的指定类型的多维数组
cv::Mat mat(int ndims, const int* sizes, int type, void* data, size_t step = AUTO_STEP);
// 使用cv::Vec定义相同类型、大小为n的一维数组
cv::Mat mat(const cv::Vec<T, n>& vec, bool = copyData = true);
// 使用cv::Matx定义相同类型、大小为mxn的二维数组
cv::Mat mat(const cv::Matx<T, m, n>& vec, bool copyData = true);
// 使用STL vector定义相同类型的一维数组
cv::Mat mat(const std::vector<T>& vec, bool copyData = true);
// 使用zeros()函数定义指定大小和类型的cv::Mat(全为0)
cv::Mat mat = cv::Mat::zeros(int rows, int cols, int type);
// 使用ones()函数定义指定大小和类型的cv::Mat(全为0)
cv::Mat ma = cv::Mat::ones(int rows, int cols, int type);
// 使用eye()函数定义指定大小和类型的cv::Mat(恒等矩阵)
cv::Mat mat = cv::Mat::eye(int rows, int cols, int type);
像素值的读写
很多时候,我们需要读取某个像素值,或者设置某个像素值;在更多的时候,
我们需要对整个图像里的所有像素进行遍历。OpenCV提供了多种方法来实现图像的遍历。
方法一:at()函数
cv::Mat grayim(600, 800, CV_8UC1);
cv::Mat colorim(600, 800, CV_8UC3);
// 遍历所有像素,并设置像素值
for( int i = 0; i < grayim.rows; ++i)
{
for( int j = 0; j < grayim.cols; ++j )
}
grayim.at<uchar>(i,j) = (i+j)%255;
// 遍历所有像素,并设置像素值
for( int i = 0; i < colorim.rows; ++i)
{
for( int j = 0; j < colorim.cols; ++j )
{
cv::Vec3b pixel;
// 注意:opencv通道顺序,BGR,非RGB
pixel[0] = i%255; // Blue
pixel[1] = j%255; // Green
pixel[2] = 0; // Red
colorim.at<Vec3b>(i,j) = pixel;
}
}
方法二:使用迭代器
// 遍历所有像素,并设置像素值
// 遍历所有像素,并设置像素值
cv::MatIterator_<uchar> grayit, grayend;
for(grayit = grayim.begin<uchar>(), grayend = grayim.end<uchar>();
grayit != grayend; ++grayit)
{
*grayit = rand()%255;
}
// 遍历所有像素,并设置像素值
cv::MatIterator_<cv::Vec3b> colorit, colorend;
for(colorit = colorim.begin<cv::Vec3b>(), colorend = colorim.end<cv::Vec3b>();
colorit != colorend; ++colorit)
{
(*colorit)[0] = rand()%255; // Blue
(*colorit)[1] = rand()%255; // Green
(*colorit)[2] = rand()%255; // Red
}
方法三:通过数据指针
cv::Mat grayim(600, 800, CV_8UC1);
cv::Mat colorim(600, 800, CV_8UC3);
//遍历所有像素,并设置像素值
for( int i = 0; i < grayim.rows; ++i)
{
//获取第 i 行首像素指针
uchar * p = grayim.ptr<uchar>(i);
//对第 i 行的每个像素(byte)操作
for( int j = 0; j < grayim.cols; ++j )
p[j] = (i+j)%255;
}
//遍历所有像素,并设置像素值
for( int i = 0; i < colorim.rows; ++i)
{
//获取第 i 行首像素指针
cv::Vec3b * p = colorim.ptr<cv::Vec3b>(i);
for( int j = 0; j < colorim.cols; ++j )
{
p[j][0] = i%255; //Blue
p[j][1] = j%255; //Green
p[j][2] = 0; //Red
}
}
图像局部操作
选择单行/单列
Mat Mat::row(int i) const
Mat Mat::col(int j) const
示例: A 矩阵的第i行,将这一行的所有元素都乘以2,然后赋值给第j行
A.row(j) = A.row(i)*2;
选择多行/多列
Range是OpenCV 中新增的类,该类有两个关键变量star和end。Range 对
象可以用来表示矩阵的多个连续的行或者多个连续的列。其表示的范围为从 start到end,包含start。
示例
// 创建一个单位阵
Mat A = Mat::eye(10, 10, CV_32S);
// 提取第 1 到 3 列(不包括 3)
Mat B = A(Range::all(), Range(1, 3));
// 提取B的第 5 至 9 行(不包括 9)
// 其实等价于C = A(Range(5, 9), Range(1, 3))
Mat C = B(Range(5, 9), Range::all());
选择指定区域
图像中提取感兴趣区域(Region of interest)有两种方法:
- 方法一:使用构造函数
//创建宽度为 320,高度为 240 的 3 通道图像
Mat img(Size(320, 240), CV_8UC3);
//roi 是表示 img 中 Rect(10, 10, 100, 100)区域的对象
Mat roi(img, Rect(10, 10, 100, 100));
- 方法二:使用括号运算符
Mat roi2 = img(Rect(10, 10, 100, 100));
当然也可以使用Range对象来定义感兴趣区域,如下:
// 用括号运算符
Mat roi3 = img(Range(10, 100), Range(10, 100));
// 用构造函数
Mat roi4(img, Range(10, 100), Range(10, 100));
取对角线元素
矩阵的对角线元素可以使用cv::Mat类的diag()函数获取:
Mat Mat::diag(int d) const
- 参数d=0时,表示取主对角线;
- 当参数d>0是,表示取主对角线下方的次对线,如当d=1 时,表示取主对角线下方,且紧贴主多角线的元素;
- 当参数d<0时,示取主对角线上方的次对角线。如同 row()和 col()函数, diag()函数也不进行内存复制操作,其复杂度也是O(1)。
cv::Mat矩阵支持的运算操作
cv::Mat表达式所支持的运算。
下面的列表中使用 A 和 B 表示 Mat 类型的对象,使用s表示Scalar对象,alpha 表示double值。
- 加法,减法,取负:A+B, A-B, A+s, A-s, s+A, s-A, -A
- 缩放取值范围:A * alpha
- 矩阵对应元素的乘法和除法:A.mul(B),A/B,alpha/A
- 矩阵乘法:A*B(注意此处是矩阵乘法,而不是矩阵对应元素相乘)
- 矩阵转置:A.t()
- 矩阵求逆和求伪逆:A.inv()
- 矩阵比较运算:A cmpop B,A cmpop alpha,alpha cmpop A。此处cmpop
- 可以是>,>=,==,!=,<=,<。如果条件成立,则结果矩阵(8U类型矩阵)的对应元素被置为255;否则置0
- 矩阵位逻辑运算: A logicop B, A logicop s, s logicop A, ~A,此处 logicop
- 可以是&,|和^
- 矩阵对应元素的最大值和最小值:min(A, B),min(A, alpha),max(A, B),max(A, alpha)
- 矩阵中元素的绝对值:abs(A)
- 叉积和点积:A.cross(B),A.dot(B)
对矩阵不理解的可以阅读《线性代数矩阵以及Eigen库的介绍、编译和使用》中的矩阵操作解析。
上一篇:《OpenCV开发笔记(二):cvui交互界面》
下一篇:《OpenCV开发笔记(四):OpenCV图片和视频数据的读取与存储》
原博主博客地址:长沙红胖子Qt C++ Linux Arm_长沙红胖子_CSDN博客-Qt开发,图形图像处理,OpenCV图像处理领域博主
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:OpenCV开发笔记(三):OpenCV图像的概念和基本操作_长沙红胖子Qt C++ Linux Arm-CSDN博客