• OpenCv Mat操作总结


    Author:: Maddock

    Date: 2015-03-23 16:33:49

    转载请注明出处:http://blog.csdn.net/adong76/article/details/40539357

    参考

    http://blog.csdn.net/ljbkiss/article/details/7381208

    http://blog.csdn.net/yang_xian521/article/details/7161335#comments

    http://blog.csdn.net/guoming0000/article/details/8629885

    http://hahack.com/wiki/opencv-basic.html

    http://blog.skyoung.org/2014/03/26/OpenCV%28III%29-How-to-use-Mat/

    Mat的基本数据结构

    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

    Mat类的数据结构如下:

    class CV_EXPORTS Mat

    {

    public:

        // ... a lot of methods ...

        ...

        /*! includes several bit-fields:

             - the magic signature

             - continuity flag

             - depth

             - number of channels

         */

        int flags;

        //! the array dimensionality, >= 2

        int dims;

        //! the number of rows and columns or (-1, -1) when the array has more than 2 dimensions

        int rows, cols;

        //! pointer to the data

        uchar* data;

        //! pointer to the reference counter;

        // when array points to user-allocated data, the pointer is NULL

        int* refcount;

        // other members

        ...

    };

    关于Mat ,首先要知道的是你不必再手动地(1)为其开辟空间(2)在不需要时立即将空间释放。但手动地做还是可以的:大多数OpenCV函数仍会手动地为输出数据开辟空间。当传递一个已经存在的 Mat 对象时,开辟好的矩阵空间会被重用。也就是说,我们每次都使用大小正好的内存来完成任务。

    基本上讲 Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不 同的维数)的指针。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。因此,当在程序中传递图像并创建拷贝 时,大的开销是由矩阵造成的,而不是信息头。OpenCV是一个图像处理库,囊括了大量的图像处理函数,为了解决问题通常要使用库中的多个函数,因此在函 数中传递图像是家常便饭。同时不要忘了我们正在讨论的是计算量很大的图像处理算法,因此,除非万不得已,我们不应该拷贝 的图像,因为这会降低程序速度。

    为了搞定这个问题,OpenCV使用引用计数机制。其思路是让每个 Mat 对象有自己的信息头,但共享同一个矩阵。这通过让矩阵指针指向同一地址而实现。而拷贝构造函数则 只拷贝信息头和矩阵指针 ,而不拷贝矩阵。

    1

    2

    3

    4

    5

    6

    Mat A, C;                                 // 只创建信息头部分

    A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存

    Mat B(A);                                 // 使用拷贝构造函数

    C = A;                                    // 赋值运算符

    以上代码中的所有Mat对象最终都指向同一个也是唯一一个数据矩阵。虽然它们的信息头不同,但通过任何一个对象所做的改变也会影响其它对象。实际 上,不同的对象只是访问相同数据的不同途径而已。这里还要提及一个比较棒的功能:你可以创建只引用部分数据的信息头。比如想要创建一个感兴趣区域( ROI ),你只需要创建包含边界信息的信息头:

    1

    2

    Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle

    Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries

    现在你也许会问,如果矩阵属于多个 Mat 对象,那么当不再需要它时谁来负责清理?简单的回答是:最后一个使用它的对象。通过引用计数机制来实现。无论什么时候有人拷贝了一个 Mat 对象的信息头,都会增加矩阵的引用次数;反之当一个头被释放之后,这个计数被减一;当计数值为零,矩阵会被清理。但某些时候你仍会想拷贝矩阵本身(不只是信息头和矩阵指针),这时可以使用函数 clone() 或者 copyTo()

    1

    2

    3

    Mat F = A.clone();

    Mat G;

    A.copyTo(G);

    现在改变 F 或者 G 就不会影响 Mat 信息头所指向的矩阵。总结一下,你需要记住的是

    OpenCV函数中输出图像的内存分配是自动完成的(如果不特别指定的话)。

    使用OpenCV的C++接口时不需要考虑内存释放问题。

    赋值运算符和拷贝构造函数( ctor )只拷贝信息头。

    使用函数 clone() 或者 copyTo() 来拷贝一副图像的矩阵。

    Mat的初始化与构造

    创建一个Mat的方法:

    1 直接构造

    Mat() 构造函数

        Mat M(2,2, CV_8UC3, Scalar(0,0,255));

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

    OpenCV中对Mat里面depth,dims,channels,step,data,elemSize和数据地址计算的理解  

    http://tmjfzy.blog.163.com/blog/static/66447025201261052543349/

    2 create

    // create by using the create function()

        M.create(4,4, CV_8UC(2));

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

    3 利用数组初始化

    // create multidimensional matrices

    //用二维数组初始化矩阵
    double m[2][3] = { {1, 2, 3}, {4, 5, 6} };
    Mat M = Mat(2, 3, CV_64F, m);

    4 接收指针指向的数据流

    void process_video_frame(const unsigned char* pixels,

                             int width, int height, int step)

    {

        Mat img(height, width, CV_8UC3, pixels, step);

        GaussianBlur(img, img, Size(7,7), 1.5, 1.5);

    }

    5 MATLAB形式的初始化方式

        // Create using MATLAB style eye, ones or zero matrix

        Mat E = Mat::eye(4, 4, CV_64F);   

        cout << "E = " << endl << " " << E << endl << endl;

       

        Mat O = Mat::ones(2, 2, CV_32F);   

        cout << "O = " << endl << " " << O << endl << endl;

        Mat Z = Mat::zeros(3,3, CV_8UC1);

    cout << "Z = " << endl << " " << Z << endl << endl;

    访问Mat的数据元素

    http://www.opencv.org.cn/opencvdoc/2.3.2/html/modules/core/doc/basic_structures.html?highlight=mat#Mat

    1 指针高效访问

    channel = 1

    // compute sum of positive matrix elements

    // (assuming that M isa double-precision matrix)

    double sum=0;

    for(int i = 0; i < M.rows; i++)

    {

        const double* Mi = M.ptr<double>(i);

        for(int j = 0; j < M.cols; j++)

            sum += std::max(Mi[j], 0.);

    }

    channel = 3

    int row = src.rows;
    int col = src.cols;

    Mat dst = Mat(row, col, CV_16UC3);

    Mat  showmask = Mat::zeros(row, col, CV_8UC1);

    for (int i = 0; i < row; ++i)
    {
        ushort  *dataWarpRow = dst.ptr<ushort>(i);
        for (int j = 0; j < col; ++j)
        {
            //Vec3b  *dataWarp = &(dst.at<Vec3b>(i, j));
            ushort  *dataWarpCol = dataWarpRow + j * src.channels();
            uchar *mask = &(showmask.at<uchar>(i, j));
            if ((dataWarpCol)[0] == 0 && (dataWarpCol)[1] == 0 && (dataWarpCol)[2] == 0)
            {
                *mask = 255;
            }
        }
    }

    2 迭代器安全访问

    // compute sum of positive matrix elements, iterator-based variant

    double sum=0;

    MatConstIterator_<double> it = M.begin<double>(), it_end = M.end<double>();

    for(; it != it_end; ++it)

    sum += std::max(*it, 0.);

    3 一维数据存储访问

    // compute the sum of positive matrix elements, optimized variant

    double sum=0;

    int cols = M.cols, rows = M.rows;

    if(M.isContinuous())

    {

        cols *= rows;

        rows = 1;

    }

    const double* ptr = M.ptr<double>(0);
    for(int i = 0; i < cols; i++)
    {
        sum += std::max(ptr[i], 0.);
    }



    4 二维单通道数据访问

    Mat矩阵中数据指针Mat.data是uchar类型指针,CV_8U系列可以通过计算指针位置快速地定位矩阵中的任意元素。

    二维单通道元素可以用Mat::at(i, j)访问,i是行序号,j是列序号。

    M.at<double>(i,j) += 1.f

    Mat H(100, 100, CV_64F);

    for(int i = 0; i < H.rows; i++)

        for(int j = 0; j < H.cols; j++)

            H.at<double>(i,j)=1./(i+j+1);

    但对于多通道的非unsigned char类型矩阵来说,以上方法都不好(注:后来知道可以通过类型转换,用指针访问data数据)。可以用Mat::ptr()来获得指向某行元素的指针,在通过行数与通道数计算相应点的指针。可以通过转换指针类型,访问非uchar类型的Mat元素。例如图像是CV_64FC1格式,可以将Mat.data指针直接转换成double*类型:

    // imgMat is a image.
    double* pimg = (double*)(imgMat.data)
     
    也可以用C++中的显式转换符static_cast,不过要通过void*类型过渡:
    void* pvoid = static_cast<void*>(imgMat.data);
    double* pimg = static_cast<double*>(pvoid);

    这种方式在Debug模式下速度提升非常显著,但没有任何的边界检查和异常处理,使用时必须十分小心。使用Mat::ptr的速度和直接使用这种方法差不多,多一层保护总比没有保护强。

    5 访问彩色图像数据

    #define IMG_B(img,y,x) img.at<Vec3b>(y,x)[0]
    #define IMG_G(img,y,x) img.at<Vec3b>(y,x)[1]
    #define IMG_R(img,y,x) img.at<Vec3b>(y,x)[2]

    Mat的一些数学运算

    This is a list of implemented matrix operations that can be combined in arbitrary complex expressions (here A, B stand for matrices ( Mat ), s for a scalar ( Scalar ), alpha for a real-valued scalar ( double )):

    • Addition, subtraction, negation: A+B, A-B, A+s, A-s, s+A, s-A, -A
    • Scaling: A*alpha
    • Per-element multiplication and division: A.mul(B), A/B, alpha/A
    • Matrix multiplication: A*B
    • Transposition: A.t() (means AT)
    • Matrix inversion and pseudo-inversion, solving linear systems and least-squares problems:

    A.inv([method]) (~ A-1) ,   A.inv([method])*B (~ X: AX=B)

    • Comparison: A cmpop B, A cmpop alpha, alpha cmpop A, where cmpop is one of :  >, >=, ==, !=, <=, <. The result of comparison is an 8-bit single channel mask whose elements are set to 255 (if the particular element or pair of elements satisfy the condition) or 0.
    • Bitwise logical operations: A logicop B, A logicop s, s logicop A, ~A, where logicop is one of :  &, |, ^.
    • Element-wise minimum and maximum: min(A, B), min(A, alpha), max(A, B), max(A, alpha)
    • Element-wise absolute value: abs(A)
    • Cross-product, dot-product: A.cross(B) A.dot(B)
    • Any function of matrix or matrices and scalars that returns a matrix or a scalar, such as norm, mean, sum, countNonZero, trace, determinant, repeat, and others.
    • Matrix initializers ( Mat::eye(), Mat::zeros(), Mat::ones() ), matrix comma-separated initializers, matrix constructors and operators that extract sub-matrices (see Mat description).
    • Mat_<destination_type>() constructors to cast the result to the proper type.

    Mat的一些重要属性和函数

    Mat::rows

    Mat::cols

    Mat::convertTo

    Converts an array to another datatype with optional scaling.

    C++: void Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0 ) const

    Parameters:

    • m – Destination matrix. If it does not have a proper size or type before the operation, it is reallocated.
    • rtype – Desired destination matrix type or, rather, the depth since the number of channels are the same as the source has. If rtype is negative, the destination matrix will have the same type as the source.
    • alpha – Optional scale factor.
    • beta – Optional delta added to the scaled values.

    Mat::type

    Returns the type of a matrix element.

    C++: int Mat::type() const

    The method returns a matrix element type. This is an identifier compatible with the CvMat type system, like CV_16SC3 or 16-bit signed 3-channel array, and so on.

    Mat::depth

    Returns the depth of a matrix element.

    C++: int Mat::depth() const

    The method returns the identifier of the matrix element depth (the type of each individual channel). For example, for a 16-bit signed 3-channel array, the method returns CV_16S . A complete list of matrix types contains the following values:

    • CV_8U - 8-bit unsigned integers ( 0..255 )
    • CV_8S - 8-bit signed integers ( -128..127 )
    • CV_16U - 16-bit unsigned integers ( 0..65535 )
    • CV_16S - 16-bit signed integers ( -32768..32767 )
    • CV_32S - 32-bit signed integers ( -2147483648..2147483647 )
    • CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
    • CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )

    Mat::channels

    Returns the number of matrix channels.

    C++: int Mat::channels() const

    The method returns the number of matrix channels.

    Mat::ptr

    Returns a pointer to the specified matrix row.

    C++: uchar* Mat::ptr(int i=0)

    C++: const uchar* Mat::ptr(int i=0) const

    C++: template<typename _Tp> _Tp* Mat::ptr(int i=0)

    C++: template<typename _Tp> const _Tp* Mat::ptr(int i=0) const

    Parameters:

    • i – A 0-based row index.

    The methods return uchar* or typed pointer to the specified matrix row. See the sample in Mat::isContinuous() to know how to use these methods.

    Mat Iplimage相互转换

    //cv::Mat -> IplImage

    Mat srcImg;                         // Mat type variable .
    srcImg = imread("left1.png");         // read image;  
    IplImage *resIplPtr = NULL;         // Initialize by NULL.
    resIplPtr = &(IplImage(srcImg));    // Mat to IplImage Pointer
    cvShowImage("resIplPtr" ,resIplPtr);
    cvWaitKey(0);

    //IplImage -> cv::Mat
    IplImage* iplimg = cvLoadImage("left1.png");
    cv::Mat matimg;
    matimg = cv::Mat(iplimg);
    namedWindow("mat",0);
    imshow("mat",matimg);
    waitKey(0);

    和图像处理相关的几个函数

    http://www.opencv.org.cn/opencvdoc/2.3.2/html/modules/core/doc/operations_on_arrays.html?highlight=split#cv.Split

    Merge

    merge

    Composes a multi-channel array from several single-channel arrays.

    C++: void merge(const Mat* mv, size_t count, OutputArray dst)

    C++: void merge(const vector<Mat>& mv, OutputArray dst)

    Split

    split

    Divides a multi-channel array into several single-channel arrays.

    C++: void split(const Mat& mtx, Mat* mv)

    C++: void split(const Mat& mtx, vector<Mat>& mv)

    获取图像的ROI

    Mat img;

    Mat RoiImg = img(Range(rbegin, rend), Range(cbeign, cend));

  • 相关阅读:
    在IE和Firfox获取keycode
    using global variable in android extends application
    using Broadcast Receivers to listen outgoing call in android note
    help me!virtual keyboard issue
    using iscroll.js and iscroll jquery plugin in android webview to scroll div and ajax load data.
    javascript:jquery.history.js使用方法
    【CSS核心概念】弹性盒子布局
    【Canvas学习笔记】基础篇(二)
    【JS核心概念】数据类型以及判断方法
    【问题记录】ElementUI上传组件使用beforeupload钩子校验失败时的问题处理
  • 原文地址:https://www.cnblogs.com/adong7639/p/4360225.html
Copyright © 2020-2023  润新知