• opencv源码阅读之——iOS的两条接口UIImageToMat()和MatToUIImage()


    本文为作者原创,未经允许不得转载;原文由作者发表在博客园: http://www.cnblogs.com/panxiaochun/p/5387743.html

    在ios下开发基于opencv的程序时经常会用到两条接口,分别是UIImageToMat()和MatToUIImage(),这两条接口是UIImage与Mat之间的转换。关于这两条api的信息opencv文档里面没有给出太多的信息,所以,需要通过阅读源码来分析。

    1.UIImageToMat的细节

    关于这条api,我们总想知道返回的mat的一些细节,比如是几通道的,是否带alpha通道,色彩空间是RGBA还BGR的,我们都不清楚,带着这几个问题,我们一起来阅读源码:

    void UIImageToMat(const UIImage* image,
                             cv::Mat& m, bool alphaExist) {
        CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
        CGFloat cols = image.size.width, rows = image.size.height;
        CGContextRef contextRef;
        CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast;
        if (CGColorSpaceGetModel(colorSpace) == 0)
        {
            m.create(rows, cols, CV_8UC1); // 8 bits per component, 1 channel
            bitmapInfo = kCGImageAlphaNone;
            if (!alphaExist)
                bitmapInfo = kCGImageAlphaNone;
            contextRef = CGBitmapContextCreate(m.data, m.cols, m.rows, 8,
                                               m.step[0], colorSpace,
                                               bitmapInfo);
        }
        else
        {
            m.create(rows, cols, CV_8UC4); // 8 bits per component, 4 channels
            if (!alphaExist)
                bitmapInfo = kCGImageAlphaNoneSkipLast |
                                    kCGBitmapByteOrderDefault;
            contextRef = CGBitmapContextCreate(m.data, m.cols, m.rows, 8,
                                               m.step[0], colorSpace,
                                               bitmapInfo);
        }
        CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows),
                           image.CGImage);
        CGContextRelease(contextRef);
    }

    这个源码是在文件夹modules/imgcodecs/src/ ios_conversions.mm里面的。从源码可以看出,opencv会先把UIImage类型的image转化为CGImga,这是一个位图bitmap图像

    @property(nullable, nonatomic,readonly) CGImageRef CGImage; // returns underlying CGImageRef or nil if CIImage based
    The CGImageRef opaque type represents bitmap images and bitmap image masks, based on sample data that you supply. A bitmap (or sampled) image is a rectangular array of pixels, with each pixel representing a single sample or data point in a source image.

    CGImageGetColorSpace会获取指定图像的色彩空间,如果指定的图像(也不能叫图像)是一个图像蒙板(image mask),则返回NULL,图像蒙板其实就是ps里面的蒙板,蒙板指定的区域才会显示,具体可以看ios文档说明。opencv的c++版不同于java版,java版里面的图像有width和height两个对象,c++里面是cols和rows,分别代表宽和高,其实就是Mat的列数和行数,表示不同。

    然后获取图像的色彩模式CGColorSpaceGetModel,得到一个枚举值:

    typedef CF_ENUM (int32_t,  CGColorSpaceModel) {
        kCGColorSpaceModelUnknown = -1,
        kCGColorSpaceModelMonochrome,
        kCGColorSpaceModelRGB,
        kCGColorSpaceModelCMYK,
        kCGColorSpaceModelLab,
        kCGColorSpaceModelDeviceN,
        kCGColorSpaceModelIndexed,
        kCGColorSpaceModelPattern
    };

    kCGColorSpaceModelMonochrome是单色图,也就是黑白的灰度图,如果是灰度图则通过CGBitmapContextCreate往cv::Mat类型的m里面的data写数据,

    大小和原图一样,色彩空间也是和原图一样,这里就是单色图,没有alpha通道。如果不是灰度图,则把原来图片的色彩空间的色彩写进cv::Mat的data里,得到转换的m。

    通过阅读源码可以知道,如果传进来的是单色灰度图,则返回的也是单色灰度图,没有alpha通道。如果传进来的是RGB 或者BGRA的,则会返回原图的色彩空间RGB或者 BGRA,其中A通道如果转换时没有指定,则默认是有的。

    2.MatToUIImage的转换细节

    下面来了解MatToUIImage的转换细节,源码:

    UIImage* MatToUIImage(const cv::Mat& image) {
    
        NSData *data = [NSData dataWithBytes:image.data
                                      length:image.elemSize()*image.total()];
    
        CGColorSpaceRef colorSpace;
    
        if (image.elemSize() == 1) {
            colorSpace = CGColorSpaceCreateDeviceGray();
        } else {
            colorSpace = CGColorSpaceCreateDeviceRGB();
        }
    
        CGDataProviderRef provider =
                CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
    
        // Preserve alpha transparency, if exists
        bool alpha = image.channels() == 4;
        CGBitmapInfo bitmapInfo = (alpha ? kCGImageAlphaLast : kCGImageAlphaNone) | kCGBitmapByteOrderDefault;
    
        // Creating CGImage from cv::Mat
        CGImageRef imageRef = CGImageCreate(image.cols,
                                            image.rows,
                                            8,
                                            8 * image.elemSize(),
                                            image.step.p[0],
                                            colorSpace,
                                            bitmapInfo,
                                            provider,
                                            NULL,
                                            false,
                                            kCGRenderingIntentDefault
                                            );
    
    
        // Getting UIImage from CGImage
        UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
        CGImageRelease(imageRef);
        CGDataProviderRelease(provider);
        CGColorSpaceRelease(colorSpace);
    
        return finalImage;
    }

    首先会获取传入的矩阵的data原始数据,长度为image.elemSize()*image.total(),elemSize()会返回元素的大小(单位:字节bytes),详情可以看opencv的文档说明(其实就是通道数*通道元素大小,例如元素类型为CV_8UC3,它的大小其实就是3字节,每个通道一字节8位bit,三个通道)。image.total()返回图像的所有元素数量。

    然后判断image的元素大小,如果为1字节,那么就设置色彩空间是单色灰度图gray(CV_8UC1),如果不是1字节,那么就设置色彩空间为RGB(所以通过MatToUIImage()转换的图像色彩空间都是RGB的),然后判断元素的大小是否为4字节,如果为4字节则是带alpha通道的,因为RGB各占一个通道,也就是各一个字节,第四个字节就是alpha通道。

    CGDataProviderCreateWithCFData创建一个CGDataProviderRef对象(_bridge标记是xcode 在Core Foundation和Foundation对象之间的转换要通过Toll-Free bridge来对内存管理进行转换,只在ARC下有效)

    CGImageCreate会通过参数创建一个bitmap位图图像,各参数可以看说明文档。再通过bitmap创建UIImage,所以通过阅读源码可以知道,图像的色彩空间是如果原图是单色灰度图,则UIImage的色彩空间为单色灰度图,如果不是,则默认为RGB,如果cv::Mat是带alpha通道,则转换的UIImage带Alpha通道

    3.总结

    UIImageToMat输出的mat默认带Alpha通道,可以选择不带alpha通道,在UIImageToMat()的第三个参数指定就行,MatToUIImga输出的UIImage默认为RGB类型

     
  • 相关阅读:
    [对对子队]会议记录4.10(Scrum Meeting 1)
    [对对子队]团队任务拆解Alpha
    [对对子队]功能规格说明书
    [对对子队]技术规格说明书
    团队项目选择
    团队作业第四次—项目系统设计与数据库设计
    团队作业第三次—项目需求分析
    团队作业第二次——团队Github实战训练
    团队作业第一次—团队展示和项目展示
    贡献分分配规则
  • 原文地址:https://www.cnblogs.com/panxiaochun/p/5387743.html
Copyright © 2020-2023  润新知