QCAR :: Image to UIImage - CoreGraphics并保留崩溃

时间:2014-08-21 12:11:44

标签: ios objective-c core-graphics vuforia

我正在使用Vuforia,尝试从相机实例获取像素并转换为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;
}

获取图像后,我将其添加到可变数组中。

从可变数组中检索图像后,每个方法都会崩溃 即。

UIImageView setImage: 

甚至打电话

NSData* comppressedData = UIImageJPEGRepresentation(image,1);

让我的应用不崩溃的唯一方法是添加以下

- (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;
}

所以我认为这是一个与CGImageRef发布相关的问题。 我真的需要了解发生了什么以及为什么在现有方法之后图像引用无效。

2 个答案:

答案 0 :(得分:1)

问题在于qcarImage超出范围并使用原始图像数据。复制图像缓冲区并将其提供给CGDataProviderRef

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 :(得分:0)

核心问题是使用包装CGDataProvider创建CGImageRef不会复制它只包装底层基地址的数据,当Vuforia决定应该这样做时它将消失。请注意,复制数据会对性能产生重大影响非常重要。

QCAR :: Image to CGImageRef

注意:我避免在这种方法中混合使用Obj-C。

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到UIImage

然后回到Obj-C地区。

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

你可能会问自己为什么要分配上下文位图(NULL)而不是包装现有的字节(baseAddress)?答案是你不能创建一个非幂2的上下文。不允许每像素24位,如果你试图将bytesPerRow设置为'width in pixels'* 3,你将获得一个空上下文每像素字节数。'所以我们创建了Image,然后将其绘制到恢复alpha通道的上下文中。