【问题标题】:QCAR::Image to UIImage - CoreGraphics and retain crashQCAR::Image 到 UIImage - CoreGraphics 并保留崩溃
【发布时间】:2014-08-21 12:11:43
【问题描述】:

我正在使用 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 发布有关的问题。 我真的需要了解发生了什么以及为什么图像参考在存在方法后无效。

【问题讨论】:

    标签: ios objective-c core-graphics vuforia


    【解决方案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];
    

    【讨论】:

      【解决方案2】:

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

      QCAR::Image 到 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 设置为“像素宽度”*“3”,您将获得空上下文每个像素的字节数。所以我们创建图像,然后将其绘制到恢复 alpha 通道的上下文中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-20
        • 1970-01-01
        • 2015-11-17
        相关资源
        最近更新 更多