opencv中Mat介绍

来源:互联网 发布:福建顶点软件 垃圾 编辑:程序博客网 时间:2024/06/05 08:27

opencv中Mat–基本图像容器简单用法


参考自 http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/user_guide/ug_mat.html

以前opencv提供IplImage 的C语言结构体存储一张图片,现在opencv有提供了一个C++接口Mat用来存储图像,和之前C语言的IplImage比,Mat提供了自动的内存管理,使用这个方法,你不需要纠结在管理内存上,而且你的代码会变得简洁(少写多得)。

关于 Mat ,首先要知道的是,你不必再手动地为其开辟空间,在不需要时立即将空间释放。

一、Mat

Mat是一个类由两个数据部分组成:

  • 矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)
  • 一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针

Mat中像素数据的存储形式:一般是二维向量,如果是灰度图,一般存放<uchar>类型;如果是RGB彩色图,存放<Vec3b>类型

单通道灰度图数据存放格式:

多通道的图像中,每列并列存放通道数量的子列,如RGB三通道彩色图:

矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。

Mat中各个属性介绍

data

uchar类型的指针,指向Mat数据矩阵的首地址。Mat提供了isContinuous()函数用来查看Mat在内存中是不是连续存储,如果是则图片被存储在一行中,访问各像素值时利用首地址可以用数组下标方式[],地址++方式或迭代器方式访问

dims

Mat矩阵的维度,若Mat是一个二维矩阵,则dims=2,三维则dims=3,大多数情况下处理的都是二维矩阵。

rows

Mat矩阵的行数。

cols

Mat矩阵的列数。

size()

先size是一个结构体,定义了Mat矩阵内数据的分布形式,数值上有关系式:

image.size().width==image.cols;

image.size().height==image.rows

channels()

Mat矩阵元素拥有的通道数。例如常见的RGB彩色图像,channels==3;而灰度图像只有一个灰度分量信息,channels==1。

depth

用来度量每一个像素中每一个通道的精度,但它本身与图像的通道数无关!depth数值越大,精度越高。在 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 }

其中U是unsigned的意思,S表示signed,也就是有符号和无符号数。

elemSize

elem是element(元素)的缩写,表示矩阵中每一个元素的数据大小,如果Mat中的数据类型是CV_8UC1,那么elemSize==1;如果是CV_8UC3CV_8SC3,那么elemSize==3;如果是CV_16UC3或者CV_16SC3,那么elemSize==6;即elemSize是以8位(一个字节)为一个单位,乘以通道数和8位的整数倍;

elemSize1

elemSize加上一个“1”构成了elemSize1这个属性,1可以认为是元素内1个通道的意思,这样从命名上拆分后就很容易解释这个属性了:表示Mat矩阵中每一个元素单个通道的数据大小,以字节为一个单位,所以有:

eleSize1==elemSize/channels;

step

可以理解为Mat矩阵中每一行的“步长”,以字节为基本单位,每一行中所有元素的字节总量,是累计了一行中所有元素、所有通道、所有通道的elemSize1之后的值;

step1()

以字节为基本单位,Mat矩阵中每一个像素的大小,累计了所有通道、所有通道的elemSize1之后的值,所以有:

step1==step/elemSize1;

type

Mat矩阵的类型,包含有矩阵中元素的类型以及通道数信息,type的命名格式为CV_(位数)+(数据类型)+(通道 数),所有取值如下:

enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 }

二、Mat对象的创建

Mat直接读入图片

Mat input1 = imread("F:/test/0.jpg");

Mat构造函数

Mat img(512, 1024, CV_8UC3, Scalar(0, 0, 255)); // Scalar()设置初始化值,依次是b,g,rimshow("img",img);  // 显示图片imwrite("path/img.jpg",img);    // 保存图片

对于二维多通道图像,首先要定义其尺寸,即行数和列数,如上所示512行,1024列。

然后,需要指定存储元素的数据类型以及每个矩阵点的通道数。为此,依据下面的规则有多种定义

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

比如CV_8UC3 表示使用8位的 unsigned char 型,每个像素由三个元素组成三通道。

输出所有像素点

cout << "M = "<< endl << " "  << M << endl << endl;

格式化打印
调用函数 randu() 来对一个矩阵使用随机数填充,需要指定随机数的上界和下界:

Mat R = Mat(3, 2, CV_8UC3);randu(R, Scalar::all(0), Scalar::all(255));

三、Mat数据的拷贝

1、只拷贝信息头和矩阵指针,而不拷贝矩阵(浅拷贝)

Mat A, C;                                 // 只创建信息头部分A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存Mat B(A);                                 // 使用拷贝构造函数C = A;                                    // 赋值运算符

以上代码中的所有Mat对象最终都指向同一个也是唯一一个数据矩阵。虽然它们的信息头不同,但通过任何一个对象所做的改变也会影响其它对象。

2、拷贝所有数据(矩阵头+像素信息)(深拷贝)

可以使用函数 clone() 或者 copyTo()

Mat F = A.clone();Mat G;A.copyTo(G);

注意以下几点

  • OpenCV函数中输出图像的内存分配是自动完成的(如果不特别指定的话)。
  • 使用OpenCV的C++接口时不需要考虑内存释放问题。
  • 赋值运算符和拷贝构造函数( ctor )只拷贝信息头。
  • 使用函数 clone() 或者 copyTo() 来拷贝一副图像的矩阵.

四、访问Mat每个点的像素值

1、使用Mat::at(x,y)访问

要获取像素的亮度值,你必须知道图像的类型和通道的数目。如下例子展示了获取单通道灰度图(类型 8UC1)的(x, y)位置处的像素值:

Scalar intensity = img.at<uchar>(x, y);

intensity.val[0] 中保存从0到255的值。注意:(x,y)x在这里指的是行y指的是列,从图像左上角开始。这里Scalar是一个可存放1—4个数值的数值的结构体

typedef struct Scalar{    double val[4];}Scalar;

如果使用的图像是1通道的,则s.val[0]中存储数据,如果使用的图像是3通道的,则s.val[0],s.val[1],s.val[2]中存储数据,颜色顺序为 BGR,如下

Vec3b intensity = img.at<Vec3b>(x, y);uchar blue = intensity.val[0];uchar green = intensity.val[1];uchar red = intensity.val[2];

你可以使用同样的方法处理浮点图像(例如通对一个3通道图像进行Sobel运算得到的浮点图像):

Vec3f intensity = img.at<Vec3f>(x, y);float blue = intensity.val[0];float green = intensity.val[1];float red = intensity.val[2];

同样的方法也可用于像素值的修改:

img.at<uchar>(x, y) = 128;

一些OpenCV函数,例如calib3d模块中的 projectPoints 函数,需要以 Mat 的格式输入二维或者三维的点。这样的矩阵必须有且仅有一列,这样每行对应一个点,矩阵类型需要是32FC2或者32FC3。这样的矩阵可以很容易的从 std::vector 转换而来:

vector<Point2f> points;//... fill the arrayMat pointsMat = Mat(points);

您也可以通过 Mat::at 方法来读写矩阵中的一个元素:

Point2f point = pointsMat.at<Point2f>(i, 0);

2、使用.ptr操作

(参考:http://blog.csdn.net/xiaowei_cqu/article/details/19839019)

使用ptr和[]操作符

Mat最直接的访问方法是通过.ptr<>函数得到一行的指针,并用[]操作符访问某一列的像素值。

// using .ptr and []  void colorReduce0(cv::Mat &image, int div=64) {        int nr= image.rows; // number of rows        int nc= image.cols * image.channels(); // total number of elements per line        for (int j=0; j<nr; j++) {           uchar* data= image.ptr<uchar>(j);           for (int i=0; i<nc; i++) {                    data[i]= data[i]/div*div + div/2;             }                         }  }  

使用指针++

// using .ptr and * ++   void colorReduce1(cv::Mat &image, int div=64) {        int nr= image.rows; // number of rows        int nc= image.cols * image.channels(); // total number of elements per line        for (int j=0; j<nr; j++) {            uchar* data= image.ptr<uchar>(j);            for (int i=0; i<nc; i++) {                   *data++= *data/div*div + div/2;              } // end of row                         }  }

指针运算(step值为每行的长度)

int nr= image.rows; // number of rows  int nc= image.cols * image.channels();int step= image.step; // effective width uchar *data= image.data;  for (int j=0; j<nr; j++) {       for (int i=0; i<nc; i++)     {         *(data+i)= *data&mask + div/2;      } // end of row                       data+= step;  // next line  }

3、Mat _iterator

迭代器方法,官方推荐方法

// using Mat_ iterator   void colorReduce8(cv::Mat &image, int div=64) {        // get iterators        cv::Mat_<cv::Vec3b>::iterator it= image.begin<cv::Vec3b>();        cv::Mat_<cv::Vec3b>::iterator itend= image.end<cv::Vec3b>();        for ( ; it!= itend; ++it) {          (*it)[0]= 255;          (*it)[1]= 255;          (*it)[2]= 255;        }  }  

五、基本图像操作

将图像转为灰度图像

Mat img = imread("image.jpg"); // loading a 8UC3 imageMat grey;cvtColor(img, grey, CV_BGR2GRAY);

灰度图的灰度信息就是YUV里的Y亮度信息,其实就是RGB和YUV色度空间的转换,每个像素点值的计算方式,

uchar gray = (uchar)(0.299*red + 0.587*green + 0.114*blue);

将图像的类型从8UC1转为32FC1

src.convertTo(dst, CV_32F);

调整图片尺寸

resize(input1, input1, Size(780, 1020), 0, 0, CV_INTER_LINEAR); // 行为1020 列为780
0 0