c++ opencv4.5.5 学习笔记(二)cv::Mat-基本图像容器

目录

  • 一、图像的表示
  • 二、 存储方法
  • 三、Mat 类
  • 四、创建 Mat 对象
    • 4.1、使用构造函数方法
    • 4.2、create()函数创建对象
  • 五、矩阵的基本元素表达
  • 六、Mat数据操作

一、图像的表示

    一副尺寸为 M × N 的图像可以用一个 M × N 的矩阵来表示,矩阵元素的值表示这个位置上的像素的亮度,一般来说像素值越大表示该点越亮。一般来说,灰度图用 2 维矩阵表示,彩色(多通道)图像用 3 维矩阵(M× N × 3)表示。对于图像显示来说,目前大部分设备都是用无符号 8 位整数(类型为 CV_8U) 表示像素亮度。图像数据在计算机内存中的存储顺序为以图像最左上点(也可能是最左下点)开始, 存储如图所示。
c++ opencv4.5.5 学习笔记(二)cv::Mat-基本图像容器_第1张图片
    Iij 表示第 i 行 j 列的像素值。如果是多通道图像,比如 RGB 图像,则每个像素用三个字节表示。在 OpenCV 中, RGB 图像的通道顺序为 BGR 。
c++ opencv4.5.5 学习笔记(二)cv::Mat-基本图像容器_第2张图片

二、 存储方法

   存储像素值是颜色空间和数据类型。颜色空间是指我们如何组合颜色分量以编码给定的颜色。最简单的一个是灰色,我们可以使用的颜色是黑色和白色。这些组合使我们能够创建许多灰色阴影。

   对于丰富多彩的方式,我们有更多的选择方法。他们每个人都将它们分解成三到四个基本组件,我们可以使用这些组合来创建其他组件。最流行的是RGB,主要是因为这也是我们的眼睛如何建立颜色。其基色为红,绿,蓝。为了编码颜色的透明度有时是第四个元素:添加了α(A)。

然而,还有许多其他颜色系统都有自己的优势:

  • RGB是最常见的,因为我们的眼睛使用类似的东西,但请记住,OpenCV标准显示系统使用BGR颜色空间(红色和蓝色通道的开关)组成颜色。
  • HSV和HLS将颜色分解为色调,饱和度和值/亮度分量,这是我们描述颜色的更自然的方式。例如,您可能会忽略最后一个组件,使您的算法对输入图像的光线条件不太敏感。
  • YCrCb被流行的JPEG图像格式使用。
  • CIE L * a * b *是感知统一的颜色空间,如果您需要测量给定颜色与其他颜色的距离,则可方便使用。

   每个建筑组件都有自己的有效域。这导致使用的数据类型。我们如何存储组件定义了我们在其域中的控件。可能的最小数据类型是char,这意味着一个字节或8位。这可能是无符号的(因此可以存储从0到255的值)或带符号(从-127到+127的值)。尽管在三个组件的情况下,这已经提供了1600万个可能的颜色来表示(像在RGB情况下),我们可以通过使用浮点数(4字节= 32位)或双(8字节= 64位)数据来获得更精细的控制每个组件的类型。然而,请记住,增加组件的大小也会增加内存中整个画面的大小。

三、Mat 类

Mat 类的定义如下所示,关键的属性如下方代码所示:

class CV_EXPORTS Mat
{
	public:
	//一系列函数
	...
	/* flag 参数中包含许多关于矩阵的信息,如:
	-Mat 的标识
	-数据是否连续
	-深度
	-通道数目
	*/
	int flags;
	//矩阵的维数,取值应该大于或等于 2
	int dims;
	//矩阵的行数和列数,如果矩阵超过 2 维,这两个变量的值都为-1
	int rows, cols;
	//指向数据的指针
	uchar* data;
	//指向引用计数的指针
	//如果数据是由用户分配的,则为 NULL
	int* refcount;24
	//其他成员变量和成员函数
	...
};

四、创建 Mat 对象

4.1、使用构造函数方法

Mat M(3,2, CV_8UC3, Scalar(0,0,255));

对于二维和多通道图像,我们首先定义它们的大小:行和列数,然后,我们需要指定用于存储元素的数据类型和每个矩阵点的通道数。为此,我们根据以下约定构造了多个定义:

CV_[The number of bits per item][Signed or Unsigned][Type Prefix]C[The channel number]

   该代码创建一个行数(高度)为 3,列数(宽度)为 2 的图像,图像元素是 8 位无符号整数类型,且有三个通道。图像的所有像素值被初始化为(0, 0,255)。由于 OpenCV 中默认的颜色顺序为 BGR,因此这是一个全红色的图像。

4.2、create()函数创建对象

   除了在构造函数中可以创建图像,也可以使用 Mat 类的 create()函数创建图像。如果 create()函数指定的参数与图像之前的参数相同,则不进行实质的内存申请操作;如果参数不同,则减少原始数据内存的索引,并重新申请内存。使用
方法如下面例程所示:

Mat M(2,2, CV_8UC3);//构造函数创建图像
M.create(3,2, CV_8UC2);//释放内存重新创建图像26

需要注意的时,使用 create()函数无法设置图像像素的初始值。
4.3、Matlab 风格的创建对象方法
MATLAB样式初始化器:cv :: Mat :: zeros,cv :: Mat :: ones,cv :: Mat :: eye。指定要使用的大小和数据类型:

Mat Z = Mat::zeros(2,3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl;
Mat O = Mat::ones(2, 3, CV_32F);
cout << "O = " << endl << " " << O << endl;
Mat E = Mat::eye(2, 3, CV_64F);
cout << "E = " << endl << " " << E << endl;

   该代码中,有些 type 参数如 CV_32F 未注明通道数目,这种情况下它表示单通道。
c++ opencv4.5.5 学习笔记(二)cv::Mat-基本图像容器_第3张图片

五、矩阵的基本元素表达

对于单通道图像,其元素类型一般为 8U(即 8 位无符号整数),当然也可以是 16S、 32F 等;这些类型可以直接用 uchar、 short、 float 等 C/C++语言中的基本数据类型表达。
如果多通道图像,如 RGB 彩色图像,需要用三个通道来表示。在这种情况下,如果依然将图像视作一个二维矩阵,那么矩阵的元素不再是基本的数据类型。OpenCV 中有模板类 Vec,可以表示一个向量。 OpenCV 中使用 Vec 类预定义了一些小向量,可以将之用于矩阵元素的表达。

typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;
typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;
typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;
typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;
typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;

   例如 8U 类型的 RGB 彩色图像可以使用 Vec3b, 3 通道 float 类型的矩阵可以使用 Vec3f。
   对于 Vec 对象,可以使用[]符号如操作数组般读写其元素,如:

Vec3b color; //用 color 变量描述一种 RGB 颜色
color[0]=255; //B 分量
color[1]=0; //G 分量
color[2]=0; //R 分量

六、Mat数据操作

两张图片加、减、叠加等操作
此部分搜集于:https://zhuanlan.zhihu.com/p/196737878

int main(int argc, char** argv)
{
Mat src1 = imread("E:\\1.bmp", 0);//0表示单通道,1表示3通道
 Mat src2 = imread("E:\\2.bmp", 0);
 Mat absSub;
 absdiff(src1, src2, absSub);//两图相减的绝对值
 namedWindow("absSub",0);
 imshow("absSub", absSub);
 Mat roiMat = src1(Range(0,3),Range(0,4));//取图像中的0到3行和0到4列
 cout<< roiMat <<endl;
 roiMat = roiMat.t();//图像矩阵转置
 cout<< roiMat <<endl;
 Mat addimg;
 add(src1, src2, addimg);//两图相加
 namedWindow("addimg",0);
 imshow("addimg", addimg);
 Mat addNum;
 add(src1,100, addNum);//图像加一个数字
 namedWindow("addNum",0);
 imshow("addNum", addNum);
 Mat ddWeightedImg;
cv::addWeighted(src1,0.3,src2,0.7,0, ddWeightedImg);//两图带权重相加,可以用于图像融合
 namedWindow("ddWeightedImg",0);
 imshow("ddWeightedImg", ddWeightedImg);
 Mat subImg;
 subtract(src1,src2, subImg);//两图相减
 namedWindow("subImg",0);
 imshow("subImg", subImg);
 Mat subNum;
 subtract(100,src1, subNum);//一个数字减去图像
 namedWindow("subNum",0);
 imshow("subNum", subNum);
 Mat divideImg;
 cv::divide(src1,src2, divideImg,1.0);//两图相除
 namedWindow("divideImg",0);
 imshow("divideImg", divideImg);//不能直接显示,因为是浮点数,需要转换
 Mat mulImg;
 cv::multiply(src1,src2, mulImg,1.0);//两图相乘
 namedWindow("mulImg",0);
 imshow("mulImg", mulImg); //不能直接显示,因为是浮点数,需要转换
 Mat transposeImg;
 cv::transpose(src1, transposeImg);//图像转置
 namedWindow("transposeImg", 0);
 imshow("transposeImg", transposeImg);
 Mat tImg;
 tImg =src1.t();//图像转置,和上面的转置一样
 namedWindow("tImg", 0);
 imshow("tImg", tImg);
 
 Mat copyImg;
 src1.copyTo(copyImg);//图像复制
 namedWindow("copyImg",0);
 imshow("copyImg", copyImg);
 Mat maxImg;
 cv::max(src1,src2, maxImg);//两图中的最大值
 namedWindow("maxImg",0);
 imshow("maxImg", maxImg);
 
 Mat inRangeImg;
 cv::inRange(src1,40,100, inRangeImg);//相当于在一定范围内的值二值化
 namedWindow("inRangeImg",0);
 imshow("inRangeImg", inRangeImg);
 
 int r = src1.rows;
 int c = src2.cols;
 Mat r1 = src1.row(10);
 Scalar meanValue = cv::mean(src1);//图像均值
 cout<<"meanValue=="<< meanValue[0]<<endl;
 
 Mat colRangeImg = src1.colRange(0,10);//取图像中一定列
 Mat rowRangeImg = src1.rowRange(0,10); //取图像中一定行
 
 纵向合并图像
 Mat colMergeImg =Mat(src1.rows*2,src1.cols,0);
 Mat subM = colMergeImg.rowRange(0,src1.rows);
 src1.copyTo(subM);
 subM = colMergeImg.rowRange(src1.rows, src1.rows*2);
 src2.copyTo(subM);
 namedWindow("colMergeImg",0);
 imshow("colMergeImg", colMergeImg);
 ///横向合并矩阵
 Mat rowMergeImg =Mat(src1.rows,src1.cols*2,0);
 subM = rowMergeImg.colRange(0,src1.cols);
 src1.copyTo(subM);
 subM = rowMergeImg.colRange(src1.cols, src1.cols*2);
 src2.copyTo(subM);
 namedWindow("rowMergeImg",0);
 imshow("rowMergeImg", rowMergeImg);
 
 第二种在纵向合并两个图像的方法,横向合并类似,可以先对图像做转置,然后合并,最后再转置回来
 Mat mergeImg2;
 mergeImg2.push_back(src1);
 mergeImg2.push_back(src2);
 namedWindow("mergeImg2", 0);
 imshow("mergeImg2", mergeImg2);
 
 double minValue,maxValue;
 Point minLoc,maxLoc;
minMaxLoc(src1,&minValue,&maxValue,&minLoc,&maxLoc);//取图像矩阵中的最大最小值和位置
 cout<<"minValue=="<<minValue<<endl;
 cout<<"maxValue=="<<maxValue<<endl;
 cout<<"minLoc"<<minLoc.x<<"--"<<minLoc.y<<endl;
 cout<<"maxLoc"<<maxLoc.x<<"--"<<maxLoc.y<<endl;
 Mat mean;
 Mat stddev;
 meanStdDev(src1, mean, stddev);//求图像矩阵的均值和方差
 cout << "mean==" << mean << endl;
 cout << "stddev==" << stddev << endl;
 waitKey(0);
system("pause");
}

你可能感兴趣的:(opencv,学习笔记,opencv,计算机视觉)