OpenCV MatToUIImage导致内存泄漏

时间:2017-05-15 09:32:54

标签: ios opencv memory-leaks ios10

我的项目正在使用openCV for iOS(2.4.9)。我发现函数 MatToUIImage 会导致内存泄漏,而且只发生在iOS 10.X上。

在我将此功能(2.4.9)更新到最新版本(3.2.0)之后,所有功能都得到了解决。唯一的区别是 CGBitmapInfo

所以有人能告诉我为什么吗?

2.4.9

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

    NSData *data = [NSData dataWithBytes:image.data
                                  length:image.elemSize()*image.total()];

    CGColorSpaceRef colorSpace;

    if (image.elemSize() == 1) {
        colorSpace = CGColorSpaceCreateDeviceGray();
    } else {
        colorSpace = CGColorSpaceCreateDeviceRGB();
    }

    CGDataProviderRef provider =
            CGDataProviderCreateWithCFData((__bridge CFDataRef)data);

    // Creating CGImage from cv::Mat
    CGImageRef imageRef = CGImageCreate(image.cols,
                                        image.rows,
                                        8,
                                        8 * image.elemSize(),
                                        image.step.p[0],
                                        colorSpace,
                                        kCGImageAlphaNone|
                                        kCGBitmapByteOrderDefault,
                                        provider,
                                        NULL,
                                        false,
                                        kCGRenderingIntentDefault
                                        );


    // Getting UIImage from CGImage
    UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);

    return finalImage;
}

3.2.0

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

    NSData *data = [NSData dataWithBytes:image.data
                                  length:image.elemSize()*image.total()];

    CGColorSpaceRef colorSpace;

    if (image.elemSize() == 1) {
        colorSpace = CGColorSpaceCreateDeviceGray();
    } else {
        colorSpace = CGColorSpaceCreateDeviceRGB();
    }

    CGDataProviderRef provider =
            CGDataProviderCreateWithCFData((__bridge CFDataRef)data);

    // Preserve alpha transparency, if exists
    bool alpha = image.channels() == 4;
    CGBitmapInfo bitmapInfo = (alpha ? kCGImageAlphaLast : kCGImageAlphaNone) | kCGBitmapByteOrderDefault;

    // Creating CGImage from cv::Mat
    CGImageRef imageRef = CGImageCreate(image.cols,
                                        image.rows,
                                        8,
                                        8 * image.elemSize(),
                                        image.step.p[0],
                                        colorSpace,
                                        bitmapInfo,
                                        provider,
                                        NULL,
                                        false,
                                        kCGRenderingIntentDefault
                                        );


    // Getting UIImage from CGImage
    UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);

    return finalImage;
}

1 个答案:

答案 0 :(得分:1)

重要更新(5.06.2017)最后,手动执行CFRelease原来是个坏主意,因为它可能会带来更多麻烦而不是解决!虽然,它给了我一个线索,泄漏与NSData(不是)释放有某种联系。

我注意到它在后台线程中从块中调用时会按预期自动释放,如下所示:

- (void)runInBackgroundWithImageBuffer:(CVImageBufferRef)imageBuffer
                              callback:(void (^)())callback {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        [self processImageBuffer:imageBuffer];
        if (callback != nil) {
            callback();
        }
    });
}

- (void)previewOpenCVImage:(cv::Mat *)image {
    UIImage *preview = MatToUIImage(*image);

    dispatch_async(dispatch_get_main_queue(), ^{
        // _imagePreview has (UIImageView *) type
        [_imagePreview setImage:preview];
    });
}

我可以确认iPhone模拟器。似乎当前MatToUIImage实现导致模拟器上的内存泄漏。我无法在设备上重现它。

由于某些原因,探查器未检测到它们,但多次调用后内存使用情况才会爆炸。

我添加了一些调整以使其正常工作:

  1. 在返回最终图像之前添加行CFRelease((CFTypeRef)data)

  2. 如果不需要图片,我们需要执行CFRelease(image.CGImage),可能还需CFRelease((CFTypeRef)image)

  3. 希望有所帮助。实际上我并不完全理解为什么会这样,谁持有参考资料以及为什么我们需要手动执行发布。