opencv中Mat介绍

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中像素数据的存储形式:一般是二维向量,如果是灰度图,一般存放类型;如果是RGB彩色图,存放类型

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

多通道的图像中,每列并列存放通道数量的子列,如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,r
imshow("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(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(x, y);
uchar blue = intensity.val[0];
uchar green = intensity.val[1];
uchar red = intensity.val[2];

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

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

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

img.at(x, y) = 128;

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

vector points;
//... fill the array
Mat pointsMat = Mat(points);

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

Point2f point = pointsMat.at(i, 0);
    Mat cc = Mat(100, 100, CV_8UC3, Scalar(0, 0, 0));
    Mat bb = Mat(100, 100, CV_8UC3, Scalar(0, 0, 0));
    for (int i = 0; i < cc.rows; i++)
    {
        for (int j = 0; j < cc.cols; j++)
        {
            intensity[i][j] = cc.at(i, j);
            Vec3b intensity1 = cc.at(i, j);
            b[i][j] = intensity1.val[0];  // 修改blue通道
            g[i][j] = intensity1.val[1];  // 修改green通道
            r[i][j] = intensity1.val[2];  // 修改red通道
            bb.at(i, j)[0] = 255;
        }
    }

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(j);  
         for (int i=0; i
    //Scalar()设置初始化值,依次是b,g,r
    Mat cc = Mat(10, 10, CV_8UC3, Scalar(0, 0, 0));
    for (int i = 0; i < cc.rows; i++)
    {
        uchar* pt = cc.ptr<uchar>(i);
        for (int j = 0, k = 0; j < cc.cols; j++)
        {
            pt[k] = 255;    // blue通道
            pt[k + 1] = 0;  // green通道
            pt[k + 2] = 0;  // red通道
            k = k + 3;
        }
    }

使用指针++

// 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(j);  
          for (int i=0; i

指针运算(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

3、Mat _iterator

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

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

五、基本图像操作

将图像转为灰度图像

Mat img = imread("image.jpg"); // loading a 8UC3 image
Mat 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

你可能感兴趣的:(计算机视觉)