how to convert from cvMat to UIImage in objective-c?

后端 未结 8 1372
渐次进展
渐次进展 2020-12-08 07:12

I\'m using the OpenCV framework with XCode and want to convert from cvMat or IplImage to UIImage, how to do that? Thanks.

相关标签:
8条回答
  • 2020-12-08 07:48

    As a category:

    #import <UIKit/UIKit.h>
    #import <opencv2/core/core.hpp>
    using namespace cv;
    
    @interface UIImage (OCV)
    
    -(id)initWithOImage:(cv::Mat)oImage;
    -(cv::Mat)oImage;
    
    @end
    

    and

    #import "UIImage+OCV.h"
    
    @implementation UIImage (OCV)
    
    -(id)initWithOImage:(cv::Mat)oImage
    {
      NSData *data = [NSData dataWithBytes:oImage.data length:oImage.elemSize() * oImage.total()];
    
      CGColorSpaceRef colorSpace;
    
      if (oImage.elemSize() == 1) {
        colorSpace = CGColorSpaceCreateDeviceGray();
      } else {
        colorSpace = CGColorSpaceCreateDeviceRGB();
      }
    
      CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
    
      CGImageRef imageRef = CGImageCreate(oImage.cols,                                     // Width
                                          oImage.rows,                                     // Height
                                          8,                                              // Bits per component
                                          8 * oImage.elemSize(),                           // Bits per pixel
                                          oImage.step[0],                                  // Bytes per row
                                          colorSpace,                                     // Colorspace
                                          kCGImageAlphaNone | kCGBitmapByteOrderDefault,  // Bitmap info flags
                                          provider,                                       // CGDataProviderRef
                                          NULL,                                           // Decode
                                          false,                                          // Should interpolate
                                          kCGRenderingIntentDefault);                     // Intent
    
      self = [self initWithCGImage:imageRef];
    
      CGImageRelease(imageRef);
      CGDataProviderRelease(provider);
      CGColorSpaceRelease(colorSpace);
    
      return self;
    }
    
    -(cv::Mat)oImage
    {
      CGColorSpaceRef colorSpace = CGImageGetColorSpace(self.CGImage);
      CGFloat cols = self.size.width;
      CGFloat rows = self.size.height;
    
      cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels
    
      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
                                                      kCGImageAlphaNoneSkipLast |
                                                      kCGBitmapByteOrderDefault); // Bitmap info flags
    
      CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), self.CGImage);
      CGContextRelease(contextRef);
    
      return cvMat;
    }
    
    @end
    
    0 讨论(0)
  • 2020-12-08 07:49

    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);
    
    0 讨论(0)
  • 2020-12-08 07:49

    You should consider using native OpenCV functions to convert forth and back :

    #import <opencv2/imgcodecs/ios.h>
    ...
    UIImage* MatToUIImage(const cv::Mat& image);
    void UIImageToMat(const UIImage* image,
                             cv::Mat& m, bool alphaExist = false);
    
    0 讨论(0)
  • 2020-12-08 07:52

    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;
    }
    
    0 讨论(0)
  • 2020-12-08 07:57

    What I have experienced with converting between UIImage and cvMat is following:

    When I used the method:

    UIImage* MatToUIImage(const cv::Mat& image);
    

    for converting cv::Mat to UIImage and the method:

    void UIImageToMat(const UIImage* image, cv::Mat& m);
    

    for converting UIImage to cv::Mat these methods didn't work correctly using the Simulator. After I deployed my app on a real device, there weren't any problems.

    Best regards, Nazar

    0 讨论(0)
  • 2020-12-08 08:01

    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; 
    }
    
    0 讨论(0)
提交回复
热议问题