3.图像对象的创建与赋值
cv::Mat src = cv::imread("E:/Softwares/OpenCV_Study/lena.png", cv::IMREAD_UNCHANGED);
cv::Mat m1 = src.clone(); // 创建方法-克隆 创建了src的一个完全独立的副本,并将其存储到名为m1的cv::Mat对象中。
cv::Mat m2;
src.copyTo(m2); // 将src的内容复制到了一个新的cv::Mat对象m2中
cv::Mat m3 = src; // 赋值法 将src的引用赋给了m3,这意味着m3和src指向相同的数据,修改其中一个会影响另一个
cv::Mat m4 = cv::Mat::zeros(src.size(), src.type()); // 创建和src相同尺寸和通道数的全零矩阵
cv::Mat m5 = cv::Mat::zeros(cv::Size(512, 512), CV_8UC3); // 创建尺寸为512x512、三通道的全零矩阵
cv::Mat m6 = cv::Mat::ones(cv::Size(512, 512), CV_8UC3); // 创建尺寸为512x512、三通道的全一矩阵
cv::Mat kernel = (cv::Mat_<char>(3, 3) << 0, -1, 0, // 创建3x3的矩阵作为图像卷积核
-1, 5, -1,
0, -1, 0);
clone()
和copyTo()
都是深拷贝,它们创建了源cv::Mat
对象的完全独立副本,即新的对象拥有自己的内存空间,而不是简单地共享源对象的数据。
区别在于:
clone()
是cv::Mat
类的成员函数,它创建一个新的cv::Mat
对象,并将源对象的数据复制到新对象中。它的语法是src.clone()
,返回一个新的cv::Mat
对象。copyTo()
是cv::Mat
类的成员函数,用于将源cv::Mat
对象的数据复制到另一个已经创建好的cv::Mat
对象中。它的语法是src.copyTo(dst)
,其中dst
是已经创建好的目标cv::Mat
对象。copyTo()
还允许在复制过程中进行类型转换和区域选择等操作。
而直接使用赋值操作符(=
)时,只是简单地将源对象的指针或引用赋给了目标对象,它们指向相同的数据。这意味着修改其中一个对象的内容将会影响到另一个对象,因为它们共享相同的数据存储空间。
CV_8UC3
是OpenCV中用于表示图像数据类型的一种标记。这个标记通常用于指定图像的数据类型,其中:
CV_8UC3
表示图像每个通道都是8位无符号的,且有三个通道,即每个像素由三个8位无符号整数组成,通常用于表示彩色图像。C3
表示图像是三通道的,即每个像素有三个分量(通道):蓝色(Blue)、绿色(Green)和红色(Red)。
因此,CV_8UC3
表示每个像素由三个8位无符号整数组成,用于表示彩色图像。
这个标记通常在创建新的cv::Mat
对象时用于指定图像的数据类型,例如:
cv::Mat image(Size(width, height), CV_8UC3);
这样就创建了一个宽度为width
,高度为height
的彩色图像,每个像素由三个8位无符号整数组成。
在示例代码中cv::Mat kernel
可以用于创建卷积核,它是一个二维数组,用于图像处理中的卷积操作。卷积核的数值决定了在卷积过程中每个像素周围的加权求和方式,从而实现不同的图像处理效果。这个卷积核是一个3x3的矩阵,用于对图像进行锐化处理。在这个卷积核中,中心像素的权重为5,周围像素的权重为-1,通过对图像进行卷积操作,可以增强图像中的边缘信息。
cv::Mat m3 = cv::Mat::zeros(cv::Size(8, 8), CV_8UC1);
std::cout << m3 << std::endl;
这行代码创建了一个8x8大小的单通道(灰度)图像,每个像素由一个8位无符号整数(uchar)表示。因为它是单通道的,所以每个像素只包含灰度信息,范围从0到255。
-
运行结果:
cv::Mat m3 = cv::Mat::zeros(cv::Size(8, 8), CV_8UC3);
std::cout << m3 << std::endl;
-
运行结果:
这行代码创建了一个8x8大小的三通道图像,每个通道由一个8位无符号整数(uchar)表示。这种类型的图像通常用于表示彩色图像,每个像素有红、绿、蓝三个通道的信息。每个通道的范围仍然是0到255。
因此,这两个cv::Mat
对象的主要区别在于通道数的不同。第一个只有一个通道,而第二个有三个通道,分别对应于彩色图像中的红、绿、蓝三种颜色通道。
cv::Mat m3 = cv::Mat::zeros(cv::Size(8, 8), CV_8UC3);
std::cout << "width: " << m3.cols << std::endl; // 8
std::cout << "height: " << m3.rows << std::endl; // 8
std::cout << "channels: " << m3.channels() << std::endl; // 3
- 这段代码中的
m3.cols
返回图像的宽度,即8;m3.rows
返回图像的高度,也是8;m3.channels()
返回图像的通道数,即3。
cv::Mat m3 = cv::Mat::ones(cv::Size(8, 8), CV_8UC1);
std::cout << "width: " << m3.cols << std::endl; // 8
std::cout << "height: " << m3.rows << std::endl; // 8
std::cout << "channels: " << m3.channels() << std::endl; // 1
std::cout << m3 << std::endl;
- 这段代码因为是单通道图像,所以输出的矩阵是一个以分号和空格分隔的8x8矩阵,每个元素值都是1。
cv::Mat m3 = cv::Mat::ones(cv::Size(8, 8), CV_8UC3);
- 以上代码尤其需要注意的是
cv::Mat::ones()
函数会创建一个所有像素值都为1的图像,但是在三通道图像中,使用参数CV_8UC3
,每个像素的通道值将会是[1, 0, 0],因为ones
函数只设置了第一个通道为1,而其他两个通道为默认值0。
cv::Mat m3 = cv::Mat::zeros(cv::Size(8, 8), CV_8UC3);
m3 = 127;
std::cout << m3 << std::endl;
-
这段代码创建了一个8x8大小的三通道图像,初始值为全零(即[0, 0, 0])。然后,将整个图像矩阵设置为了127。在这种情况下,每个像素的每个通道都会被设置为127,因为将一个标量值赋给了整个
cv::Mat
对象 -
运行结果:
-
那么问题来了,想要将三个通道都变为直接值该如何操作
cv::Mat m3 = cv::Mat::zeros(cv::Size(8, 8), CV_8UC3);
m3 = cv::Scalar(127, 127, 127);
-
运行结果:
-
cv::Scalar(127, 127, 127)
创建了一个cv::Scalar
对象,其中包含了三个值,分别是 127,127 和 127。在 OpenCV 中,cv::Scalar
主要用于表示颜色值,其中的三个值分别对应于bgr三个颜色通道(蓝色、绿色、红色),也可以表示其他的多通道数据。在这这段代码中,
cv::Scalar(127, 127, 127)
可以表示一个灰度颜色,其中每个颜色通道的值都是 127。在设置图像的像素值时,可以使用cv::Scalar
对象来指定颜色值,以方便处理不同通道数的情况。
cv::Mat m3 = cv::Mat::zeros(cv::Size(512, 512), CV_8UC3);
m3 = cv::Scalar(127, 127, 127);
std::cout << "width: " << m3.cols << std::endl;
std::cout << "height: " << m3.rows << std::endl;
std::cout << "channels: " << m3.channels() << std::endl;
std::cout << m3 << std::endl;
cv::imshow("create mat", m3);
-
运行结果:
-
这段代码首先创建了一个大小为512x512的三通道图像
m3
,初始值为全黑色,然后将整个图像矩阵设置为灰度值为127的灰色,每个通道的值都是127。接着,代码输出了图像的宽度、高度和通道数,并打印了图像的像素值。最后,使用cv::imshow()
函数显示了创建的图像。这个代码段的输出结果应该是图像的宽度为512,高度为512,通道数为3(表示三通道图像),并且图像呈现出灰度值为127的灰色。 -
IO 操作通常是相对较慢的,因此在实际的程序中,尽量减少不必要的 IO 操作是一个很好的做法。在调试阶段,输出一些重要的信息来验证程序的正确性是有意义的,但在生产环境中,应该尽可能地减少不必要的输出操作,以提高程序的执行效率。在上述示例中,如果不需要实时查看图像,可以将
cv::imshow()
函数调用移除,这样可以减少不必要的图像显示操作,提高程序的执行效率。
cv::Mat m3 = cv::Mat::zeros(cv::Size(300, 300), CV_8UC3);
m3 = cv::Scalar(255, 0, 0);
std::cout << "width: " << m3.cols << std::endl;
std::cout << "height: " << m3.rows << std::endl;
std::cout << "channels: " << m3.channels() << std::endl;
std::cout << m3 << std::endl;
cv::imshow("blue mat", m3);
-
这段代码创建了一个大小为300x300的三通道图像
m3
,初始值为全黑色。然后,将整个图像矩阵设置为蓝色,即每个像素的通道值为 [255, 0, 0]。接着,代码输出了图像的宽度、高度和通道数,并打印了图像的像素值。最后,使用cv::imshow()
函数显示了创建的蓝色图像。这个代码段的输出结果应该是图像的宽度为300,高度为300,通道数为3(表示三通道图像),并且图像呈现出蓝色。 -
通过以上两种示例得知使用
cv::Mat::zeros()
函数可以创建一个全黑的图像,然后使用cv::Scalar()
函数来赋予不同的颜色。这种方法可以创建不同颜色的图像,具体的颜色由cv::Scalar()
函数的参数确定,分别对应蓝、绿、红三个通道的值。例如,要创建一个红色的图像,可以将
cv::Scalar()
的参数设置为(0, 0, 255)
,表示红色通道值为255,而绿色和蓝色通道的值为0。类似地,可以根据需要创建其他颜色的图像。
综合示例:
cv::Mat m3 = cv::Mat::zeros(cv::Size(300, 300), CV_8UC3);
m3 = cv::Scalar(255, 0, 0);
std::cout << "width: " << m3.cols << std::endl;
std::cout << "height: " << m3.rows << std::endl;
std::cout << "channels: " << m3.channels() << std::endl;
cv::Mat m4 = m3;
m4 = cv::Scalar(0, 0, 255);
cv::imshow("mat", m3);
-
运行结果:
cv::Mat m3 = cv::Mat::zeros(cv::Size(300, 300), CV_8UC3);
m3 = cv::Scalar(255, 0, 0);
std::cout << "width: " << m3.cols << std::endl;
std::cout << "height: " << m3.rows << std::endl;
std::cout << "channels: " << m3.channels() << std::endl;
cv::Mat m4 = m3.clone(); // 深拷贝
m4 = cv::Scalar(0, 0, 255);
cv::imshow("mat m3", m3);
cv::imshow("mat m4", m4);
cv::Mat m3 = cv::Mat::zeros(cv::Size(300, 300), CV_8UC3);
m3 = cv::Scalar(255, 0, 0);
std::cout << "width: " << m3.cols << std::endl;
std::cout << "height: " << m3.rows << std::endl;
std::cout << "channels: " << m3.channels() << std::endl;
cv::Mat m4;
m3.copyTo(m4); // 深拷贝
m4 = cv::Scalar(0, 0, 255);
cv::imshow("mat m3", m3);
cv::imshow("mat m4", m4);
-
以上两段代码会产生同样的运行效果:
-
以上两段代码都是创建了两个
cv::Mat
对象m3
和m4
,并设置它们的像素值。主要的区别在于深拷贝的使用。第一段代码中,使用了
m4 = m3.clone()
进行深拷贝,确保了m4
和m3
是完全独立的对象,因此修改m4
的值不会影响到m3
,反之亦然。而第二段代码中,使用了
m3.copyTo(m4)
进行深拷贝,同样也确保了m4
和m3
是完全独立的对象,因此修改m4
的值不会影响到m3
,反之亦然。总结起来,这两段代码都创建了两个相同的图像对象,然后对其中一个对象进行了深拷贝,确保了它们的独立性,从而可以对它们进行不同的操作而不会相互影响。