[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ではこれを解消しました。
ライブラリー作成して得た学び
-
enum の
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