你好,歡迎來到IOS教程網

 Ios教程網 >> IOS使用技巧 >> IOS技巧綜合 >> opencv源碼閱讀之——iOS的兩條接口UIImageToMat()和MatToUIImage()

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

編輯:IOS技巧綜合
[摘要]本文是對opencv源碼閱讀之——iOS的兩條接口UIImageToMat()和MatToUIImage()的講解,對學習IOS蘋果軟件開發有所幫助,與大家分享。

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


                

				
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved