QCAR :: Image for UIImage - CoreGraphics and crash saving

I am using Vuforia trying to get pixels from camera instance and convert to UIImage

- (UIImage *)createUIImage:(const QCAR::Image *)qcarImage
{
    int width = qcarImage->getWidth();
    int height = qcarImage->getHeight();
    int bitsPerComponent = 8;
    int bitsPerPixel = QCAR::getBitsPerPixel(QCAR::RGB888);
    int bytesPerRow = qcarImage->getBufferWidth() * bitsPerPixel / bitsPerComponent;
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNone;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, qcarImage->getPixels(), QCAR::getBufferSize(width, height, QCAR::RGB888), NULL);

    CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
    CGImageRef imageRefRetain = CGImageRetain(imageRef);
    UIImage *image = [UIImage imageWithCGImage:imageRefRetain];

    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpaceRef);
    CGImageRelease(imageRef);

    return image;
}

      

After getting the image, I add it to the mutable array.

After getting an image from a mutable array, I get a crash on every method i.e..

UIImageView setImage: 

      

or even calling

NSData* comppressedData = UIImageJPEGRepresentation(image,1);

      

The only way to make my application non-crash is to add the following

- (UIImage *)createUIImage:(const QCAR::Image *)qcarImage
{
    ...
    UIImage *image = [UIImage imageWithCGImage:imageRefRetain];

    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpaceRef);
    CGImageRelease(imageRef);

    NSData* comppressedData = UIImageJPEGRepresentation(image,1);
                    UIImage* jpegImage = [UIImage imageWithData:comppressedData];
    return jpegImage;
}

      

So I think the issue is with the release of the CGImageRef. I really need to understand what is going on and why the ref image is not valid after the existing method.

+3


source to share


2 answers


The problem is that it qcarImage

goes out of scope and takes the raw image data with it. Copy the image buffer and specifyCGDataProviderRef



NSData * imageData = [NSData dataWithBytes:qcarImage->getPixels() 
                                    length:QCAR::getBufferSize(width, height, QCAR::RGB888)];
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, imageData.bytes, imageData.length, NULL);
CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
//This was leaking memory, commenting out
//CGImageRef imageRefRetain = CGImageRetain(imageRef);
UIImage *image = [UIImage imageWithCGImage:imageRef];

      

+1


source


The central issue is that creating a CGImageRef using the CGDataProvider wrapper doesn't copy the data, which it just wraps with a base address, which will disappear when Vuforia decides it needs it. It is very important to note that copying data will have significant success.

QCAR :: Image to CGImageRef

Note. I avoid mixing Obj-C in this method.

CGImageRef CGImageCreateWithQCARImage(const QCAR::Image *image, float rotation)
{
    CGImageRef quartzImage = NULL;

    if (image) {
        QCAR::PIXEL_FORMAT pixelFormat = image->getFormat();

        CGColorSpaceRef colorSpace = NULL;
        switch (pixelFormat) {
            case QCAR::RGB888:
                colorSpace = CGColorSpaceCreateDeviceRGB();
                break;
            case QCAR::GRAYSCALE:
                colorSpace = CGColorSpaceCreateDeviceGray();
                break;
            case QCAR::YUV:
            case QCAR::RGB565:
            case QCAR::RGBA8888:
            case QCAR::INDEXED:
                OARLogError("Image format conversion not implemented.");
                break;
            case QCAR::UNKNOWN_FORMAT:
                OARLogError("Image format unknown.");
                break;
        }

        if (colorSpace != NULL) {
            size_t bitsPerComponent = 8;
            size_t width = image->getWidth();
            size_t height = image->getHeight();
            const void *baseAddress = image->getPixels();
            size_t totalBytes = QCAR::getBufferSize(width, height, pixelFormat);

            CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast;
            CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

            size_t bitsPerPixel = QCAR::getBitsPerPixel(pixelFormat);
            size_t bytesPerPixel = 4;
            size_t bytesPerRow = image->getStride();

            CGContextRef context = CGBitmapContextCreate(NULL,
                                                         width,
                                                         height,
                                                         bitsPerComponent,
                                                         bytesPerPixel * width,
                                                         colorSpace,
                                                         bitmapInfo);

            CFDataRef dataRef = CFDataCreate(NULL, (const UInt8 *)baseAddress, totalBytes);
            CGDataProviderRef provider = CGDataProviderCreateWithCFData(dataRef);
            CGImageRef intermediateImage = CGImageCreate(width,
                                                         height,
                                                         bitsPerComponent,
                                                         bitsPerPixel,
                                                         bytesPerRow,
                                                         colorSpace,
                                                         bitmapInfo,
                                                         provider,
                                                         NULL,
                                                         false,
                                                         renderingIntent);

            CGFloat halfWidth = width / 2.f;
            CGFloat halfHeight = height / 2.f;
            CGContextTranslateCTM(context, halfWidth, halfHeight);
            CGContextRotateCTM(context, rotation);
            CGContextScaleCTM(context, -1.f, -1.f);

            CGContextDrawImage(context, CGRectMake(-halfWidth, -halfHeight, width, height), intermediateImage);
            CGImageRelease(intermediateImage);

            quartzImage = CGBitmapContextCreateImage(context);
            CGContextRelease(context);

            CGColorSpaceRelease(colorSpace);
            CGDataProviderRelease(provider);
            CFRelease(dataRef);
        }
    }

    return quartzImage;
}

      



CGImageRef to UIImage

Then again on the ground Obj-C.

QCAR::Image qcarImage = ...; 
CGImageRef imageRef = CGImageCreateWithQCARImage(qcarImage);
UIImage *image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);

      

You may be asking yourself why allocate the context bitmap ( NULL

) rather than wrap existing bytes ( baseAddress

)? The answer is that you cannot create a context with weakness 2. 24 bits per pixel is not allowed and you will get a null context if you try to set bytesPerRow to be "pixel width" * 3 bytes per pixel. So we create an image and then paint it in the context of the alpha restoration.

0


source







All Articles