【问题标题】:Converting from UIImage into cv::Mat, then back to UIImage is reducing image quality从 UIImage 转换为 cv::Mat,然后再转换回 UIImage 会降低图像质量
【发布时间】:2017-10-16 05:29:29
【问题描述】:

我正在使用适用于 iOS 的 OpenCV,并使用以下两个函数将 UIImage 转换为 cv::Mat,反之亦然

UIImage 到 cv::Mat:

+ (cv::Mat)toCVMat:(UIImage*)image
{
    // (1) Get image dimensions
    CGFloat cols = image.size.width;
    CGFloat rows = image.size.height;

    // (2) Create OpenCV image container, 8 bits per component, 4 channels
    cv::Mat cvMat(rows, cols, CV_8UC4);

    // (3) Create CG context and draw the image
    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,
                                                    cols,
                                                    rows,
                                                    8,
                                                    cvMat.step[0],
                                                    CGImageGetColorSpace(image.CGImage),
                                                    kCGImageAlphaNoneSkipLast |
                                                    kCGBitmapByteOrderDefault);

    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
    CGContextRelease(contextRef);

    // (4) Return OpenCV image container reference
    return cvMat;
}

cv::Mat 到 UIImage:

+ (UIImage*)fromCVMat:(const cv::Mat&)cvMat
{
    // (1) Construct the correct color space
    CGColorSpaceRef colorSpace;
    if ( cvMat.channels() == 1 ) {
        colorSpace = CGColorSpaceCreateDeviceGray();
    } else {
        colorSpace = CGColorSpaceCreateDeviceRGB();
    }

    // (2) Create image data reference
    CFDataRef data = CFDataCreate(kCFAllocatorDefault, cvMat.data, (cvMat.elemSize() * cvMat.total()));

    // (3) Create CGImage from cv::Mat container
    CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
    CGImageRef imageRef = CGImageCreate(cvMat.cols,
                                        cvMat.rows,
                                        8,
                                        8 * cvMat.elemSize(),
                                        cvMat.step[0],
                                        colorSpace,
                                        kCGImageAlphaNone | kCGBitmapByteOrderDefault,
                                        provider,
                                        NULL,
                                        false,
                                        kCGRenderingIntentDefault);

    // (4) Create UIImage from CGImage
    UIImage * finalImage = [UIImage imageWithCGImage:imageRef];

    // (5) Release the references
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CFRelease(data);
    CGColorSpaceRelease(colorSpace);

    //    UIImage * imgNew = [self cloneWithAlpha:finalImage :.3];
    // (6) Return the UIImage instance
    return finalImage;
}

然后将一个 UIImage 转换成一个 cv::Mat 再转换回 UIImage,代码如下

    cv::Mat currentCVFrame=[img toCVMat];
    UIImage* testImage = [UIImage fromCVMat:currentCVFrame];

如果我在转换之前保存 UIImage,然后在将其转换回 UIImage 后再次保存,我会注意到文件大小有所不同(第二个大约减少了 20%)。我知道在进行转换时会有一些质量损失。任何人都可以确认并告诉我我的转换功能是否有问题?

【问题讨论】:

    标签: c++ ios objective-c opencv uiimage


    【解决方案1】:

    试试下面的代码

       + (UIImage *)UIImageFromCVMat:(cv::Mat)cvMat
    {
        NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()];
        CGColorSpaceRef colorSpace;
    
        if (cvMat.elemSize() == 1) {
            colorSpace = CGColorSpaceCreateDeviceGray();
        } else {
            colorSpace = CGColorSpaceCreateDeviceRGB();
        }
    
        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
                                            kCGImageAlphaNoneSkipLast|kCGBitmapByteOrderDefault,// 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;
    }
    
    + (cv::Mat)cvMatFromUIImage:(UIImage *)image
    {
        CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
        CGFloat cols,rows;
        if  (image.imageOrientation == UIImageOrientationLeft
             || image.imageOrientation == UIImageOrientationRight) {
            cols = image.size.height;
            rows = image.size.width;
        }
        else{
            cols = image.size.width;
            rows = image.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);
    
        CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
        CGContextRelease(contextRef);
    
    
        cv::Mat cvMatTest;
        cv::transpose(cvMat, cvMatTest);
    
        if  (image.imageOrientation == UIImageOrientationLeft
             || image.imageOrientation == UIImageOrientationRight) {
    
        }
        else{
            return cvMat;
    
        }
        cvMat.release();
    
        cv::flip(cvMatTest, cvMatTest, 1);
    
    
        return cvMatTest;
    }
    

    它非常适合我...

    【讨论】:

    • 谢谢,刚刚测试,它提供的结果与我的旧代码相同:如果我在转换之前保存 UIImage,它的大小为 1.4MB,如果我将其转换为 cv::Mat 并将其转换回来到 UIImage,然后保存,它的大小是 1.2MB
    猜你喜欢
    • 2012-12-29
    • 2015-01-05
    • 2012-06-09
    • 2017-12-24
    • 2021-11-25
    • 2017-03-03
    • 2016-06-11
    • 1970-01-01
    • 2015-11-24
    相关资源
    最近更新 更多