OPENCV中的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


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

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

       对Mat的操作:

1.初始化,只拷贝信息头,或者是拷贝原始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
2. 对于拷贝mat中的数据矩阵时,用法如下

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

1
2
3
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;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值