how to convert from cvMat to UIImage in objective-c?
Note: most implementations don't correctly handle an alpha channel or convert from OpenCV's BGR pixel format to iOS's RGB.
This will correctly convert from cv::Mat
to UIImage
:
+(UIImage *)UIImageFromCVMat:(cv::Mat)cvMat { NSData *data = [NSData dataWithBytes:cvMat.data length:image.step.p[0]*image.rows]; CGColorSpaceRef colorSpace; CGBitmapInfo bitmapInfo; if (cvMat.elemSize() == 1) { colorSpace = CGColorSpaceCreateDeviceGray(); bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault; } else { colorSpace = CGColorSpaceCreateDeviceRGB(); bitmapInfo = kCGBitmapByteOrder32Little | ( cvMat.elemSize() == 3? kCGImageAlphaNone : kCGImageAlphaNoneSkipFirst ); } CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); // Creating CGImage from cv::Mat CGImageRef imageRef = CGImageCreate( cvMat.cols, //width cvMat.rows, //height 8, //bits per component 8 * cvMat.elemSize(), //bits per pixel cvMat.step[0], //bytesPerRow colorSpace, //colorspace bitmapInfo, // bitmap info provider, //CGDataProviderRef NULL, //decode false, //should interpolate kCGRenderingIntentDefault //intent ); // Getting UIImage from CGImage UIImage *finalImage = [UIImage imageWithCGImage:imageRef]; CGImageRelease(imageRef); CGDataProviderRelease(provider); CGColorSpaceRelease(colorSpace); return finalImage; }
And to convert from UIImage
to cv::Mat
:
+ (cv::Mat)cvMatWithImage:(UIImage *)image{ CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage); size_t numberOfComponents = CGColorSpaceGetNumberOfComponents(colorSpace); CGFloat cols = image.size.width; CGFloat rows = image.size.height; cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipLast | kCGBitmapByteOrderDefault; // check whether the UIImage is greyscale already if (numberOfComponents == 1){ cvMat = cv::Mat(rows, cols, CV_8UC1); // 8 bits per component, 1 channels bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault; } CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to backing data cols, // Width of bitmap rows, // Height of bitmap 8, // Bits per component cvMat.step[0], // Bytes per row colorSpace, // Colorspace bitmapInfo); // Bitmap info flags CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage); CGContextRelease(contextRef); return cvMat;}
From opencv 2.4.6 on this functionality is already included.Just include
opencv2/highgui/ios.h
In OpenCV 3 this include has changed to:
opencv2/imgcodecs/ios.h
And you can use these functions:
UIImage* MatToUIImage(const cv::Mat& image);void UIImageToMat(const UIImage* image, cv::Mat& m, bool alphaExist = false);
Here is the correct method to convert a cv::Mat
to a UIImage
.
Every other implementation I've seen — including OpenCV's documentation — is incorrect: they do not correctly convert from OpenCV's BGR to iOS's RGB, and they do not consider the alpha channel (if one exists). See comments above bitmapInfo = …
.
+(UIImage *)UIImageFromCVMat:(cv::Mat)cvMat { NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()]; CGColorSpaceRef colorSpace; CGBitmapInfo bitmapInfo; if (cvMat.elemSize() == 1) { colorSpace = CGColorSpaceCreateDeviceGray(); bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault; } else { colorSpace = CGColorSpaceCreateDeviceRGB(); // OpenCV defaults to either BGR or ABGR. In CoreGraphics land, // this means using the "32Little" byte order, and potentially // skipping the first pixel. These may need to be adjusted if the // input matrix uses a different pixel format. bitmapInfo = kCGBitmapByteOrder32Little | ( cvMat.elemSize() == 3? kCGImageAlphaNone : kCGImageAlphaNoneSkipFirst ); } CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); // Creating CGImage from cv::Mat CGImageRef imageRef = CGImageCreate( cvMat.cols, //width cvMat.rows, //height 8, //bits per component 8 * cvMat.elemSize(), //bits per pixel cvMat.step[0], //bytesPerRow colorSpace, //colorspace bitmapInfo, // bitmap info provider, //CGDataProviderRef NULL, //decode false, //should interpolate kCGRenderingIntentDefault //intent ); // Getting UIImage from CGImage UIImage *finalImage = [UIImage imageWithCGImage:imageRef]; CGImageRelease(imageRef); CGDataProviderRelease(provider); CGColorSpaceRelease(colorSpace); return finalImage; }