OpenCV 之 Mat 类澳门新萄京

  数字图像可看作1个数值矩阵, 其中的各类成分代表三个像素点,如下图所示:

  数字图像可看作1个数值矩阵, 其中的逐个成分代表三个像素点,如下图所示:

   
澳门新萄京 1

   
澳门新萄京 2

   OpenCV 中,用 Mat
来表示该数值矩阵,它是很主要的一种数据结构,因为 OpenCV
中的大多数函数都和 Mat 有关:

   OpenCV 中,用 Mat
来表示该数值矩阵,它是很重点的一种数据结构,因为 OpenCV
中的大多数函数都和 Mat 有关:

   有的是 Mat 的分子函数;有的把 Mat
作为参数;还有的将 Mat 作为再次回到值

   有的是 Mat 的积极分子函数;有的把 Mat
作为参数;还有的将 Mat 作为重临值

 

 

 1  Mat 简介

  Mat,在 OpenCV 中代表的是 N
维稠密矩阵,与细密矩阵绝对的是稀疏矩阵(只存储非零的像素值),后者常用于直方图处理中,OpenCV 中对应为 cv::SparseMat

 
如下所示:第2个为稠密矩阵的蕴藏格局,存储全体的像素数值;第贰个为疏散矩阵的贮存形式,只存储非零的像素值

      $\quad \begin{bmatrix} 0 & 2 & 0 \\ 1 & 0 & 1 \\ 0 & 2 & 0
\end{bmatrix} $        $\quad \begin{bmatrix}  & 2 &  \\ 1 &  & 1
\\  & 2 &  \end{bmatrix} $

  当 N=1 时,所有像素存储为一行;当 N=2
时,全数像素依照一行行的顺序排列;当 N=3
时,全体像素根据一面面的顺序排列,其中一行行的像素构成2个平面。

  下图左,为灰度图的贮存方式;图右,为 RAV4GB
图像的积存形式,注意其储存顺序为 BG翼虎 (Blue->格林->Red)

     澳门新萄京 3    
澳门新萄京 4

 1  Mat 简介

  Mat,在 OpenCV 中意味的是 N
维稠密矩阵,与细密矩阵相对的是稀疏矩阵(只存储非零的像素值),后者常用于直方图处理中,OpenCV 中对应为 cv::SparseMat

 
如下所示:第三个为稠密矩阵的积存格局,存储全部的像素数值;首个为疏散矩阵的仓储格局,只存储非零的像素值

      $\quad \begin{bmatrix} 0 & 2 & 0 \\ 1 & 0 & 1 \\ 0 & 2 & 0
\end{bmatrix} $        $\quad \begin{bmatrix}  & 2 &  \\ 1 &  & 1
\\  & 2 &  \end{bmatrix} $

  当 N=1 时,全部像素存储为一行;当 N=2
时,全部像素依照一行行的顺序排列;当 N=3
时,全数像素依照一面面的顺序排列,其中一行行的像素构成一个平面。

  下图左,为灰度图的囤积格局;图右,为 LANDGB
图像的仓储形式,注意其储存顺序为 BGHighlander (Blue->格林->Red)

     澳门新萄京 5    
澳门新萄京 6

2  Mat 特点

2  Mat 特点

2.1  组成

   Mat 类包括两片段,一是 矩阵头
(matrix header),二是 矩阵指针 (pointer to
matrix),部分矩阵头如下:

int  flags;  // signaling the contents of the matrix
int  dims;   // dimensions
int  rows, cols;  // rows and columns 
MatSize  size;  // 
MatStep  step;  //

  矩阵指针如下,指向包涵全部像素值的矩阵

uchar* data;  // pointer to the data

2.1  组成

   Mat 类包涵两片段,一是 矩阵头
(matrix header),二是 矩阵指针 (pointer to
matrix),部分矩阵头如下:

int  flags;  // signaling the contents of the matrix
int  dims;   // dimensions
int  rows, cols;  // rows and columns 
MatSize  size;  // 
MatStep  step;  //

  矩阵指针如下,指向包括全数像素值的矩阵

uchar* data;  // pointer to the data

2.2  赋值算子

  Mat 类中的赋值算子 “=” 和
拷贝构造函数,涉及的是浅拷贝,因而,当执行那八个操作时,仅仅是复制了矩阵头。

  假诺想要深拷贝,达到复制图像矩阵的目标,应接纳 clone() 或 copyTo() 函数,如下图所示 (摘自参考资料 —
4):

  
澳门新萄京 7

2.2  赋值算子

  Mat 类中的赋值算子 “=” 和
拷贝构造函数,涉及的是浅拷贝,由此,当执行这多个操作时,仅仅是复制了矩阵头。

  假诺想要深拷贝,达到复制图像矩阵的目的,应运用 clone() 或 copyTo() 函数,如下图所示 (摘自参考资料 —
4):

  
澳门新萄京 8

2.3  代码示例

  下边是简约的辨证,将矩阵 m3 透过 copyTo() 函数复制给 m1,而 m2 是由此m1 一直赋值的,二者指向的是一律的数码。因而,假诺改变了
m1,则 m2 对应的矩阵数值,也会展开相应的改动。

Mat m1(3, 3, CV_32FC1, Scalar(1.1f) );
cout << "m1 = " << endl << " " << m1 << endl << endl;
// using assign operator
Mat m2 = m1;
cout << "m2 = " << endl << " " << m2 << endl << endl;

Mat m3(3, 3, CV_32FC1, Scalar(3.3f) );
m3.copyTo(m1);
cout << "m1 = " << endl << " " << m1 << endl << endl;
cout << "m2 = " << endl << " " << m2 << endl << endl;

2.3  代码示例

  下边是归纳的印证,将矩阵 m3 通过 copyTo() 函数复制给 m1,而 m2 是经过
m1 直接赋值的,二者指向的是平等的数码。因而,借使更改了
m1,则 m2 应和的矩阵数值,也会进展相应的更动。

Mat m1(3, 3, CV_32FC1, Scalar(1.1f) );
cout << "m1 = " << endl << " " << m1 << endl << endl;
// using assign operator
Mat m2 = m1;
cout << "m2 = " << endl << " " << m2 << endl << endl;

Mat m3(3, 3, CV_32FC1, Scalar(3.3f) );
m3.copyTo(m1);
cout << "m1 = " << endl << " " << m1 << endl << endl;
cout << "m2 = " << endl << " " << m2 << endl << endl;

3  Mat 创建

3  Mat 创建

3.1  数据类型

  在开创 Mat 在此以前,首先了然 Mat 中成分的数据类型,其格式为 CV_{8U, 16S, 16U, 32S, 32F, 64F}C{1, 2,
3} 或 CV_{8U, 16S, 16U, 32S, 32F,
64F}C(n)

  第一个 {} 内数据表示的意思如下:

CV_8U  - 8-bit 无符号整数 ( 0..255 )
CV_8S  - 8-bit 有符号整数 ( -128..127 )
CV_16U - 16-bit 无符号整数 ( 0..65535 )
CV_16S - 16-bit 有符号整数 ( -32768..32767 )
CV_32S - 32-bit 有符号整数 ( -2147483648..2147483647 )
CV_32F - 32-bit 浮点数 ( -FLT_MAX..FLT_MAX, INF, NAN )
CV_64F - 64-bit 浮点数 ( -DBL_MAX..DBL_MAX, INF, NAN )

 第①个 {} 内的数据 或 (n),表示的是图像矩阵的通道数,CV_8UC3 则相当于于
CV_8UC(3),表示的数据类型为:3通道8个人无符号整数

3.1  数据类型

  在开创 Mat 此前,首先驾驭 Mat 中成分的数据类型,其格式为 CV_{8U, 16S, 16U, 32S, 32F, 64F}C{1, 2,
3} 或 CV_{8U, 16S, 16U, 32S, 32F,
64F}C(n)

  第①个 {} 内数据表示的意思如下:

CV_8U  - 8-bit 无符号整数 ( 0..255 )
CV_8S  - 8-bit 有符号整数 ( -128..127 )
CV_16U - 16-bit 无符号整数 ( 0..65535 )
CV_16S - 16-bit 有符号整数 ( -32768..32767 )
CV_32S - 32-bit 有符号整数 ( -2147483648..2147483647 )
CV_32F - 32-bit 浮点数 ( -FLT_MAX..FLT_MAX, INF, NAN )
CV_64F - 64-bit 浮点数 ( -DBL_MAX..DBL_MAX, INF, NAN )

 第一个 {} 内的数据 或 (n),表示的是图像矩阵的通道数,CV_8UC3 则约等于CV_8UC(3),表示的数据类型为:3坦途五个人无符号整数

3.2  成立方式

3.2  创建格局

3.2.1  构造函数

  创制几个 3 行 5 列,3 通道 32 位,浮点型的矩阵,通道 1, 2, 3
的值分别为 1.1f,2.2f,3.3f

Mat m(3, 5, CV_32FC3, Scalar(1.1f, 2.2f, 3.3f) );
cout << "m = " << endl << " " << m << endl << endl;

  输出的矩阵如下:

   澳门新萄京 9

3.2.1  构造函数

  创造2个 3 行 5 列,3 通道 32 位,浮点型的矩阵,通道 1, 2, 3
的值分别为 1.1f,2.2f,3.3f

Mat m(3, 5, CV_32FC3, Scalar(1.1f, 2.2f, 3.3f) );
cout << "m = " << endl << " " << m << endl << endl;

  输出的矩阵如下:

   澳门新萄京 10

3.2.2  create 函数

  使用 Mat() + create() + setTo(),也得以创设如上的数值矩阵

Mat m;
// Create data area for 3 rows and 10 columns of 3-channel 32-bit floats
m.create(3,5,CV_32FC3);
// Set the values in the 1st channel to 1.0, the 2nd to 0.0, and the 3rd to 1.0
m.setTo(Scalar(1.1f, 2.2f,3.3f));
cout << "m = " << endl << " " << m << endl << endl;

3.2.2  create 函数

  使用 Mat() + create() + setTo(),也足以打造如上的数值矩阵

Mat m;
// Create data area for 3 rows and 10 columns of 3-channel 32-bit floats
m.create(3,5,CV_32FC3);
// Set the values in the 1st channel to 1.0, the 2nd to 0.0, and the 3rd to 1.0
m.setTo(Scalar(1.1f, 2.2f,3.3f));
cout << "m = " << endl << " " << m << endl << endl;

3.2.3  特殊矩阵

  单位矩阵 (ones),对角矩阵 (eye),零矩阵 (zeros),如下所示:

// 单位矩阵
Mat O = Mat::ones(3, 3, CV_32F);
cout << "O = " << endl << " " << O << endl << endl;
// 零矩阵
Mat Z = Mat::zeros(3, 3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl << endl;
// 对角矩阵
Mat E = Mat::eye(3, 3, CV_64F);
cout << "E = " << endl << " " << E << endl << endl;

3.2.3  特殊矩阵

  单位矩阵 (ones),对角矩阵 (eye),零矩阵 (zeros),如下所示:

// 单位矩阵
Mat O = Mat::ones(3, 3, CV_32F);
cout << "O = " << endl << " " << O << endl << endl;
// 零矩阵
Mat Z = Mat::zeros(3, 3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl << endl;
// 对角矩阵
Mat E = Mat::eye(3, 3, CV_64F);
cout << "E = " << endl << " " << E << endl << endl;

4  Mat 遍历

4  Mat 遍历

4.1  at<>() 函数

   常用来遍历 Mat 成分的基本函数为 at<>(),其中 <> 内的数据类型,取决于 Mat 中成分的数据类型,二者的应和关系如下:

CV_8U  --  Mat.at<uchar>(y,x)
CV_8S  --  Mat.at<schar>(y,x)
CV_16U --  Mat.at<ushort>(y,x)
CV_16S --  Mat.at<short>(y,x)
CV_32S --  Mat.at<int>(y,x)
CV_32F --  Mat.at<float>(y,x)
CV_64F --  Mat.at<double>(y,x)

  简单的遍历如下,使用了 Qt 的 qDebug() 来显示输出

Mat m1 = Mat::eye(10, 10, CV_32FC1);
// use qDebug()
qDebug() << "Element (3,3) is : " << m1.at<float>(3,3);  

Mat m2 = Mat::eye(10, 10, CV_32FC2);
// use qDebug()
qDebug() << "Element (3,3) is " << m2.at<cv::Vec2f>(3,3)[0] << "," << m2.at<cv::Vec2f>(3,3)[1];

  注意:at<>() 函数中 () 内,行索引号在前,列索引号在后,也即 (y, x)

4.1  at<>() 函数

   常用来遍历 Mat 成分的骨干函数为 at<>(),其中 <> 内的数据类型,取决于 Mat 中成分的数据类型,二者的照应关系如下:

CV_8U  --  Mat.at<uchar>(y,x)
CV_8S  --  Mat.at<schar>(y,x)
CV_16U --  Mat.at<ushort>(y,x)
CV_16S --  Mat.at<short>(y,x)
CV_32S --  Mat.at<int>(y,x)
CV_32F --  Mat.at<float>(y,x)
CV_64F --  Mat.at<double>(y,x)

  不难的遍历如下,使用了 Qt 的 qDebug() 来呈现输出

Mat m1 = Mat::eye(10, 10, CV_32FC1);
// use qDebug()
qDebug() << "Element (3,3) is : " << m1.at<float>(3,3);  

Mat m2 = Mat::eye(10, 10, CV_32FC2);
// use qDebug()
qDebug() << "Element (3,3) is " << m2.at<cv::Vec2f>(3,3)[0] << "," << m2.at<cv::Vec2f>(3,3)[1];

  注意:at<>() 函数中 () 内,行索引号在前,列索引号在后,也即 (y, x)

4.2  遍历格局

4.2  遍历形式

4.2.1  高效遍历

Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
    // accept only char type matrices
    CV_Assert(I.depth() == CV_8U);
    int channels = I.channels();
    int nRows = I.rows;
    int nCols = I.cols * channels;
    if (I.isContinuous())
    {
        nCols *= nRows;
        nRows = 1;
    }
    int i,j;
    uchar* p;
    for(i=0; i<nRows; ++i)
    {
        p = I.ptr<uchar>(i);
        for (j = 0; j<nCols; ++j)
        {
            p[j] = table[p[j]];
        }
    }
    return I;
}

4.2.1  高效遍历

Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
    // accept only char type matrices
    CV_Assert(I.depth() == CV_8U);
    int channels = I.channels();
    int nRows = I.rows;
    int nCols = I.cols * channels;
    if (I.isContinuous())
    {
        nCols *= nRows;
        nRows = 1;
    }
    int i,j;
    uchar* p;
    for(i=0; i<nRows; ++i)
    {
        p = I.ptr<uchar>(i);
        for (j = 0; j<nCols; ++j)
        {
            p[j] = table[p[j]];
        }
    }
    return I;
}

4.2.2  迭代器遍历

Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
    // accept only char type matrices
    CV_Assert(I.depth() == CV_8U);
    const int channels = I.channels();
    switch(channels)
    {
    case 1:
        {
            MatIterator_<uchar> it, end;
            for(it=I.begin<uchar>(), end=I.end<uchar>(); it!=end; ++it)
                *it = table[*it];
            break;
        }
    case 3:
        {
            MatIterator_<Vec3b> it, end;
            for(it=I.begin<Vec3b>(), end=I.end<Vec3b>(); it!=end; ++it)
            {
                (*it)[0] = table[(*it)[0]];
                (*it)[1] = table[(*it)[1]];
                (*it)[2] = table[(*it)[2]];
            }
        }
    }
    return I;
}

4.2.2  迭代器遍历

Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
    // accept only char type matrices
    CV_Assert(I.depth() == CV_8U);
    const int channels = I.channels();
    switch(channels)
    {
    case 1:
        {
            MatIterator_<uchar> it, end;
            for(it=I.begin<uchar>(), end=I.end<uchar>(); it!=end; ++it)
                *it = table[*it];
            break;
        }
    case 3:
        {
            MatIterator_<Vec3b> it, end;
            for(it=I.begin<Vec3b>(), end=I.end<Vec3b>(); it!=end; ++it)
            {
                (*it)[0] = table[(*it)[0]];
                (*it)[1] = table[(*it)[1]];
                (*it)[2] = table[(*it)[2]];
            }
        }
    }
    return I;
}

 4.2.3  耗时计算

  比较方面三种办法的耗时,可采纳如下代码来进行测算:

double t = (double)getTickCount();
// do something ...
t = ((double)getTickCount() - t)/getTickFrequency();
qDebug() << "Times passed in seconds: " << t << endl; // using qDebug()

 

 4.2.3  耗时统计

  相比方面二种办法的耗时,可应用如下代码来开展总括:

double t = (double)getTickCount();
// do something ...
t = ((double)getTickCount() - t)/getTickFrequency();
qDebug() << "Times passed in seconds: " << t << endl; // using qDebug()

 

参考资料:

 1.  <Learning OpenCV3> chapter 4

 2.  OpenCV Tutorials / The
Core Functionality (core module) / Mat – The Basic Image
Container

 3.  OpenCV Tutorials / The Core
Functionality (core module) / How to scan images, lookup
tables and time measurement with
OpenCV

 4. 
OpenCV基础篇之Mat数据结构 

 

参考资料:

 1.  <Learning OpenCV3> chapter 4

 2.  OpenCV Tutorials / The
Core Functionality (core module) / Mat – The Basic Image
Container

 3.  OpenCV Tutorials / The Core
Functionality (core module) / How to scan images, lookup
tables and time measurement with
OpenCV

 4. 
OpenCV基础篇之Mat数据结构 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注