Pythonにてraw現像の処理をいろいろ調べたり、追加したりしてきましたが、最後JPEG保存するにあたり、やはりEXIFデータの付加は必須だろうと思い、いろいろ調べてみたメモです。ライブラリとしては、piexifというのを使ってみました。
結論から言ってしまうと、どのようなデータにも当てはまるような、汎用的な記述に行き着きませんでした。Canon限定でうまく動いているようですが、Olympus、Panasonic、iPhoneのrawデータでは一部書き換えが必要です。
piexif
Python上でEXIFを簡単に扱うことができるライブラリです。執筆時点では1.1.3というバージョンになってます。日本の方が作ったもののようです。PyCharmからのインストールはいつものように、File-> Setting-> Project Interpreter から右上の+ボタンを押して、検索のところからpiexifと検索すると見つかり、インストールできました。他にもいろいろ扱う手段はありそうです。
例えば、PIL (Pillow)でもこんな感じで、
from PIL import Image exif = Image.open(filename).info['parsed_exif']
扱えるようですが、rawデータから取ってきてくれなかったり、取ってきた後の編集もやや回りくどかったです。個人的には。
また、他にも、ExifReadというライブラリもあるようです。こちらはrawでも一応開いてはくれましたが、書き込みの方法がぱっと見つからなかったので、あきらめました。こちらも
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がついているのか、するっとこのフローを通せました。コードとしては、
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データには不正なタグがあるのか、読み込めたのですが、書き込めなかったです。なので不正なタグを消して対応することになりました。
コードは
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つのタグを消し去るとうまく行きました。
# この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つをいじることが多いです。
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
コメント