首页 > 其他 > 详细

OpenCV学习笔记(二) cv::Mat

时间:2014-03-02 18:47:35      阅读:742      评论:0      收藏:0      [点我收藏+]

部分内容转自:

http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/mat%20-%20the%20basic%20image%20container/mat%20-%20the%20basic%20image%20container.html#matthebasicimagecontainer

http://ggicci.blog.163.com/blog/static/210364096201261052543349/

 

Mat本质上是由两个数据部分组成的类:

  • 矩阵头,包含信息有矩阵的大小,用于存储的方法,矩阵存储的地址等
  • 一个指针,指向包含了像素值的矩阵(可根据选择用于存储的方法采用任何维度存储数据)。

矩阵头部的大小是恒定的。然而,矩阵本身的大小因图像的不同而不同,通常是较大的数量级。因此,当你在您的程序中传递图像并在有些时候创建图像副本您需要花费很大的代价生成图像矩阵本身,而不是图像的头部。为了解决这一问题 OpenCV 使用引用计数系统。其思想是Mat的每个对象具有其自己的头,但可能他们通过让他们矩阵指针指向同一地址的两个实例之间共享该矩阵。此外,拷贝运算符将只能复制矩阵头部,也还将复制指针到大型矩阵,但不是矩阵本身。

特性

  • reference counting:当counting为0时,会自动释放内存;
  • shallow copy:当令mat1=mat2时,二者指向的是同一份image data,对mat2的修改会等效作用于mat1上,如果确实要拷贝出一个副本时,需要调用copyTo函数或者clone函数。

 

1
2
3
4
5
6
7
8
9
//image1属于shallow copy,而image2是赋值了一个副本,当result发生变化时,image1同时发生变化,而image2不变
cv::Mat image1, image2, result=cv::imread("image.jpg");
image1 = result;
result.copyTo(image2);
cv::flip(result, result, 1);
cv::namedWindow("IMAGE1");
cv::imshow("IMAGE1", image1);
cv::namedWindow("IMAGE2");
cv::imshow("IMAGE2", image2);

注意,这个特性要求OpenCV中的类应该返回副本copyTo(returnMat),否则当某个类的实体发生改变时,该类的其余实体都会发生改变。

  • 出于效率优化,每行的结尾可能存在padding,使得每行大小是2的整数次幂,可以通过M.isContinuous()判断是否存在padding(True:不存在padding)。当不存在padding时,Mat image的内存占用为(byte)=image.elemSize() * image.total()

属性

  • data:Mat对象中的一个指针,指向内存中存放矩阵数据的一块内存 (uchar* data)
  • dims:Mat所代表的矩阵的维度,如 3 * 4 的矩阵为 2 维, 3 * 4 * 5 的为3维
  • channels():通道,矩阵中的每一个矩阵元素拥有的值的个数,比如说 3 * 4 矩阵中一共 12 个元素,如果每个元素有三个值,那么就说这个矩阵是 3 通道的,即 channels = 3。常见的是一张彩色图片有红、绿、蓝三个通道。
  • depth():深度,即每一个像素的位数(bits),在opencv的Mat.depth()中得到的是一个 0 – 6 的数字,分别代表不同的位数:enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 }; 可见 0和1都代表8位, 2和3都代表16位,4和5代表32位,6代表64位;
  • step:是一个数组,定义了矩阵的布局,包括padding部分,具体见下面图片分析,另外注意M.step[m-1] == M.elemSize();
  • step1(n) == step[n] / elemSize1,M.step1(m-1)总是等于 channels;
  • elemSize() : 矩阵中每一个元素的数据大小,如果Mat中的数据的数据类型是 CV_8U 那么 elemSize = 1,CV_8UC3 那么 elemSize = 3,CV_16UC2 那么 elemSize = 4;
  • elemSize1(): 表示的是矩阵中数据类型的大小,即 elemSize / channels 的大小
  • total():像素的总数

地址计算

灰度图的每个像素都是0~255的8 bit值。彩色图有BGR三通道,其像素可视为一个三维向量,每个分量也是一个0~255的8 bit值。代码中有时存在第四维alpha,表示透明度。

最小的数据类型可能是 char 类型,这意味着一个字节或 8 位。这可能是有符号(值-127 到 + 127)或无符号(以便可以存储从 0 到 255 之间的值)。虽然这三个组件的情况下已经给 16 万可能的颜色来表示 (如 RGB 的情况下),我们可通过使用浮点数 (4 字节 = 32 位) 或double(8 字节 = 64 位) 数据类型的每个组件获得甚至更精细的控制。

注意:当目标为ROI时,地址计算失效。

addr(Mi0,i1,…im-1)=M.data + M.step[0] * i0 + M.step[1] * i1 + … + M.step[m-1] * im-1 

(其中 m = M.dims M的维度)

考虑二维情况(stored row by row)按行存储:

bubuko.com,布布扣
  • M.dims == 2 ; 

当数据类型为 CV_8U单通道的 uchar 时:

  • M.channels() == 1 ;
  • M.elemSize() == 1 
  • M.elemSize1() ==  1 ;
  • M.step[0] ==  4  ; 
  • M.step[1] == 1; 
  • M.step1(0) == 4; 
  • M.step1(1) == 1;

当数据类型是 CV_8UC3三通道:

  • M.channels() == 3;
  • M.elemSize() == 3 
  • M.elemSize1() == 1 
  • M.step[0] == 12 ;
  • M.step[1] ==  3;
  • M.step1(0) == 12 ;
  • M.step1(1) ==  3;

 

bubuko.com,布布扣  当数据类型为 CV_16SC4,也就是 short 类型:
  • M.dims == 3 ;
  • M.rows == M.cols == –1;
  • M.channels() == 4 ;
  • M.elemSize1() == sizeof(short) == 2 ;
  • M.elemSize() == M.elemSize1() * M.channels() == M.step[M.dims-1] == M.step[2] == 2 * 4 == 8;
  • M.step[0] == 4 * 6 * M.elemSize() == 192;
  • M.step[1] == 6 * M.elemSize() == 48;
  • M.step[2] == M.elemSize() == 8;
  • M.step1(0) == M.step[0] / M.elemSize() == 48 / 2 == 96 (第一维度(即面的元素个数) * 通道数);
  • M.step1(1) == M.step[1] / M.elemSize() == 12 / 2 == 24(第二维度(即行的元素个数/列宽) * 通道数);
  • M.step1(2) == M.step[2] / M.elemSize() == M.channels() == 4(第三维度(即元素) * 通道数);

其他

P.S.1
在OpenCV1中采用的IplImage(Intel Image Processing Library)类型应尽量不再使用。可用以下方法将IplImage转为Mat:

1
2
3
IplImage* iplImage = cvLoadImage("c:\\img.jpg");
 
cv::Mat image(iplImage,false); //false是默认参数,表示浅拷贝,即image指向同一区域,不额外占用空间。

若确实需要使用IplImage时,应注意dangling pointer的问题,可选择:

  • 使用reference counting pointer:cv::Ptr<IplImage> iplImage = cvLoadImage("c:\\img.jpg");
  • 显式销毁指针:cvReleaseImage(&iplImage);

 P.S.2

Mat 中的channel是BGR,在Qt中显示的图像需是QImage类型(通道为RGB),可通过以下方式转换:

1
2
3
4
5
6
7
8
// change color channel ordering
cv::cvtColor(image,image,CV_BGR2RGB);
// Qt image
QImage img= QImage((const unsigned char*)(image.data), image.cols,image.rows,QImage::Format_RGB888);
// display on label
ui->label->setPixmap(QPixmap::fromImage(img));
// resize the label to fit the image
ui->label->resize(ui->label->pixmap()->size());

OpenCV学习笔记(二) cv::Mat,布布扣,bubuko.com

OpenCV学习笔记(二) cv::Mat

原文:http://www.cnblogs.com/ericxing/p/3575541.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!