如何在iPhone(iOS)上将AVFoundation的captureStillImageAsynchronouslyFromConnection捕获的TIFF照片保存为带有EXIF元数据的文件?

3

这个问题我只询问了在Xcode和iOS中没有外部库的情况下可能性。我已经在另一个问题中探索使用libtiff的可能性。

问题

我已经在Stack Overflow上筛选了数周,并找到了每个问题的可行解决方案。我有4个需要解决的问题:

  1. 我需要从相机获取RGBA数据,不进行任何压缩
  2. 我需要尽可能多的元数据,特别是EXIF
  3. 我需要以TIFF格式保存,以便与其他软件兼容并保持无损
  4. 我需要通过保存在文件而不是照片库中来保护免受随意查看

通过使用JPEG,我可以满足2和4。 通过从相机缓冲区制作原始数据(分别为NSData),我可以满足1、3和4。 在Xcode和iOS中,我是否能够满足所有4个先决条件?我即将放弃,寻求您作为最后一种选择的建议。

虽然我还在探索中,但我也陷入了我尝试的另一条路libtiff。不过我仍在努力...

这是我尝试过的伟大建议列表,我的代码只是从像这样的stackoverflow来源组合而成:

解决方案

captureStillImageAsynchronouslyFromConnection开始的完整操作序列:

[[self myAVCaptureStillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
 {
     //get all the metadata in the image
     CFDictionaryRef metadata = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate);
     // get image reference
     CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(imageSampleBuffer);
     // >>>>>>>>>> lock buffer address
     CVPixelBufferLockBaseAddress(imageBuffer, 0);

     //Get information about the image
     uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
     size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
     size_t width = CVPixelBufferGetWidth(imageBuffer);
     size_t height = CVPixelBufferGetHeight(imageBuffer);

     // create suitable color space
     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

     //Create suitable context (suitable for camera output setting kCVPixelFormatType_32BGRA)
     CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);

     // <<<<<<<<<< unlock buffer address
     CVPixelBufferUnlockBaseAddress(imageBuffer, 0);

     // release color space
     CGColorSpaceRelease(colorSpace);

     //Create a CGImageRef from the CVImageBufferRef
     CGImageRef newImage = CGBitmapContextCreateImage(newContext);

     // release context
     CGContextRelease(newContext);

     // create destination and write image with metadata
     CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:filePath isDirectory:NO];
     CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypeTIFF, 1, NULL);
     CGImageDestinationAddImage(destination, imageRef, metadata);

     // finalize and release destination
     CGImageDestinationFinalize(destination);
     CFRelease(destination);
 }

与静态图像输出相关的相机设置包括:

[[self myAVCaptureSession] setSessionPreset:AVCaptureSessionPresetPhoto];

并且

NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey, nil];
[myAVCaptureStillImageOutput setOutputSettings:outputSettings];

我得到了一张非压缩的、带有所有元数据的标准TIFF格式的优美图片。(它在其他系统上也被镜像,但是现在我可以写入EXIF和其他元数据,我也可以对其进行微调,我相信。)
再次感谢Wildaker提供的帮助!
2个回答

2

这篇帖子对于解决类似问题非常有帮助,所以我想提供一个Swift 2.0的解决方案实现,以备有人需要。

stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: { (imageDataSampleBuffer, error) -> Void in


    // get image meta data (EXIF, etc)
    let metaData: CFDictionaryRef? = CMCopyDictionaryOfAttachments( kCFAllocatorDefault, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate )


    // get reference to image
    guard let imageBuffer = CMSampleBufferGetImageBuffer( imageDataSampleBuffer ) else { return }


    // lock the buffer
    CVPixelBufferLockBaseAddress( imageBuffer, 0 )


    // read image properties
    let baseAddress = CVPixelBufferGetBaseAddress( imageBuffer )

    let bytesPerRow = CVPixelBufferGetBytesPerRow( imageBuffer )

    let width = CVPixelBufferGetWidth( imageBuffer )

    let height = CVPixelBufferGetHeight( imageBuffer )


    // color space
    let colorSpace = CGColorSpaceCreateDeviceRGB()


    // context - camera output settings kCVPixelFormatType_32BGRA
    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue).union(.ByteOrder32Little)

    let newContext = CGBitmapContextCreate( baseAddress, width, height, 8, bytesPerRow, colorSpace, bitmapInfo.rawValue )


    //unlock buffer
    CVPixelBufferUnlockBaseAddress( imageBuffer, 0 )

    //Create a CGImageRef from the CVImageBufferRef
    guard let newImage = CGBitmapContextCreateImage( newContext ) else {
        return
    }


    // create tmp file and write image with metadata
    let fileName = String(format: "%@_%@", NSProcessInfo.processInfo().globallyUniqueString, "cap.tiff")
    let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(fileName)

    if let destination = CGImageDestinationCreateWithURL( fileURL, kUTTypeTIFF, 1, nil) {

        CGImageDestinationAddImage( destination, newImage, metaData )

        let wrote = CGImageDestinationFinalize( destination )

        if !wrote || NSFileManager.defaultManager().fileExistsAtPath(fileURL.URLString) {

            return

        }

    }

}

附言:为使此功能正常工作,您需要按照以下方式配置您的图像缓冲区:

    stillImageOutput = AVCaptureStillImageOutput()

    stillImageOutput?.outputSettings = [ kCVPixelBufferPixelFormatTypeKey: Int(kCVPixelFormatType_32BGRA) ]

2

既然你已经解决了1、3和4,似乎你唯一缺少的 hurdle 是将数据和元数据一起保存。试试这个方法(假设未处理的数据在名为 myImageDataSampleBufferCMSampleBufferRef 中,而且你已经完成了将图形数据放入名为 myImageCGImageRef 的繁重工作):

CFDictionaryRef metadata = CMCopyDictionaryOfAttachments(kCFAllocatorDefault,
  myImageDataSampleBuffer,
  kCMAttachmentMode_ShouldPropagate);
NSFileManager* fm = [[NSFileManager alloc] init];
NSURL* pathUrl = [fm URLForDirectory:saveDir
  inDomain:NSUserDomainMask
  appropriateForURL:nil
  create:YES
  error:nil];
NSURL* saveUrl = [pathUrl URLByAppendingPathComponent:@"myfilename.tif"];
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)saveUrl,
  (CFStringRef)@"public.tiff", 1, NULL);
CGImageDestinationAddImage(destination, myImage, metadata);
CGImageDestinationFinalize(destination);
CFRelease(destination);

我不会忘记。目前我还没有时间尝试它,因为我正在全力解决软件中的另一个问题。我保证! - AOphagen
请查看这个问题。如何读取这些文件(需要在UICollectionView中显示缩略图)? - AOphagen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接