Pythonにてraw現像の処理をいろいろ調べたり、追加したりしてきましたが、最後JPEG保存するにあたり、やはりEXIFデータの付加は必須だろうと思い、いろいろ調べてみたメモです。ライブラリとしては、piexifというのを使ってみました。
結論から言ってしまうと、どのようなデータにも当てはまるような、汎用的な記述に行き着きませんでした。Canon限定でうまく動いているようですが、Olympus、Panasonic、iPhoneのrawデータでは一部書き換えが必要です。
piexif
Python上でEXIFを簡単に扱うことができるライブラリです。執筆時点では1.1.3というバージョンになってます。日本の方が作ったもののようです。PyCharmからのインストールはいつものように、File-> Setting-> Project Interpreter から右上の+ボタンを押して、検索のところからpiexifと検索すると見つかり、インストールできました。他にもいろいろ扱う手段はありそうです。
例えば、PIL (Pillow)でもこんな感じで、
1 2 3 | from PIL import Image exif = Image. open (filename).info[ 'parsed_exif' ] |
扱えるようですが、rawデータから取ってきてくれなかったり、取ってきた後の編集もやや回りくどかったです。個人的には。
また、他にも、ExifReadというライブラリもあるようです。こちらはrawでも一応開いてはくれましたが、書き込みの方法がぱっと見つからなかったので、あきらめました。こちらも
1 2 3 4 | import exifread f = open (filename, 'rb' ) exif = exifread.process_file(f) |
こんな感じで読み込めました。手段はいろいろあるようです。
読み書き(Canon)
基本的には読み取りをpiexifで行い、書き込みをPILで行う。という流れで扱います。サンプルコードがそうなっていたので。なので、処理フローは
1.piexifでrawを読み取り、exif取得
2.rawpyで現像 適宜exif書き換え
3.PILでexif付きで保存
こんな感じになります。Canonのrawデータには素直にEXIFがついているのか、するっとこのフローを通せました。コードとしては、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | from PIL import Image import piexif import rawpy exif_dict = piexif.load(filename) raw = rawpy.imread(filename) img = raw.postprocess(use_camera_wb = True , user_flip = 0 ) # 画像サイズだけ修正 exif_dict[ "0th" ][piexif.ImageIFD.ImageWidth] = img.shape[ 1 ] exif_dict[ "0th" ][piexif.ImageIFD.ImageLength] = img.shape[ 0 ] exif_bytes = piexif.dump(exif_dict) im = Image.fromarray(img) im.save(savefile, "jpeg" , exif = exif_bytes) |
こんな感じになるかと思います。現像時のオプションで、user_flipをあえて無回転に設定してEXIFの情報と合わせています。なので本来サイズを変更する必要はないのですが、サンプルとして。本当はJPEGに変換しているので、それ関連の値も付与した方がいいのかもしれませんが、必要性を感じたことがないので、個人的にはこれで十分です。
ココで辞書に指定している[piexif.ImageIFD.ImageWidth]とかのキーですが、Web上に情報がなく、ソースコードを覗いて調べました。このソースにある数字とEXIFの仕様書から突き合わせて使います。いちいちソース覗くのが面倒なので、最後に該当コードの定義部だけ載せておきます。
読み書き(Panasonic)
上記のCanonのケースはレギュラーケースで、他の会社のRawデータはそのまま使えませんでした。Panasonic(DMC-GM1)のRawデータには不正なタグがあるのか、読み込めたのですが、書き込めなかったです。なので不正なタグを消して対応することになりました。
コードは
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | from PIL import Image import piexif import rawpy exif_dict = piexif.load(filename) raw = rawpy.imread(filename) img = raw.postprocess(use_camera_wb = True , user_flip = 0 ) # 画像サイズだけ修正 exif_dict[ "0th" ][piexif.ImageIFD.ImageWidth] = img.shape[ 1 ] exif_dict[ "0th" ][piexif.ImageIFD.ImageLength] = img.shape[ 0 ] # この3つのタグを消去(何がいけないのか不明) del exif_dict[ "0th" ][piexif.ImageIFD.ProcessingSoftware] del exif_dict[ "0th" ][piexif.ImageIFD.XResolution] del exif_dict[ "0th" ][piexif.ImageIFD.YResolution] exif_bytes = piexif.dump(exif_dict) im = Image.fromarray(img) im.save(savefile, "jpeg" , exif = exif_bytes) |
これで動きました。この3つのタグが悪さをしているようです。なぜか不明です。ダンプすると確かにおかしな値が入ってます。
他にもJPEGにしてしまえば不要なタグはいろいろありそうですが、いったん放置しときます。
読み書き(Olympus)
これが厄介。完全に壊れた値が読み込まれています。機種はOMD E-M10 MkIIです。一部書き換えてどうにかなるレベルではないです。現像処理自体はできるのですごく残念ですが、諦めます。フォーマットの問題なのかなぁ…。
こいつを使う場合には、raw+JPEGで撮影しておいて、JPEGのEXIFをコピーすることで回避しようと思います。JPEGであればこのコードのままで読み込めました。なので、現像時にJPEGも用意しておく必要が出てきます。少しダサいですが、しょうがないです。
読み書き(iPhone SE)
iPhoneのRAWデータもいくつかタグを消す必要がありました。こいつの場合には、上記Panasonicのコードのように、6つのタグを消し去るとうまく行きました。
1 2 3 4 5 6 7 | # この6つのタグを消去 del exif_dict[ "0th" ][piexif.ImageIFD.TileWidth] del exif_dict[ "0th" ][piexif.ImageIFD.TileLength] del exif_dict[ "0th" ][piexif.ImageIFD.TileOffsets] del exif_dict[ "0th" ][piexif.ImageIFD.TileByteCounts] del exif_dict[ "0th" ][piexif.ImageIFD.BlackLevel] del exif_dict[ "0th" ][piexif.ImageIFD.AsShotNeutral] |
他にも消して差し支えないようなタグ(現像してしまえば不要なタグ)もいっぱい入ってましたが、最低限は上記で済みそうです。
まとめ
いろいろ妥協をしていますが、ひとまず手元にあるrawデータのEXIFのコピーを取ることができました。現像したJPEGには付けときたいですよね。現像したいメーカーのRAWデータに応じたコードの修正が必要になりますが、うまい方法が見つかりませんでした。
ちまちま必須タグだけ取り出せばよいのかもしれませんが、メーカーオリジナルタグもあるし、レンズ情報もないオールドレンズの写真もあるので、めんどくさい。ので深追いはやめにします。
おまけ
piexifのタグ定義の箇所を抜粋しときます。よくにらめっこしたので。ファイルは、_exif.pyってのでした。(作者様にお断り入れた方がいいのかな…、こういうのって)
大きく[“0th”]のタグと、[“Exif”]のタグと、[“GPS”]のタグの3つをいじることが多いです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | class ImageIFD: """Exif tag number reference - 0th IFD""" ProcessingSoftware = 11 NewSubfileType = 254 SubfileType = 255 ImageWidth = 256 ImageLength = 257 BitsPerSample = 258 Compression = 259 PhotometricInterpretation = 262 Threshholding = 263 CellWidth = 264 CellLength = 265 FillOrder = 266 DocumentName = 269 ImageDescription = 270 Make = 271 Model = 272 StripOffsets = 273 Orientation = 274 SamplesPerPixel = 277 RowsPerStrip = 278 StripByteCounts = 279 XResolution = 282 YResolution = 283 PlanarConfiguration = 284 GrayResponseUnit = 290 GrayResponseCurve = 291 T4Options = 292 T6Options = 293 ResolutionUnit = 296 TransferFunction = 301 Software = 305 DateTime = 306 Artist = 315 HostComputer = 316 Predictor = 317 WhitePoint = 318 PrimaryChromaticities = 319 ColorMap = 320 HalftoneHints = 321 TileWidth = 322 TileLength = 323 TileOffsets = 324 TileByteCounts = 325 SubIFDs = 330 InkSet = 332 InkNames = 333 NumberOfInks = 334 DotRange = 336 TargetPrinter = 337 ExtraSamples = 338 SampleFormat = 339 SMinSampleValue = 340 SMaxSampleValue = 341 TransferRange = 342 ClipPath = 343 XClipPathUnits = 344 YClipPathUnits = 345 Indexed = 346 JPEGTables = 347 OPIProxy = 351 JPEGProc = 512 JPEGInterchangeFormat = 513 JPEGInterchangeFormatLength = 514 JPEGRestartInterval = 515 JPEGLosslessPredictors = 517 JPEGPointTransforms = 518 JPEGQTables = 519 JPEGDCTables = 520 JPEGACTables = 521 YCbCrCoefficients = 529 YCbCrSubSampling = 530 YCbCrPositioning = 531 ReferenceBlackWhite = 532 XMLPacket = 700 Rating = 18246 RatingPercent = 18249 ImageID = 32781 CFARepeatPatternDim = 33421 CFAPattern = 33422 BatteryLevel = 33423 Copyright = 33432 ExposureTime = 33434 ImageResources = 34377 ExifTag = 34665 InterColorProfile = 34675 GPSTag = 34853 Interlace = 34857 TimeZoneOffset = 34858 SelfTimerMode = 34859 FlashEnergy = 37387 SpatialFrequencyResponse = 37388 Noise = 37389 FocalPlaneXResolution = 37390 FocalPlaneYResolution = 37391 FocalPlaneResolutionUnit = 37392 ImageNumber = 37393 SecurityClassification = 37394 ImageHistory = 37395 ExposureIndex = 37397 TIFFEPStandardID = 37398 SensingMethod = 37399 XPTitle = 40091 XPComment = 40092 XPAuthor = 40093 XPKeywords = 40094 XPSubject = 40095 PrintImageMatching = 50341 DNGVersion = 50706 DNGBackwardVersion = 50707 UniqueCameraModel = 50708 LocalizedCameraModel = 50709 CFAPlaneColor = 50710 CFALayout = 50711 LinearizationTable = 50712 BlackLevelRepeatDim = 50713 BlackLevel = 50714 BlackLevelDeltaH = 50715 BlackLevelDeltaV = 50716 WhiteLevel = 50717 DefaultScale = 50718 DefaultCropOrigin = 50719 DefaultCropSize = 50720 ColorMatrix1 = 50721 ColorMatrix2 = 50722 CameraCalibration1 = 50723 CameraCalibration2 = 50724 ReductionMatrix1 = 50725 ReductionMatrix2 = 50726 AnalogBalance = 50727 AsShotNeutral = 50728 AsShotWhiteXY = 50729 BaselineExposure = 50730 BaselineNoise = 50731 BaselineSharpness = 50732 BayerGreenSplit = 50733 LinearResponseLimit = 50734 CameraSerialNumber = 50735 LensInfo = 50736 ChromaBlurRadius = 50737 AntiAliasStrength = 50738 ShadowScale = 50739 DNGPrivateData = 50740 MakerNoteSafety = 50741 CalibrationIlluminant1 = 50778 CalibrationIlluminant2 = 50779 BestQualityScale = 50780 RawDataUniqueID = 50781 OriginalRawFileName = 50827 OriginalRawFileData = 50828 ActiveArea = 50829 MaskedAreas = 50830 AsShotICCProfile = 50831 AsShotPreProfileMatrix = 50832 CurrentICCProfile = 50833 CurrentPreProfileMatrix = 50834 ColorimetricReference = 50879 CameraCalibrationSignature = 50931 ProfileCalibrationSignature = 50932 AsShotProfileName = 50934 NoiseReductionApplied = 50935 ProfileName = 50936 ProfileHueSatMapDims = 50937 ProfileHueSatMapData1 = 50938 ProfileHueSatMapData2 = 50939 ProfileToneCurve = 50940 ProfileEmbedPolicy = 50941 ProfileCopyright = 50942 ForwardMatrix1 = 50964 ForwardMatrix2 = 50965 PreviewApplicationName = 50966 PreviewApplicationVersion = 50967 PreviewSettingsName = 50968 PreviewSettingsDigest = 50969 PreviewColorSpace = 50970 PreviewDateTime = 50971 RawImageDigest = 50972 OriginalRawFileDigest = 50973 SubTileBlockSize = 50974 RowInterleaveFactor = 50975 ProfileLookTableDims = 50981 ProfileLookTableData = 50982 OpcodeList1 = 51008 OpcodeList2 = 51009 OpcodeList3 = 51022 NoiseProfile = 51041 ZZZTestSlong1 = 60606 ZZZTestSlong2 = 60607 ZZZTestSByte = 60608 ZZZTestSShort = 60609 ZZZTestDFloat = 60610 class ExifIFD: """Exif tag number reference - Exif IFD""" ExposureTime = 33434 FNumber = 33437 ExposureProgram = 34850 SpectralSensitivity = 34852 ISOSpeedRatings = 34855 OECF = 34856 SensitivityType = 34864 StandardOutputSensitivity = 34865 RecommendedExposureIndex = 34866 ISOSpeed = 34867 ISOSpeedLatitudeyyy = 34868 ISOSpeedLatitudezzz = 34869 ExifVersion = 36864 DateTimeOriginal = 36867 DateTimeDigitized = 36868 OffsetTime = 36880 OffsetTimeOriginal = 36881 OffsetTimeDigitized = 36882 ComponentsConfiguration = 37121 CompressedBitsPerPixel = 37122 ShutterSpeedValue = 37377 ApertureValue = 37378 BrightnessValue = 37379 ExposureBiasValue = 37380 MaxApertureValue = 37381 SubjectDistance = 37382 MeteringMode = 37383 LightSource = 37384 Flash = 37385 FocalLength = 37386 Temperature = 37888 Humidity = 37889 Pressure = 37890 WaterDepth = 37891 Acceleration = 37892 CameraElevationAngle = 37893 SubjectArea = 37396 MakerNote = 37500 UserComment = 37510 SubSecTime = 37520 SubSecTimeOriginal = 37521 SubSecTimeDigitized = 37522 FlashpixVersion = 40960 ColorSpace = 40961 PixelXDimension = 40962 PixelYDimension = 40963 RelatedSoundFile = 40964 InteroperabilityTag = 40965 FlashEnergy = 41483 SpatialFrequencyResponse = 41484 FocalPlaneXResolution = 41486 FocalPlaneYResolution = 41487 FocalPlaneResolutionUnit = 41488 SubjectLocation = 41492 ExposureIndex = 41493 SensingMethod = 41495 FileSource = 41728 SceneType = 41729 CFAPattern = 41730 CustomRendered = 41985 ExposureMode = 41986 WhiteBalance = 41987 DigitalZoomRatio = 41988 FocalLengthIn35mmFilm = 41989 SceneCaptureType = 41990 GainControl = 41991 Contrast = 41992 Saturation = 41993 Sharpness = 41994 DeviceSettingDescription = 41995 SubjectDistanceRange = 41996 ImageUniqueID = 42016 CameraOwnerName = 42032 BodySerialNumber = 42033 LensSpecification = 42034 LensMake = 42035 LensModel = 42036 LensSerialNumber = 42037 Gamma = 42240 class GPSIFD: """Exif tag number reference - GPS IFD""" GPSVersionID = 0 GPSLatitudeRef = 1 GPSLatitude = 2 GPSLongitudeRef = 3 GPSLongitude = 4 GPSAltitudeRef = 5 GPSAltitude = 6 GPSTimeStamp = 7 GPSSatellites = 8 GPSStatus = 9 GPSMeasureMode = 10 GPSDOP = 11 GPSSpeedRef = 12 GPSSpeed = 13 GPSTrackRef = 14 GPSTrack = 15 GPSImgDirectionRef = 16 GPSImgDirection = 17 GPSMapDatum = 18 GPSDestLatitudeRef = 19 GPSDestLatitude = 20 GPSDestLongitudeRef = 21 GPSDestLongitude = 22 GPSDestBearingRef = 23 GPSDestBearing = 24 GPSDestDistanceRef = 25 GPSDestDistance = 26 GPSProcessingMethod = 27 GPSAreaInformation = 28 GPSDateStamp = 29 GPSDifferential = 30 GPSHPositioningError = 31 |
コメント