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

轉自:https://www.cnblogs.com/panxiaochun/p/5387743.html

本文爲作者原創,未經允許不得轉載;原文由作者發表在博客園: 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類型

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章