【问题标题】:Getting a CGImage from CIImage从 CIImage 获取 CGImage
【发布时间】:2013-01-18 15:44:17
【问题描述】:

我有一个从 CIImage 加载的 UIImage:

tempImage = [UIImage imageWithCIImage:ciImage];

问题是我需要将tempImage 裁剪为特定的CGRect,而我知道如何做到这一点的唯一方法是使用CGImage。 问题是在 iOS 6.0 文档中我发现了这个:

CGImage
If the UIImage object was initialized using a CIImage object, the value of the property is NULL.

A.如何从 CIImage 转换为 CGImage? 我正在使用这段代码,但我有内存泄漏(并且无法理解在哪里):

+(UIImage*)UIImageFromCIImage:(CIImage*)ciImage {  
    CGSize size = ciImage.extent.size;  
    UIGraphicsBeginImageContext(size);  
    CGRect rect;  
    rect.origin = CGPointZero;  
    rect.size   = size;  
    UIImage *remImage = [UIImage imageWithCIImage:ciImage];  
    [remImage drawInRect:rect];  
    UIImage *result = UIGraphicsGetImageFromCurrentImageContext();  
    UIGraphicsEndImageContext();  
    remImage = nil;  
    ciImage = nil;  
    //
    return result;  
}

【问题讨论】:

  • 您提到您需要 CGImage 来进行裁剪。正如 Joris Kluivers 所说,您可以通过在 CIImage 上使用 CICrop 过滤器在没有 CGImage 的情况下进行裁剪。你还有什么需要CGImage的吗?如果有,是什么?
  • 另外,关于内存泄漏,您是否尝试过使用 Instruments 的 Leaks 模板?在 Leaks 工具和 Allocations 工具的 Heapshot 工具之间,您应该能够确定您在应用程序中的哪个位置泄漏或积累了内存。
  • @PeterHosey 我做了,我发现由于某种原因我有超过 200 个 CIImage 的实时实例和超过 100 个 CGImage 的实时实例,所有这些都源自这种方法。我只是不知道在哪里

标签: ios core-graphics core-image cgimage ciimage


【解决方案1】:

Swift 3Swift 4Swift 5

这是一个很好的小函数,可以在 Swift 中将 CIImage 转换为 CGImage

func convertCIImageToCGImage(inputImage: CIImage) -> CGImage? {
    let context = CIContext(options: nil)
    if let cgImage = context.createCGImage(inputImage, from: inputImage.extent) {
        return cgImage
    }
    return nil
}

注意事项:

  • CIContext(options: nil) 将使用软件渲染器,可能会很慢。要提高性能,请使用 CIContext(options: [CIContextOption.useSoftwareRenderer: false]) - 这会强制操作在 GPU 上运行,而且速度会更快。
  • 如果您多次使用CIContext,请按照苹果的建议进行缓存。

【讨论】:

    【解决方案2】:

    请参阅 CIContext 文档以了解 createCGImage:fromRect:

    CGImageRef img = [myContext createCGImage:ciImage fromRect:[ciImage extent]];
    

    来自对类似问题的回答:https://stackoverflow.com/a/10472842/474896

    此外,由于您以 CIImage 开头,您可以使用 CIFilter 实际裁剪您的图像。

    【讨论】:

    • 可以而且应该:Core Image 非常懒惰,因为createCGImage:fromRect:(或任何其他需要完成像素的方法)是所有工作实际得到的点完毕;在此之前没有进行实际过滤。使用 Core Image 过滤器进行裁剪实际上会节省大量工作(与裁剪多少成正比),因为那时您不会要求裁剪出的像素,因此它们根本不会被渲染。 (当然,另一种方法是为fromRect: 传递裁剪矩形,这将具有相同的效果。)
    • @Joris Kluivers 感谢您的回答,但我得到了相同的结果: CIContext *context = [CIContext new]; CGImageRef ref = [context createCGImage:ciImage fromRect:[ciImage extent]]; tempImage = [UIImage imageWithCGImage:ref]; CGImageRelease(参考); NSLog(@"tempImage: %f %f",tempImage.size.width,tempImage.size.height);输出:tempImage:0.000000 0.000000
    • @PeterHosey 你是对的,但不幸的是我在进行转换时没有裁剪信息,我需要先进行转换,因为我使用的是 CGImage。谢谢
    • @AndreiStoleru:您可以尝试使用contextWithOptions: 而不是new 来创建上下文。另外,如果您记录[ciImage extent] 的值,您会在日志中得到什么?
    • @PeterHosey NSLog(@"extent: %f",ciImage.extent.size.width); DEBUG -[CamViewController captureOutput:didOutputSampleBuffer:fromConnection:]:180 - extent: 1280.000000 也尝试使用contextWithOptions:,但结果相同。
    【解决方案3】:

    经过一番谷歌搜索后,我发现了这种将 CMSampleBufferRef 转换为 CGImage 的方法:

    + (CGImageRef)imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer // Create a CGImageRef from sample buffer data
    {
        CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
        CVPixelBufferLockBaseAddress(imageBuffer,0);        // Lock the image buffer
    
        uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);   // Get information of the image
        size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
        size_t width = CVPixelBufferGetWidth(imageBuffer);
        size_t height = CVPixelBufferGetHeight(imageBuffer);
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
        CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
        CGImageRef newImage = CGBitmapContextCreateImage(newContext);
        CGContextRelease(newContext);
    
        CGColorSpaceRelease(colorSpace);
        CVPixelBufferUnlockBaseAddress(imageBuffer,0);
        /* CVBufferRelease(imageBuffer); */  // do not call this!
    
        return newImage;
    }
    

    (但我关闭了标签,所以我不知道从哪里得到它)

    【讨论】:

    • 这根本不涉及 CIImage。所以,你真的一直打算从 CMSampleBuffer 创建一个 CGImage,而 CIImage 正是你想要做到这一点的手段?
    • @PeterHosey 您可能会推断出我从AVCaptureOutput 获取sampleBuffer,并且我正在使用CIImage 进行人脸检测。最终目标是从捕获的图像中只裁剪人脸,因为我太愚蠢而无法理解 CIImage 和 CGI​​mage 我搜索了另一个解决方案:CMSampleBuffer ps。我接受了 Joris 的回答,因为这是我问题的正确答案。
    • 啊,人脸检测。这是合法的。你看过 AVCaptureMetadataOutput 和 AVMetadataFaceObject 了吗?
    • @PeterHosey 感谢您的建议,似乎比CIDetector 快;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-23
    • 2010-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多