Search code examples
objective-copencv

how to convert from cv::Mat to UIImage in Objective-C?


I'm using the OpenCV framework with Xcode and want to convert from cv::Mat or IplImage to UIImage. How can I do that?


Solution

  • 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;
    }