测试 cv::Mat 数据结构的不同属性
1. 头文件
#include
#include
#include
using namespace std;
using namespace cv;
2. 测试函数,它创建一幅图像
这里需要注意 如果是单纯的function的话,后续调用函数可能会报错,因此我这里加了个1。其次,CV_8U表示每个像素对应1字节(灰度图像),U表示无符号,也可用S表示有符号。
对于彩色图像,应该使用CV_8UC3。也可以定义16位或32位,有符号或者无符号,例如CV_16SC3。甚至可以使用32位或者64位的浮点数,例如CV_32F。
cv::Mat function1() {
//创建图像
cv::Mat ima(500, 500, CV_8U, 50);
//返回图像
return ima;
}
3. 创建一个240行x320列的图像并显示
cv::Mat image1(240, 320, CV_8U, 100);
cv::imshow("Image", image1);//显示图像
cv::waitKey(0);
4. 重新分配一个新图像
可以随时用create分配或重新分配图像的数据块。若图像已被分配,其原来的内容会先被释放。如果新的尺寸与类型与原来相同,就不会重新分配内存。
image1.create(200, 200, CV_8U);
image1 = 200;
cv::imshow("Image", image1);
cv::waitKey(0);
5. 创建红色图像
cv::Scalar,用于在调用函数时传递像素值,该结构一般包含三个值,顺序是BGR。与之类似,初始化灰度图像可以用:cv::Scalar(100)。
//创建一个红色图像,通道次序为BGR
cv::Mat image2(240, 230, CV_8UC3, cv::Scalar(0, 0, 255));
cv::imshow("Image", image2);
cv::waitKey(0);
或者
cv::Size结构包含了矩阵高度和宽度,和cols和rows一样可以提供图像尺寸信息。另外也可以使用size()的方法得到当前矩阵的大小。
cv::Mat image2(cv::Size(320, 240), CV_8UC3);
image2 = cv::Scalar(0, 0, 255);
cv::imshow("Image", image2);
cv::waitKey(0);
6. 测试副本图像的性质
一旦没有了指向cv::Mat对象的引用,分配的内存会自动释放,它的实现方法是通过 cv::Mat 实现计数引用和浅复制。当在两幅图 像之间赋值时,图像数据(即像素)并不会被复制,此时两幅图像都指向同一个内存块。这同样 适用于图像间的值传递或值返回。由于维护了一个引用计数器,因此只有当图像的所有引用都将 释放或赋值给另一幅图像时,内存才会被释放。
如果要对图像内容做一个深复制, 你可以使用 copyTo 方法,目标图像将会调用 create 方法。另一个生成图像副本的方法是 clone, 即创建一个完全相同的新图像
//读入一幅图像
cv::Mat image3 = cv::imread("2.jpg");
//所有这些图像都指向同一个数据块
cv::Mat image4(image3);
image1 = image3;
//这些图像是源图像的副本图像
image3.copyTo(image2);
cv::Mat image5 = image3.clone();
//转换图像进行测试
cv::flip(image3, image3, 1);
//检查哪些图像受到了影响
cv::imshow("Image 3", image3);
cv::imshow("Image 1", image1);
cv::imshow("Image 2", image2);//无影响
cv::imshow("Image 4", image4);
cv::imshow("Image 5", image5);//无影响
cv::waitKey(0);
经过验证得:图像2和5无影响。
分析:若该图像与源图像指向同一个数据块,则源图像的变化会影响该图像变化;副本图像不会受源图像变化影响。
7. 关于灰度图像
如果需要把一幅图像复制到另一幅图像中,且两者的数据类型不一定相同,那就要使用 convertTo 方法了。
以下代码的原始图像被复制进了一幅浮点型图像。这一方法包含两个可选参数:缩放比例和偏 移量。需要注意的是,这两幅图像的通道数量必须相同。
cv::Mat image1;
cv::Mat image2 = cv::imread("2.jpg");
//从函数中获取一个灰度图像
cv::Mat gray = function1();
cv::imshow("Image", gray);
cv::waitKey(0);
//作为灰度图像读入
image1 = cv::imread("2.jpg", CV_LOAD_IMAGE_GRAYSCALE);
image1.convertTo(image2, CV_32F, 1 / 255.0, 0.0);
cv::imshow("Image", image2);
cv::waitKey(0);
8. 易错点
如果某个函数调用了这个类的 method,就会对图像属性进行一次浅复制。副本一旦被修改, class 属性也会被“偷偷地”修改,这会影响这个类的后续行为(反之亦然)。这违反了面向对 象编程中要的封装性原理。为了避免这种类型的错误,你需要将其改成返回属性的一个副本。
class Test {
// 图像属性
cv::Mat ima;
public:
// 在构造函数中创建一幅灰度图像
Test() : ima(240,320,CV_8U,cv::Scalar(100)) {}
// 用这种方法回送一个类属性,这是一种不好的做法
cv::Mat method() { return ima; }
};