如何从AVFoundations中保存TIFF照片captureStillImageAsynchronouslyFromConnection到iPhone(iOS)上具有EXIF元数据的文件?

时间:2013-06-28 09:07:50

标签: avfoundation tiff exif nsdocumentdirectory

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

问题

我一直在筛选堆栈溢出数周,并为自己的每一个问题找到了解决方案。我有4件事需要工作:

  1. 我需要RGBA数据,因为它来自相机,没有任何压缩
  2. 我需要尽可能多的元数据,尤其是EXIF
  3. 我需要以TIFF格式保存,以便与其他软件兼容并且无损耗
  4. 我需要通过保存文件而不是照片库来保护他人免受随意查看
  5. 我可以使用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的帮助!

2 个答案:

答案 0 :(得分:2)

由于你已经破解了1,3和4,你似乎唯一遇到的障碍是将数据和元数据保存在一起。试试这个(假设未经处理的数据位于名为CMSampleBufferRef的{​​{1}}中,您已经完成了将图形数据放入名为myImageDataSampleBuffer的{​​{1}}中的繁重工作:< / p>

CGImageRef

答案 1 :(得分: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

        }

    }

}

P.S。要使其工作,您必须像这样配置图像缓冲区:

    stillImageOutput = AVCaptureStillImageOutput()

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