はじめに
これも自分用のメモです。python を使うと楽にできます。注意点として、
- 緯度経度は度分秒なので60進数
- 値は実数ではなく、分数で。(有理数、というのかな)
いくつか関係するライブラリがあります。どれか一つに統一したほうがよいのですが、とりあえず動いたものメモです。(おい)
仕様書?
下記の文献を見つけました。
これによるとGPS Info IFDは以下のように定義されているようです。
たくさんありますが、私が扱ったファイルでは0 - 6 までのタグのみでした。GPSの緯度、経度、高さが読み書きできたので、とりあえずは十分でした。ここでGPSLatitudeRefは一文字の'N'か'S'で北緯、南緯を表します。GPSLatitudeは分数で表します。
python で読み書きする
Pillow で緯度経度を読む
Pillow を使用しました。準備で苦労したかは覚えていません。
pip3 install pillow
下記で緯度経度を読めました。
from PIL import Image
infile = "test.JPG"
img = Image.open( infile )
exif = img._getexif()
for k, v in exif.items():
if k==34853:
print(v)
これで
{0: b'\x02\x00\x00\x00',
1: 'N',
2: (35.0, 20.0, 53.51123),
3: 'E',
4: (137.0, 9.0, 17.83123),
5: b'\x00', 6: 256.123}
のような結果を得ます。先の仕様書のGPS INfo IFD の定義と見比べると意味が分かると思います。度分秒なので、10進法にするには変換が必要です。例えば
deg,minu,sec = 35.0, 20.0, 53.51366
deg + minu/60.0 + sec/3600.0
で 35.34819823888889
を得ます。
piexif でGPS Exif の緯度経度を書き換える
piexifを利用しました。
度分秒と分数への変換を関数にし、Gps exif を読んで書き換える作戦です。Fraction
は実数を有理数に変換してくれる関数です。文字列だと3/4
になるので、分子と分母をset にして返す処理を関数にしています。
def _to_deg(value:float, loc:list):
"""
Convert degree in decimal to degree, minutes, and second. Note Degree and minute is integer and seconds is float.
"""
if value < 0:
loc_value = loc[0] # 'S' or 'E'
elif value > 0:
loc_value = loc[1] # 'N' or 'W'
else:
loc_value = ""
abs_value = abs(value)
deg = int(abs_value) # integer part
t1 = (abs_value-deg)*60
minu = int(t1) # minutes
sec = round((t1 - minu)* 60, 5) # seconds
return (deg, min, sec, loc_value)
from fractions import Fraction
def _change_to_rational(number):
f = Fraction(str(number))
return (f.numerator, f.denominator)
を用いて、以下を実行します。
import piexif
lat, lng, altitude = 39.123, 139.123, 50.123
jpg_filepath = "test.JPG"
# convert decimal to degree, minutes, seconds
lat_deg = to_deg(lat, ["S", "N"])
lng_deg = to_deg(lng, ["W", "E"])
exif_lat = (change_to_rational(lat_deg[0]), change_to_rational(lat_deg[1]), change_to_rational(lat_deg[2]))
exif_lng = (change_to_rational(lng_deg[0]), change_to_rational(lng_deg[1]), change_to_rational(lng_deg[2]))
gps_ifd = {
piexif.GPSIFD.GPSVersionID: (2, 0, 0, 0),
piexif.GPSIFD.GPSAltitudeRef: 0,
piexif.GPSIFD.GPSAltitude: change_to_rational(round(altitude, 3)),
piexif.GPSIFD.GPSLatitudeRef: lat_deg[3],
piexif.GPSIFD.GPSLatitude: exif_lat,
piexif.GPSIFD.GPSLongitudeRef: lng_deg[3],
piexif.GPSIFD.GPSLongitude: exif_lng,
}
gps_exif = {"GPS": gps_ifd}
exif_data = piexif.load(jpg_filepath)
exif_data.update(gps_exif)
exif_bytes = piexif.dump(exif_data)
piexif.insert(exif_bytes, file_name)
これは確かStackOverflow を参考にさせていただいたと思うのですが、linkを無くしてしまいました。見つけたら追加します。
まとめ・雑感
必要にせまらて写真のGPSExif の読み書きをした時のメモです。
- Pillow, piexif を利用したが、何となくどちらかに統一して正しく使うのがよさそう。
えいやの作業メモなので、拡充させていきたい、、、という投稿でした。最近雑だな。。^^; (2020/08/08)
コードのタイポを修正しました。