本文为做者原创,未经容许不得转载;原文由做者发表在博客园: 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类型