記録。

めも。

AVFoundationを使用して撮影した写真のExif情報を取得したり、追加してみる(その2)

前回、書き込む方の記事を書かなかったので、今から書きます。

とりあえず、コードを貼ります。

import UIKit
import AVFoundation

class ViewController: UIViewController, CameraButtonDelegate, AVCapturePhotoCaptureDelegate {

  //viewDidLoadなど省略しています
    
    func takePicture() {
        let settings = AVCapturePhotoSettings()
        self.output.capturePhoto(with: settings, delegate: self)
    }
    
    
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        if let imageData = photo.fileDataRepresentation() {
            let exifSample = ExifSample()
            //exif読み込み
            exifSample.read(imageData)
            //exif書き込み
            exifSample.write(imageData)
        }
    }
import UIKit
import ImageIO
import MobileCoreServices

class ExifSample: NSObject {

 //書き込み    
    func write(_ data: Data) {
        let ciImage = CIImage(data: data)
        if let ci = ciImage {
            //exifの生成
            let exif = NSMutableDictionary()
            exif.setObject("take a picture!!", forKey: kCGImagePropertyExifUserComment as! NSCopying)
            //メタデータの生成
            let metaData = NSMutableDictionary()
            metaData.setObject(exif, forKey: kCGImagePropertyExifDictionary as! NSCopying)
            //ciimageをcgimageに変換
            let context = CIContext(options: nil)
            let cgImage = context.createCGImage(ci, from: ci.extent)
            //書き込まれる画像データと書き込む情報の保存先の生成
            let imageData = NSMutableData()
            let dest: CGImageDestination? =  CGImageDestinationCreateWithData(imageData, kUTTypeJPEG, 1, nil)
            
            if let wrappedDest = dest, let wrappedCgImage = cgImage {
                //書き込み
                //新しく書き込まれた画像データはimageDataに保存される
                CGImageDestinationAddImage(wrappedDest, wrappedCgImage, metaData)
                CGImageDestinationFinalize(wrappedDest)
                
                //書き込み確認
                confirm(imageData)
            } else {
                print("wrappedDest is nil")
            }
        } else {
            print("ci is nil")
        }
    }
    
 //書き込み確認
    func confirm(_ newData: NSMutableData) {
        
        let newString = newData.base64EncodedString(options: .lineLength64Characters)
        let decode = Data(base64Encoded: newString, options: .ignoreUnknownCharacters)
        
        if let wrappedData = decode {
            let newCiImage = CIImage(data: wrappedData)
            if let wrappedNewCi = newCiImage {
                print(wrappedNewCi.properties)
            } else {
                print("wrappedCi is nil")
            }
        }
    }
}
 

ということで、ViewController側のphotoOutputで撮影した画像データ(imageData)を渡してExifSampleのwriteで書き込みを行なっています。

書き込み時にはImageI/Oフレームワーク で提供されているCGImageDestinationなどを使いました。

書き込みたい情報はNSMutableDictionary型の変数にキーと値をセットしています。 最初のexifは「"{Exif}"」キーの下の階層にあるので、最終的には「"{Exif}"」キーの値としてセットして書き込まなければなりません。 ということで上の階層の辞書としてmetaData(こいつも NSMutableDictionary型)でキーにkCGImagePropertyExifDictionaryを指定し、先ほどのexifをセットしています。

撮影した画像データはCGImageに変換して扱っています。

CGImageDestinationCreateWithDataでCGDestinationオブジェクトを作っています。 NSMutableData型のimageDataが書き込む画像データを入れるところです。(書き込み場所の指定)
kUTTypeJPEGで書き込み後のデータタイプを指定、
1は画像ファイル中の画像数(今回は撮影した画像なので1枚)、
optionsは何かあればということですが、特にないのでnilです。

CGImageDestinationAddImageでimageDataに画像データとexif情報が追加されます。

CGImageDestinationFinalize(wrappedDest)で実際に書き込みが行われて、がっちゃんこって感じです。

元々のCGImageが更新されるようなイメージを描いていたのですが、実際には新しいものができる感じっぽいです。(だからimageDataがあると思っている。)

ちなみにCGDestinationっていうのはCGImageDestinationCreateWithDataであったようにどこに書き込むのかの場所を決めたり、 CGImageDestinationAddImage、CGImageDestinationFinalizeのように実際にデータを書き込んだりすることができるオブジェクトです。

(この辺は、ドキュメントやブログを参考に解釈したので、間違っていたらコメントをください。。。)

今回はデータオブジェクトに書き込みをしましたが、URLとかでも行けるっぽいです。



今回のブログの参考資料

アプリ開発ブログ(仮): 画像をメタデータ付きで読み書き

CGImageDestination | Apple Developer Documentation

CGImageDestinationCreateWithData(_:_:_:_:) - Image I/O | Apple Developer Documentation


最後にgithubにコードがあるのでご参考までに。

https://github.com/2takaanthony85/ReadAndWriteExif