OPENCV中的mat学习

来源:互联网 发布:如何查看5s支持网络 编辑:程序博客网 时间:2024/05/18 01:08

主要参考自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


其中,对Mat中元素的访问则主要是参考点击打开链接

      之前我也是开始接触的是IplImage,后来接触的是CvMat,到最后慢慢的发现,Mat才是比较好用的,所以这次自己想仔细的再学习一次Mat。

       对Mat的操作:

1.初始化,只拷贝信息头,或者是拷贝原始Mat中的感兴趣区域

一地址而实现。而拷贝构造函数则 只拷贝信息头和矩阵指针 ,而不拷贝矩阵。

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

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

12
Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangleMat E = A(Range:all(), Range(1,3)); // using row and column boundaries
2.对于拷贝mat中的数据矩阵时,用法如下

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

123
Mat F = A.clone();Mat G;A.copyTo(G);
3.显示的创建mat

你可以通过 Mat 的运算符 << 来实现,但要记住这只对二维矩阵有效。

Mat 不但是一个很赞的图像容器类,它同时也是一个通用的矩阵类,所以可以用来创建和操作多维矩阵。创建一个Mat对象有多种方法:
  • Mat() 构造函数

        Mat M(2,2, CV_8UC3, Scalar(0,0,255));     cout << "M = " << endl << " " << M << endl << endl;   
Demo image of the matrix output

比如 CV_8UC3 表示使用8位的 unsigned char 型,每个像素由三个元素组成三通道。预先定义的通道数可以多达四个。 Scalar是个short型vector。指定这个能够使用指定的定制化值来初始化矩阵。当然,如果你需要更多通道数,你可以使用大写的宏并把通道数放在小括号中,如下所示

  • 在 C\C++ 中通过构造函数进行初始化

        int sz[3] = {2,2,2};     Mat L(3,sz, CV_8UC(1), Scalar::all(0));

    上面的例子演示了如何创建一个超过两维的矩阵:指定维数,然后传递一个指向一个数组的指针,这个数组包含每个维度的尺寸;其余的相同

  • 为已存在IplImage指针创建信息头:

    IplImage* img = cvLoadImage("greatwave.png", 1);Mat mtx(img); // convert IplImage* -> Mat
  • Create() function: 函数

        M.create(4,4, CV_8UC(2));    cout << "M = "<< endl << " "  << M << endl << endl;
Demo image of the matrix output

这个创建方法不能为矩阵设初值,它只是在改变尺寸时重新为矩阵数据开辟内存。

  • 对于小矩阵你可以用逗号分隔的初始化函数:

        Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);     cout << "C = " << endl << " " << C << endl << endl;
Demo image of the matrix output
  • 使用 clone() 或者 copyTo() 为一个存在的 Mat 对象创建一个新的信息头。

        Mat RowClone = C.row(1).clone();    cout << "RowClone = " << endl << " " << RowClone << endl << endl;
    Demo image of the matrix output


4.输入mat

Note

 

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

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

从上面的例子中可以看到默认格式,除此之外,OpenCV还支持以下的输出习惯

  • 默认方式

        cout << "R (default) = " << endl <<        R           << endl << endl;
    Default Output
  • Python

        cout << "R (python)  = " << endl << format(R,"python") << endl << endl;
    Default Output
  • 以逗号分隔的数值 (CSV)

        cout << "R (csv)     = " << endl << format(R,"csv"   ) << endl << endl;
    Default Output
  • Numpy

        cout << "R (numpy)   = " << endl << format(R,"numpy" ) << endl << endl;
    Default Output
  • C语言

        cout << "R (c)       = " << endl << format(R,"C"     ) << endl << endl;
    Default Output

打印其它常用项目

OpenCV支持使用运算符<<来打印其它常用OpenCV数据结构。

  • 2维点

        Point2f P(5, 1);    cout << "Point (2D) = " << P << endl << endl;
    Default Output
  • 3维点

        Point3f P3f(2, 6, 7);    cout << "Point (3D) = " << P3f << endl << endl;
    Default Output
  • 基于cv::Mat的std::vector

        vector<float> v;    v.push_back( (float)CV_PI);   v.push_back(2);    v.push_back(3.01f);        cout << "Vector of floats via Mat = " << Mat(v) << endl << endl;
    Default Output
  • std::vector点

        vector<Point2f> vPoints(20);    for (size_t E = 0; E < vPoints.size(); ++E)        vPoints[E] = Point2f((float)(E * 5), (float)(E % 7));    cout << "A vector of 2D Points = " << vPoints << endl << endl;
    Default Output


4.对MAT中元素的访问
/*****************************************************************    内容摘要:本例采用8种方法对图像Mat的像素进行扫描,并对像素点的像*            素进行压缩,压缩间隔为div=64,并比较扫描及压缩的效率,效*            率最高的是采用.ptr及减少循环次数来遍历图像,并采用位操*            作来对图像像素进行压缩。*   作    者:Jacky Liu*   完成日期:2012.8.10*   参考资料:《OpenCV 2 computer Vision Application Programming*              cookbook》****************************************************************/#include <opencv2/core/core.hpp>#include <opencv2/imgproc/imgproc.hpp>#include <opencv2/highgui/highgui.hpp>#include <iostream>//利用.ptr和数组下标进行图像像素遍历void colorReduce0(cv::Mat &image, int div = 64){    int nl = image.rows;    int nc = image.cols * image.channels();        //遍历图像的每个像素    for(int j=0; j<nl ;++j)    {        uchar *data = image.ptr<uchar>(j);        for(int i=0; i<nc; ++i)        {            data[i] = data[i]/div*div+div/2;     //减少图像中颜色总数的关键算法:if div = 64, then the total number of colors is 4x4x4;整数除法时,是向下取整。        }    }}//利用.ptr和 *++ 进行图像像素遍历void colorReduce1(cv::Mat &image, int div = 64){    int nl = image.rows;    int nc = image.cols * image.channels();        //遍历图像的每个像素    for(int j=0; j<nl ;++j)    {        uchar *data = image.ptr<uchar>(j);        for(int i=0; i<nc; ++i)        {            *data++ = *data/div*div + div/2;        }    }}//利用.ptr和数组下标进行图像像素遍历,取模运算用于减少图像颜色总数void colorReduce2(cv::Mat &image, int div = 64){    int nl = image.rows;    int nc = image.cols * image.channels();        //遍历图像的每个像素    for(int j=0; j<nl ;++j)    {        uchar *data = image.ptr<uchar>(j);        for(int i=0; i<nc; ++i)        {            data[i] = data[i]-data[i]%div +div/2;  //利用取模运算,速度变慢,因为要读每个像素两次        }    }}//利用.ptr和数组下标进行图像像素遍历,位操作运算用于减少图像颜色总数void colorReduce3(cv::Mat &image, int div = 64){    int nl = image.rows;    int nc = image.cols * image.channels();    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));   //div=64, n=6    uchar mask = 0xFF<<n;                                            //e.g. div=64, mask=0xC0        //遍历图像的每个像素    for(int j=0; j<nl ;++j)    {        uchar *data = image.ptr<uchar>(j);        for(int i=0; i<nc; ++i)        {            *data++ = *data&mask + div/2;        }    }}//形参传入const conference,故输入图像不会被修改;利用.ptr和数组下标进行图像像素遍历void colorReduce4(const cv::Mat &image, cv::Mat &result,int div = 64){    int nl = image.rows;    int nc = image.cols * image.channels();    result.create(image.rows,image.cols,image.type());        //遍历图像的每个像素    for(int j=0; j<nl ;++j)    {        const uchar *data_in = image.ptr<uchar>(j);        uchar *data_out = result.ptr<uchar>(j);        for(int i=0; i<nc; ++i)        {            data_out[i] = data_in[i]/div*div+div/2;     //减少图像中颜色总数的关键算法:if div = 64, then the total number of colors is 4x4x4;整数除法时,是向下取整。        }    }}//利用.ptr和数组下标进行图像像素遍历,并将nc放入for循环中(比较糟糕的做法)void colorReduce5(cv::Mat &image, int div = 64){    int nl = image.rows;        //遍历图像的每个像素    for(int j=0; j<nl ;++j)    {        uchar *data = image.ptr<uchar>(j);        for(int i=0; i<image.cols * image.channels(); ++i)        {            data[i] = data[i]/div*div+div/2;     //减少图像中颜色总数的关键算法:if div = 64, then the total number of colors is 4x4x4;整数除法时,是向下取整。        }    }}//利用迭代器 cv::Mat iterator 进行图像像素遍历void colorReduce6(cv::Mat &image, int div = 64){    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] = (*it)[0]/div*div+div/2;        //利用operator[]处理每个通道的像素        (*it)[1] = (*it)[1]/div*div+div/2;        (*it)[2] = (*it)[2]/div*div+div/2;    }}//利用.at<cv::Vec3b>(j,i)进行图像像素遍历void colorReduce7(cv::Mat &image, int div = 64){    int nl = image.rows;    int nc = image.cols;        //遍历图像的每个像素    for(int j=0; j<nl ;++j)    {        for(int i=0; i<nc; ++i)        {            image.at<cv::Vec3b>(j,i)[0] = image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;            image.at<cv::Vec3b>(j,i)[1] = image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;            image.at<cv::Vec3b>(j,i)[2] = image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;        }    }}//减少循环次数,进行图像像素遍历,调用函数较少,效率最高。void colorReduce8(cv::Mat &image, int div = 64){    int nl = image.rows;    int nc = image.cols;    //判断是否是连续图像,即是否有像素填充    if(image.isContinuous())    {        nc = nc*nl;        nl = 1;    }    int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));    uchar mask = 0xFF<<n;        //遍历图像的每个像素    for(int j=0; j<nl ;++j)    {        uchar *data = image.ptr<uchar>(j);        for(int i=0; i<nc; ++i)        {            *data++ = *data & mask +div/2;            *data++ = *data & mask +div/2;            *data++ = *data & mask +div/2;        }    }}const int NumTests = 9;        //测试算法的数量const int NumIteration = 20;   //迭代次数int main(int argc, char* argv[]){    int64 t[NumTests],tinit;    cv::Mat image1;    cv::Mat image2;        //数组初始化    int i=0;    while(i<NumTests)    {        t[i++] = 0;    }    int n = NumIteration;        //迭代n次,取平均数    for(int i=0; i<n; ++i)    {        image1 = cv::imread("../boldt.jpg");        if(!image1.data)        {            std::cout<<"read image failue!"<<std::endl;            return -1;        }        // using .ptr and []        tinit = cv::getTickCount();        colorReduce0(image1);        t[0] += cv::getTickCount() - tinit;                // using .ptr and *++        image1 = cv::imread("../boldt.jpg");        tinit = cv::getTickCount();        colorReduce1(image1);        t[1] += cv::getTickCount()  - tinit;                // using .ptr and [] and modulo        image1 = cv::imread("../boldt.jpg");        tinit = cv::getTickCount();        colorReduce2(image1);        t[2] += cv::getTickCount()  - tinit;                // using .ptr and *++ and bitwise        image1 = cv::imread("../boldt.jpg");        tinit = cv::getTickCount();        colorReduce3(image1);        t[3] += cv::getTickCount()  - tinit;        //using input and output image        image1 = cv::imread("../boldt.jpg");        tinit = cv::getTickCount();        colorReduce4(image1,image2);        t[4] += cv::getTickCount()  - tinit;                // using .ptr and [] with image.cols * image.channels()        image1 = cv::imread("../boldt.jpg");        tinit = cv::getTickCount();        colorReduce5(image1);        t[5] += cv::getTickCount()  - tinit;                // using .ptr and *++ and iterator        image1 = cv::imread("../boldt.jpg");        tinit = cv::getTickCount();        colorReduce6(image1);        t[6] += cv::getTickCount()  - tinit;                //using at        image1 = cv::imread("../boldt.jpg");        tinit = cv::getTickCount();        colorReduce7(image1);        t[7] += cv::getTickCount()  - tinit;        //using .ptr and * ++ and bitwise (continuous+channels)        image1 = cv::imread("../boldt.jpg");        tinit = cv::getTickCount();        colorReduce8(image1);        t[8] += cv::getTickCount()  - tinit;    }    cv::namedWindow("Result");    cv::imshow("Result",image1);    cv::namedWindow("Result Image");    cv::imshow("Result Image",image2);    std::cout<<std::endl<<"-------------------------------------------------------------------------"<<std::endl<<std::endl;    std::cout<<"using .ptr and [] = "<<1000*t[0]/cv::getTickFrequency()/n<<"ms"<<std::endl;    std::cout<<"using .ptr and *++ = "<<1000*t[1]/cv::getTickFrequency()/n<<"ms"<<std::endl;    std::cout<<"using .ptr and [] and modulo = "<<1000*t[2]/cv::getTickFrequency()/n<<"ms"<<std::endl;    std::cout<<"using .ptr and *++ and bitwise = "<<1000*t[3]/cv::getTickFrequency()/n<<"ms"<<std::endl;    std::cout<<"using input and output image = "<<1000*t[4]/cv::getTickFrequency()/n<<"ms"<<std::endl;    std::cout<<"using .ptr and [] with image.cols * image.channels() = "<<1000*t[5]/cv::getTickFrequency()/n<<"ms"<<std::endl;    std::cout<<"using .ptr and *++ and iterator = "<<1000*t[6]/cv::getTickFrequency()/n<<"ms"<<std::endl;    std::cout<<"using at = "<<1000*t[7]/cv::getTickFrequency()/n<<"ms"<<std::endl;    std::cout<<"using .ptr and * ++ and bitwise (continuous+channels) = "<<1000*t[8]/cv::getTickFrequency()/n<<"ms"<<std::endl;    std::cout<<std::endl<<"-------------------------------------------------------------------------"<<std::endl<<std::endl;    cv::waitKey();    return 0;}






原创粉丝点击