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

后端 未结 8 1373
渐次进展
渐次进展 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 08:06

    Here I am mentioning all the needed conversion methods together.

    Converting UIImage color to UIImage gray, without using opencv and only using iOS library functions:

    - (UIImage *)convertImageToGrayScale:(UIImage *)image
    {
        // Create image rectangle with current image width/height
        CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
    
        // Grayscale color space
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
    
        // Create bitmap content with current image size and grayscale colorspace
        CGContextRef context = CGBitmapContextCreate(nil, image.size.width, image.size.height, 8, 0, colorSpace, kCGImageAlphaNone);
    
        // Draw image into current context, with specified rectangle
        // using previously defined context (with grayscale colorspace)
        CGContextDrawImage(context, imageRect, [image CGImage]);
    
        /* changes start here */
        // Create bitmap image info from pixel data in current context
        CGImageRef grayImage = CGBitmapContextCreateImage(context);
    
        // release the colorspace and graphics context
        CGColorSpaceRelease(colorSpace);
        CGContextRelease(context);
    
        // make a new alpha-only graphics context
        context = CGBitmapContextCreate(nil, image.size.width, image.size.height, 8, 0, nil, kCGImageAlphaOnly);
    
        // draw image into context with no colorspace
        CGContextDrawImage(context, imageRect, [image CGImage]);
    
        // create alpha bitmap mask from current context
        CGImageRef mask = CGBitmapContextCreateImage(context);
    
        // release graphics context
        CGContextRelease(context);
    
        // make UIImage from grayscale image with alpha mask
        UIImage *grayScaleImage = [UIImage imageWithCGImage:CGImageCreateWithMask(grayImage, mask) scale:image.scale orientation:image.imageOrientation];
    
        // release the CG images
        CGImageRelease(grayImage);
        CGImageRelease(mask);
    
        // return the new grayscale image
        return grayScaleImage;
    }
    

    Converting color UIImage to color cvMat. Please note that, you will find this piece of code in several links but there is a small modification here. Notice the portion "swap channels". This part is for keeping the color undisturbed otherwise the color channel got modified.

    Also notice the following lines. These lines will help to keep the orientation of the image undisturbed.

        if  (image.imageOrientation == UIImageOrientationLeft
             || image.imageOrientation == UIImageOrientationRight) {
            cols = image.size.height;
            rows = image.size.width;
        }
    
    - (cv::Mat)cvMatFromUIImage:(UIImage *)image
    {
        CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
        CGFloat cols = image.size.width;
        CGFloat rows = image.size.height;
    
        if  (image.imageOrientation == UIImageOrientationLeft
             || image.imageOrientation == UIImageOrientationRight) {
            cols = image.size.height;
            rows = image.size.width;
        }
    
        cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels (color channels + alpha)
    
        CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to  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), image.CGImage);
        CGContextRelease(contextRef);
        CGColorSpaceRelease(colorSpace);
    
        //--swap channels -- //
        std::vector<Mat> ch;
        cv::split(cvMat,ch);
        std::swap(ch[0],ch[2]);
        cv::merge(ch,cvMat);
    
        return cvMat;
    }
    

    Converting UIImage to cvMat gray. Notice the line

    cv::Mat cvMat(rows, cols, CV_8UC4, Scalar(1,2,3,4)); // 8 bits per component, 4 channels

    instead of

    cv::Mat cvMat(rows, cols, CV_8UC1); // 8 bits per component, 1 channels

    This line is needed otherwise the code will throw error

    - (cv::Mat)cvMatGrayFromUIImage:(UIImage *)image
    {
        CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
        CGFloat cols = image.size.width;
        CGFloat rows = image.size.height;
    
      //  cv::Mat cvMat(rows, cols, CV_8UC1); // 8 bits per component, 1 channels
        cv::Mat cvMat(rows, cols, CV_8UC4, Scalar(1,2,3,4)); // 8 bits per component, 4 channels
    
        CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to 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), image.CGImage);
        CGContextRelease(contextRef);
        CGColorSpaceRelease(colorSpace);
        return cvMat;
    }
    

    Now finally, converting cvMat (color,binary, gray) to UIImage (color, binary, gray). Notice the line :

    UIImage *finalImage = [UIImage imageWithCGImage:imageRef scale:1 orientation:self.originalImage.imageOrientation];
    

    This line will help to keep the original orientation of the image

    ENJOY !!

    -(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();
            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 scale:1 orientation:self.originalImage.imageOrientation];
        CGImageRelease(imageRef);
        CGDataProviderRelease(provider);
        CGColorSpaceRelease(colorSpace);
    
        return finalImage;
    }
    
    0 讨论(0)
  • 2020-12-08 08:08

    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);
    

    Note: if your UIImage comes from the camera, you should 'normalize' it ( iOS UIImagePickerController result image orientation after upload) before converting to cv::Mat since OpenCV does not take into account Exif data. If you don't do that the result should be misoriented.

    0 讨论(0)
提交回复
热议问题