VS+openCV之深入了解 cv::Mat

测试 cv::Mat 数据结构的不同属性

1. 头文件

#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<iostream>
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; } 
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值