opencv源碼閱讀之——iOS的兩條接口UIImageToMat()和MatToUIImage()

本文爲做者原創,未經容許不得轉載;原文由做者發表在博客園: http://www.cnblogs.com/panxiaochun/p/5387743.htmlhtml

在ios下開發基於opencv的程序時常常會用到兩條接口,分別是UIImageToMat()和MatToUIImage(),這兩條接口是UIImage與Mat之間的轉換。關於這兩條api的信息opencv文檔裏面沒有給出太多的信息,因此,須要經過閱讀源碼來分析。java

1.UIImageToMat的細節

關於這條api,咱們總想知道返回的mat的一些細節,好比是幾通道的,是否帶alpha通道,色彩空間是RGBA還BGR的,咱們都不清楚,帶着這幾個問題,咱們一塊兒來閱讀源碼:ios

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圖像c++

@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的列數和行數,表示不一樣。chrome

而後獲取圖像的色彩模式CGColorSpaceGetModel,獲得一個枚舉值:api

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

kCGColorSpaceModelMonochrome是單色圖,也就是黑白的灰度圖,若是是灰度圖則經過CGBitmapContextCreate往cv::Mat類型的m裏面的data寫數據,xcode

大小和原圖同樣,色彩空間也是和原圖同樣,這裏就是單色圖,沒有alpha通道。若是不是灰度圖,則把原來圖片的色彩空間的色彩寫進cv::Mat的data裏,獲得轉換的m。ide

經過閱讀源碼能夠知道,若是傳進來的是單色灰度圖,則返回的也是單色灰度圖,沒有alpha通道。若是傳進來的是RGB 或者BGRA的,則會返回原圖的色彩空間RGB或者 BGRA,其中A通道若是轉換時沒有指定,則默認是有的。atom

2.MatToUIImage的轉換細節

下面來了解MatToUIImage的轉換細節,源碼:spa

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類型

相關文章
相關標籤/搜索