本文为作者原创,未经允许不得转载;原文由作者发表在博客园: 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 ? kCGIm