【问题标题】:How to save a TIFF photo from AVFoundations captureStillImageAsynchronouslyFromConnection to a file with EXIF metadata on an iPhone (iOS)?如何将 AVFoundations captureStillImageAsynchronouslyFromConnection 中的 TIFF 照片保存到 iPhone (iOS) 上带有 EXIF 元数据的文件中?
【发布时间】:2013-06-26 00:20:18
【问题描述】:

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

问题

几周以来,我一直在筛选堆栈溢出问题,并为自己的每一个问题找到了可行的解决方案。我有 4 件事需要处理:

  1. 我需要来自相机的 RGBA 数据,不进行任何压缩
  2. 我需要尽可能多的元数据,尤其是 EXIF
  3. 我需要保存为 TIFF 格式,以便与其他软件兼容和无损
  4. 我需要通过保存在文件而不是照片库中来防止随意查看

我可以使用 JPEG 获得 2 和 4。 我可以使用从相机缓冲区制作的原始数据(分别为 NSData)获得 1、3 和 4。 我可以使用 Xcode 和 iOS 满足所有 4 个先决条件吗?我即将放弃并寻找您的意见作为最后的手段。

虽然仍在探索这一点,但我也被困在我尝试过的另一条道路上,libtiff。不过,我还在努力……

以下是我尝试过的好建议列表,我自己的代码只是从堆栈溢出源中整理出来的,如下所示:

解决方案

来自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 的帮助!

【问题讨论】:

    标签: avfoundation tiff exif nsdocumentdirectory


    【解决方案1】:

    由于您已经破解了 1、3 和 4,看来您缺少的唯一障碍是将数据和元数据保存在一起。试试这个(假设未处理的数据位于名为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);
    

    【讨论】:

    • 非常感谢!我会尽快尝试:)
    • 我不会忘记的。我还没有时间尝试它,因为我现在完全忙于我的软件中的另一个问题。承诺!
    • 请查看this question。如何读回这些文件(需要在UICollectionView 中显示缩略图)?
    【解决方案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) ]
    

    【讨论】:

      猜你喜欢
      • 2011-12-19
      • 1970-01-01
      • 2012-11-21
      • 2012-02-16
      • 1970-01-01
      • 1970-01-01
      • 2018-11-10
      • 1970-01-01
      • 2010-09-15
      相关资源
      最近更新 更多