openCV学习笔记(三): 认识Mat

前面通过两个demo已经尝鲜过了,下面开始真正深入学习openCV中的核心组件,今天的主题是:Mat。


      Mat - 基本图像容器

      还是看官网   https://docs.opencv.org/master/d6/d6d/tutorial_mat_the_basic_image_container.html         

      Mat结构图示例:

      

openCV作为计算机视觉库,主要目的就是处理图像信息,openCV存储图像的数据结构就是Mat。


Mat的数据结构是怎么描述和存储图像的呢?有两幅图还是比较直观的

灰度图:

三通道RGB存储方式:

相信大家很容易看懂这种数据结构。Mat也为我们提供了很多接口,访问行列等信息,别忘了openCV是开源的,可以通过研究

引擎的source中去了解更多的接口,这边不一一列举,后面的笔记应该都会用到的!


        openCV老的版本中通过lplimage的C语言结构体存储图形信息,因为需要手动释放内存等原因和越来越多的嵌入式语言的支持,openCV引入了C++,再定义了Mat数据结构体的同时,也为我们提供了引用计数的自动管理内存的方式,如果做过Oc开发应该对引用计数内存管理方式很亲切,这里不多介绍,总之一句话:我们不需要关心内存的分配和释放,我们只要按照官方推荐的构建Mat方法和传参方式写openCV就ok了。     

     

        关于Mat是怎么处理内存的,官方给的答案是:

        

To tackle this issue OpenCV uses a reference counting system. The idea is that each Mat object has its own header, 
however the matrix may be shared between two instance of them by having their matrix pointers point to the same address. 
Moreover, the copy operators will only copy the headers and the pointer to the large matrix, not the data itself.
OpenCV使用引用计数机制。其思路是让每个 Mat 对象有自己的信息头,但共享同一个矩阵。这通过让矩阵指针指向同一地址而实现。
而拷贝构造函数则 只拷贝信息头和矩阵指针 ,而不拷贝矩阵。

Mat A, C;                                 // 只创建信息头部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存

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

C = A;                                    // 赋值运算符

openCV提供给了我们很多创建Mat对象的方式:

1、通过Mat()构造函数

Mat M(2,2, CV_8UC3, Scalar(0,0,255)); 
    cout << "M = " << endl << " " << M << endl << endl; 
输出结果为:

M1 = 
[  0,   0, 255,   0,   0, 255;
   0,   0, 255,   0,   0, 255]

2、通过c\c++构造函数

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

3、通过create函数(此方法不能给矩阵初始化数据,只是在改变尺寸的时候分配新内存)

 M.create(4,4, CV_8UC(2));
    cout << "M = "<< endl << " "  << M << endl << endl;

4、MATLAB形式初始化

   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;

5、可以通过clone()或者copyTo()为一个存在的Mat对象创建一个新的信息头

Mat RowClone = C.row(1).clone();
    cout << "RowClone = " << endl << " " << RowClone << endl << endl;

官方后面还介绍了格式化打印,point类型/vector类型等打印支持,需要的时候再了解吧,都不是重心,我们还是终端关注Mat的结构,内存管理机制和使用方法,

下面我们通过一个小例子实现Mat的加载、修改和保存到设备本地的功能!


在IOS设备中从本地读取一个Mat,修改并重新保存到本地的实现

这个Demo有两种实现方案,第一篇我们也讲过了,UIImage和Mat是可以相互转化的,我们可以将图片以UIImage的形式加载出来,然后转化成Mat处理,然后在转为UiImage进行保存。

        上面的方案可行,但是不是我们要熟悉的方式,我这边要调用openCV的imread和imwrite等接口实现Mat的读取、修改和保存,必要的时候这个方法能使性能最大化!


首先,我在IOS设备中保存一张图片

/**
 将一个uiimage保存到设备文档中

 @param img img description
 */
-(void)savePhotoToDeviceDocumentPath:(UIImage*) img{
//    UIImage* image = [UIImage imageNamed:@"Level_house_Unlock_5.png"];
    NSString* documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    //打印一下这个path吧
    NSLog(@"当前设备的document路径为 %@",documentPath);
    //开始保存吧
    NSString* imageSavePath = [documentPath stringByAppendingPathComponent:[NSString stringWithFormat:@"demo.png"]];
    
    BOOL result = [UIImagePNGRepresentation(img) writeToFile:imageSavePath atomically:YES];
    if(result == YES){
        NSLog(@"保存成功啦");
    }
}

用imread直接读取图片

       

//读取吧
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *filePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"demo.png"]];
    const char* cPath = [filePath cStringUsingEncoding:NSMacOSRomanStringEncoding];
    Mat newImage;
    newImage = imread(cPath,IMREAD_COLOR);
    if(!newImage.data){
        cout << "没有找到图片啊!" << endl;
    }else{
        cout << "已经读取了图片" << endl;
    }


修改图片为灰度图

//修改这个图片
    Mat gray_image;
    cvtColor( newImage, gray_image, COLOR_BGR2GRAY );

将这个图片保存到本地

 //重新保存
    UIImage* imageNew = [[CVUtil sharedHelper]UIImageFromCVMat:gray_image];
    NSString* documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    //打印一下这个path吧
    NSLog(@"当前设备的document路径为 %@",documentPath);
    //开始保存吧
    NSString* imageSavePath = [documentPath stringByAppendingPathComponent:[NSString stringWithFormat:@"demo.png"]];
    
    imwrite([imageSavePath cStringUsingEncoding:NSMacOSRomanStringEncoding], gray_image);

测试下结果吧,去设备document路径下看:

   demo写完了,后面继续按照官网学习!

上一篇介绍摄像头图像处理:

http://blog.csdn.net/jbl20078/article/details/78839702


下一篇介绍遍历Mat数据和利用查找表修改图像的方法:

http://blog.csdn.net/jbl20078/article/details/78850772

    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值