本文爲做者原創,未經容許不得轉載;原文由做者發表在博客園: http://www.cnblogs.com/panxiaochun/p/5387743.htmlhtml
在ios下開發基於opencv的程序時常常會用到兩條接口,分別是UIImageToMat()和MatToUIImage(),這兩條接口是UIImage與Mat之間的轉換。關於這兩條api的信息opencv文檔裏面沒有給出太多的信息,因此,須要經過閱讀源碼來分析。java
關於這條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
下面來了解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通道
UIImageToMat輸出的mat默認帶Alpha通道,能夠選擇不帶alpha通道,在UIImageToMat()的第三個參數指定就行,MatToUIImga輸出的UIImage默認爲RGB類型