piexifでrawからのexifデータ取得

python

 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

python現像
スポンサーリンク
キャンプ工学

コメント

タイトルとURLをコピーしました