Search code examples
objective-cswiftopencvautomatic-ref-countingbridge

Memory problems while converting cvMat to UIImage


before posting this question here, i have read all the materials and similar posts on it but i cant get the main "idea" what is happening and how to fix it, in 10 of the similar question, everyone was fixing this problem with @autoreleasepool in this case i was unable to achive my goal. So while converting cvMat to UIImage i have increasing memory depending on size.

Below are step which i am doing before converting mat to uiimage:

cv::Mat undistorted = cv::Mat(cvSize(maxWidth,maxHeight), CV_8UC1);
cv::Mat original = [MatStructure convertUIImageToMat:adjustedImage];

cv::warpPerspective(original, undistorted, cv::getPerspectiveTransform(src, dst), cvSize(maxWidth, maxHeight));
original.release();

adjustedImage = [MatStructure convertMatToUIImage:undistorted];
undistorted.release();

problem is visible while i am converting my mat to uiimage, memory goes up to 400 mb and on every cycle it rises.

+ (UIImage *) convertMatToUIImage: (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);
        CGBitmapInfo bmInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;

        CGImageRef imageRef = CGImageCreate(cvMat.cols,                              // width
                                            cvMat.rows,                              // height
                                            8,                                       // bits per component
                                            8 * cvMat.elemSize(),                    // bits per pixel
                                            cvMat.step.p[0],                         // bytesPerRow
                                            colorSpace,                              // colorspace
                                            bmInfo,                                  // bitmap info
                                            provider,                                // CGDataProviderRef
                                            NULL,                                    // decode
                                            false,                                   // should interpolate
                                            kCGRenderingIntentDefault                // intent
                                            );

        UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
        CGImageRelease(imageRef);
        CGDataProviderRelease(provider);
        CGColorSpaceRelease(colorSpace);
        cvMat.release(); // this line is optional.

        return image;
    }

I have seen many similar code but every single example works as this one. I belive that problem holds in (__bridge CFDataRef) and ARC cant clean up this data, if i will try to CFRelease((__bridge CFDataRef)data) than will happen crash because program will search for allocated memory and it will be freed already so it will run to crash.

I am using openCV3 and have tried their method MatToUIImage but problem still exsits, on leaks profiler there are no leaks at all, and most expensive task in memory is convertMatToUIImage.

I am reading all day about it but actually can't find any useful solution yet.

Currently i work on swift 3.0 which inherits class XXX and it uses objC class to crop something and than return to UIImage as well. In deinit i am assigning this inherited class property nil, but problem still exsists.Also i think that dataWithBytes is duplicating memory like if i have 16MB at start after creating NSData it will be 32MB..

And please if you can suggests useful threads about this problem i will be glad to read them all. Thanks for help


Solution

  • After working on this problem more than three days, i had to rewrite function and it worked 100%, i have tested on five different devices.

    CFRelease, Free() and @autoreleasepool did not helped me at all and i implemented this:

    data = UIImageJPEGRepresentation([[UIImage alloc] initWithCGImage:imageRef], 0.2f); // because images are 30MB and up
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *appFile = [documentsDirectory stringByAppendingPathComponent:@"MyFile.jpeg"];
    
    [data writeToFile:appFile atomically:NO];
    
    data = nil;
    

    after this solution everything worked fine. So i grab the UIImage and converting to NSData, after that we should save it to the local directory and the only thing left is to read the data from directory. hope this thread will help someone one day.