[Swift] CIImage の EXIF とかの書き換えが楽になるライブラリ作ってみた
概要
Swift で、生成した CIImage の Exif を書き換えたいときって、ありますよね。
CIImage.properties
をコピーしたものを書き換えた上で、settingProperties(:)
に書き換えた properties
を渡して実行すれば、書き換えられた CIImage をゲット出来るんですが、この properties
の書き換えがまあ面倒くさいんですよ。
- 書き換えたいデータのキーは
ImageIO
のドキュメント をにらめっこしながら探さないといけない - Exif の
DateTimeOriginal
とDateTimeDigitized
は String で、しかもフォーマットがyyyy:MM:dd HH:mm:ss
っていうまあクセつよなフォーマットなのでDateFormatter
をいじってパースするしかない (TIFF の他の日付系のタグもほぼ全部これ)
とまあこんな感じでタイプセーフじゃないし書き換えるコードが長くなりがちで面倒くさすぎるので、ライブラリーを作りました
作ったライブラリーの概要
地味に人生で初めて作った Swift Package でした。
このライブラリーは、Codable
を使い、CIImage.properties
をパースしてタイプセーフに扱えるようにしたものです。
CIImage.properties
をいじるときは必ず Image I/O が絡むので、名前は SwiftyImageIO
としました。
[String: Any]
なものをどうやってパースするか、その方法の模索に時間がかかりましたが、今回のケースにぴったりな DictionaryCoder
が見つかったのでありがたかったです。
導入するとどうなるか
-
例として、Exif の書き換えるときのコードはこんな感じになります。
-
導入前
import CoreImage import Foundation import ImageIO extension DateFormatter { static var tiff: DateFormatter { let formatter: DateFormatter = .init() formatter.locale = NSLocale.system formatter.dateFormat = "yyyy:MM:dd HH:mm:ss" return formatter } } let testCIImage: CIImage? = .init(contentsOf: URL(string: "file:///path/to/image")) guard var imageProperties: [String: Any] = testCIImage?.properties else { return } var exifDictionary: [String: Any]? = imageProperties[kCGImagePropertyExifDictionary as String] as? [String: Any] exifDictionary?[kCGImagePropertyExifDateTimeDigitized as String] = DateFormatter.tiff.date(from: .now) imageProperties[kCGImagePropertyExifDictionary as String] = exifDictionary let metadataModifiedCIImage: CIImage = testCIImage?.settingProperties(imageProperties)
-
導入後
import CoreImage import Foundation import SwiftyImageIO let testCIImage: CIImage? = .init(contentsOf: URL(string: "file:///path/to/image")) guard var imageProperties: ImageIOProperties = testCIImage?.swiftyImageProperties else { return } imageProperties.exif?.dateTimeDigitized = .now let metadataModifiedCIImage: CIImage? = try? testCIImage?.settingProperties(imageProperties)
いかがでしょう。
導入前はぐちゃぐちゃだったものが、たった数行で日付が書き換わったCIImage
が手に入ります🤩気持ちいいですね🥰
-
-
CGImagePropertyOrientation
のデバッグ時、ちゃんと case の名前が表示されるようになるCGImagePropertyOrientation
は、Vision Framework をいじる際や、CIImage の画像の向きを扱う際に使うことになるものなのですが、普通にprint()
してもこんな感じでケース名を表示してくれないんです。
デバッグのとき、地味に厄介です。
SwiftyImageIO
ではこれを解消しました。
ライブラリー作成して得た学び
-
rawValue
は Computed Property を使えばリテラルじゃなくても問題ないCodable な struct を作るにあたって、CodingKeys には Image I/O で用意されている辞書のキーの定数群をそのまま使いたい思いがありました。
ただ、以下のように書くとコンパイルエラーが発生します。(中略) enum CodingKeys: String, CodingKey { case gps = kCGImagePropertyGPSDictionary ... } (中略)
何かいい方法はないかとググっていたら、これを見つけました。
https://stackoverflow.com/a/31216091/18698351
Computed Property を使えば、定数もそのまま活用できる、というのが今回の学びでした。
(中略) enum CodingKeys: String, CodingKey { case gps var rawValue: String { // ← これ!! switch self { case .gps: return kCGImagePropertyGPSDictionary } } ... } (中略)
これのおかげで、辞書のキーの内容を知らなくても実装できたので、めちゃくちゃ助かりました
余談
まだ作りかけではあるのですが、とりあえず EXIF
, GPS
, TIFF
は Codable な struct への書き下しが終わっています。
今後もぼちぼち書き下ししていきたいと思っています
Discussion