ゼロからはじめるPython 第42回 Pythonで写真に埋め込まれているGPS情報から撮影場所を調べよう
多くの写真ファイルには、撮影場所の緯度と経度が記録されている。写真に埋め込まれているGPS情報は非常に便利だ。旅行先で撮影した写真など、どこで撮影したのか忘れたとしても、すぐに調べることができる。今回は、写真に埋め込まれているGPS情報を元に、撮影場所を調べるプログラムを作ってみよう。
○写真のGPS情報はどこに記録されている?
スマートフォンなどで写真を撮影した場合、撮影場所が表示できる機能がついている。それが、スマートフォンの中だけであっても便利だ。しかし、写真をPCにコピーしたり、ネットにアップした場合でも、その撮影場所の情報を調べることができる。これは、JPEG画像の中に、撮影場所が記録されていることを意味する。
JPEG画像には、EXIFという撮影情報を記録するための領域があり、そこに、GPS情報や、撮影時のカメラの設定などが保存される。
○写真からEXIF情報を抜き出してみよう
そして、Pythonを使うと、こうした情報は手軽に抽出することができる。つまり、大量の画像を、カメラごとに振り分けたり、撮影場所ごとに振り分けたり、フラッシュを使ったかどうかで振り分けたりすることができるのだ。
以下は、PythonでJPEG画像のEXIF情報を全て抽出して表示するプログラムだ。「get_exif.py」という名前で保存しよう。
from PIL import Image
# 画像ファイルを開く
im = Image.open('test.jpg')
# EXIF情報を得る
exif = im._getexif()
# 一覧で表示
for id, value in exif.items():
print(id, value)
そして、コマンドラインからプログラムを実行してみよう。(macOS/Linuxでは、pythonをpython3コマンドに置き換えて実行しよう。)
python get_exif.py
すると、次のように、画面にEXIFの一覧情報が表示される。
この画面を見ると分かるが、EXIF情報というのは、EXIFタグ番号と実際の値のペアで成り立っていることが分かるだろう。
○EXIFからGPS情報を取得しよう
それでは、ここからGPS情報を抽出して、緯度経度情報を得るプログラムを作ってみよう。なお、EXIFに保存されているGPS情報は、分数形式になっているので、比較的一般的な(35.132, 138.513)のような形式にするには、変換の計算を行う必要がある。
それでは、作ってみよう。以下のプログラムは、EXIFからGPS情報を取り出し、少数形式に変換して表示するプログラムだ。「get_gps.py」という名前で保存しよう。このプログラムは、後でPythonのモジュールとしても使えるように工夫している。
from PIL import Image
import PIL.ExifTags as ExifTags
def get_gps(fname):
# 画像ファイルを開く --- (*1)
im = Image.open(fname)
# EXIF情報を辞書型で得る
exif = {
ExifTags.TAGS[k]: v
for k, v in im._getexif().items()
if k in ExifTags.TAGS
}
# GPS情報を得る --- (*2)
gps_tags = exif["GPSInfo"]
gps = {
ExifTags.GPSTAGS.get(t, t): gps_tags[t]
for t in gps_tags
}
# 緯度経度情報を得る --- (*3)
def conv_deg(v):
# 分数を度に変換
d = float(v[0][0]) / float(v[0][1])
m = float(v[1][0]) / float(v[1][1])
s = float(v[2][0]) / float(v[2][1])
return d + (m / 60.0) + (s / 3600.0)
lat = conv_deg(gps["GPSLatitude"])
lat_ref = gps["GPSLatitudeRef"]
if lat_ref != "N": lat = 0 - lat
lon = conv_deg(gps["GPSLongitude"])
lon_ref = gps["GPSLongitudeRef"]
if lon_ref != "E": lon = 0 - lon
return lat, lon
if __name__ == "__main__":
lat, lon = get_gps("test.jpg")
print(lat, lon)
このプログラムを実行するには、以下のようにコマンドラインでコマンドを実行する。(先ほどと同様に、macOS/Linuxでは、pythonをpython3に置き換えよう。)
python get_gps.py
すると、以下のように、写真の撮影場所の緯度経度情報が表示される。
35.132041666666666 138.51321666666666
この緯度経度情報を、Google Mapsで検索すると以下のようになる。確かに、新東名の清水PAで豚丼を食べた記憶がある。つまり、正しく位置情報を取得できたということだ。
それでは、簡単にプログラムの内容を確認しておこう。(*1)の部分では、画像ファイルを開き、EXIF情報を辞書型で取得する。(*2)の部分では、EXIFからGPS情報に関するデータを取り出す。そして、(*3)の部分で、分数形式の緯度経度情報を分かりやすい少数形式に変換する。
なお、プログラムの分かりやすさを重視してプログラムを作っているので、GPS情報を含まない画像を与えると、エラーが出てプログラムが止まってしまう。必要なら、エラー処理などを加えると良いだろう。
○緯度経度から地名を取得しよう
次に、GPS情報を元に、地名を取得してみよう。実際のところ、GPS情報から地名を取得するのは簡単ではない。なぜなら、世界中にある都市の数は膨大であり、世界中の緯度経度のデータベースを参照する必要があるからだ。しかし、便利なことに、Pythonには緯度経度から世界中の都市を調べるお手軽ライブラリ「reverse_geocoder」がある。これを利用して、緯度経度から地名を取得してみよう。
reverse_geocoderをインストールするには、以下のコマンドを実行しよう。
# Windows / Anacondaを利用している場合
pip install --upgrade reverse_geocoder
# macOS / Linuxを利用している場合
pip3 install --upgrade reverse_geocoder
インストールができたら、画像ファイルから地名を求めるプログラムを作ってみよう。以下のプログラムを「get_area.py」という名前で保存しよう。この時、先ほど作ったプログラム「get_gps.py」と同じディレクトリに配置する必要がある。
import get_gps, reverse_geocoder as rg
# 画像ファイルから緯度経度情報を得る
(lat, lon) = get_gps.get_gps("test.jpg")
# 都市名を調べる
results = rg.search([(lat, lon)])
# 分かりやすく表示
area = {t: results[0][t] for t in results[0]}
print(area['cc'], area['admin1'], area['name'])
プログラムを実行するには、以下のコマンドを実行しよう。
python get_area.py
すると、以下のように表示される。正確ではないものの、だいたい合っているという感じだ。
JP Shizuoka Fujinomiya
また、日本語ではなく英語で表示される。というのも、reverse_geocoderは日本語に対応していない。とは言え、「どこで写真を撮影したのか調べる」という目的は果たしたことにしよう。
参考までに、別の写真でもいろいろ試してみよう。海外で撮影した写真や横浜で撮影した写真で試したところ、正しく都市名が表示された。大きな都市やだいたいの場所が知りたいという用途であれば十分実用的だ。
○まとめ
以上、今回は、JPEGファイルに埋め込まれているEXIF情報、特にGPS情報の扱い方について紹介した。緯度経度が分かれば、そこから地名を調べることもできた。pipコマンド一発でインストールできる手軽なライブラリ「reverse_geocoder」でも、大まかに国や都市名を調べることができた。もしも、より詳細な場所を地図上に表示したい場合には、先ほど紹介したように、Google Mapsを使うこともできるだろう。本稿を参考にすれば、ハードディスクの中の未整理写真を撮影場所ごとに分けるプログラムを作るのも難しくないだろう。
自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。
○写真のGPS情報はどこに記録されている?
スマートフォンなどで写真を撮影した場合、撮影場所が表示できる機能がついている。それが、スマートフォンの中だけであっても便利だ。しかし、写真をPCにコピーしたり、ネットにアップした場合でも、その撮影場所の情報を調べることができる。これは、JPEG画像の中に、撮影場所が記録されていることを意味する。
○写真からEXIF情報を抜き出してみよう
そして、Pythonを使うと、こうした情報は手軽に抽出することができる。つまり、大量の画像を、カメラごとに振り分けたり、撮影場所ごとに振り分けたり、フラッシュを使ったかどうかで振り分けたりすることができるのだ。
以下は、PythonでJPEG画像のEXIF情報を全て抽出して表示するプログラムだ。「get_exif.py」という名前で保存しよう。
from PIL import Image
# 画像ファイルを開く
im = Image.open('test.jpg')
# EXIF情報を得る
exif = im._getexif()
# 一覧で表示
for id, value in exif.items():
print(id, value)
そして、コマンドラインからプログラムを実行してみよう。(macOS/Linuxでは、pythonをpython3コマンドに置き換えて実行しよう。)
python get_exif.py
すると、次のように、画面にEXIFの一覧情報が表示される。
この画面を見ると分かるが、EXIF情報というのは、EXIFタグ番号と実際の値のペアで成り立っていることが分かるだろう。
○EXIFからGPS情報を取得しよう
それでは、ここからGPS情報を抽出して、緯度経度情報を得るプログラムを作ってみよう。なお、EXIFに保存されているGPS情報は、分数形式になっているので、比較的一般的な(35.132, 138.513)のような形式にするには、変換の計算を行う必要がある。
それでは、作ってみよう。以下のプログラムは、EXIFからGPS情報を取り出し、少数形式に変換して表示するプログラムだ。「get_gps.py」という名前で保存しよう。このプログラムは、後でPythonのモジュールとしても使えるように工夫している。
from PIL import Image
import PIL.ExifTags as ExifTags
def get_gps(fname):
# 画像ファイルを開く --- (*1)
im = Image.open(fname)
# EXIF情報を辞書型で得る
exif = {
ExifTags.TAGS[k]: v
for k, v in im._getexif().items()
if k in ExifTags.TAGS
}
# GPS情報を得る --- (*2)
gps_tags = exif["GPSInfo"]
gps = {
ExifTags.GPSTAGS.get(t, t): gps_tags[t]
for t in gps_tags
}
# 緯度経度情報を得る --- (*3)
def conv_deg(v):
# 分数を度に変換
d = float(v[0][0]) / float(v[0][1])
m = float(v[1][0]) / float(v[1][1])
s = float(v[2][0]) / float(v[2][1])
return d + (m / 60.0) + (s / 3600.0)
lat = conv_deg(gps["GPSLatitude"])
lat_ref = gps["GPSLatitudeRef"]
if lat_ref != "N": lat = 0 - lat
lon = conv_deg(gps["GPSLongitude"])
lon_ref = gps["GPSLongitudeRef"]
if lon_ref != "E": lon = 0 - lon
return lat, lon
if __name__ == "__main__":
lat, lon = get_gps("test.jpg")
print(lat, lon)
このプログラムを実行するには、以下のようにコマンドラインでコマンドを実行する。(先ほどと同様に、macOS/Linuxでは、pythonをpython3に置き換えよう。)
python get_gps.py
すると、以下のように、写真の撮影場所の緯度経度情報が表示される。
35.132041666666666 138.51321666666666
この緯度経度情報を、Google Mapsで検索すると以下のようになる。確かに、新東名の清水PAで豚丼を食べた記憶がある。つまり、正しく位置情報を取得できたということだ。
それでは、簡単にプログラムの内容を確認しておこう。(*1)の部分では、画像ファイルを開き、EXIF情報を辞書型で取得する。(*2)の部分では、EXIFからGPS情報に関するデータを取り出す。そして、(*3)の部分で、分数形式の緯度経度情報を分かりやすい少数形式に変換する。
なお、プログラムの分かりやすさを重視してプログラムを作っているので、GPS情報を含まない画像を与えると、エラーが出てプログラムが止まってしまう。必要なら、エラー処理などを加えると良いだろう。
○緯度経度から地名を取得しよう
次に、GPS情報を元に、地名を取得してみよう。実際のところ、GPS情報から地名を取得するのは簡単ではない。なぜなら、世界中にある都市の数は膨大であり、世界中の緯度経度のデータベースを参照する必要があるからだ。しかし、便利なことに、Pythonには緯度経度から世界中の都市を調べるお手軽ライブラリ「reverse_geocoder」がある。これを利用して、緯度経度から地名を取得してみよう。
reverse_geocoderをインストールするには、以下のコマンドを実行しよう。
# Windows / Anacondaを利用している場合
pip install --upgrade reverse_geocoder
# macOS / Linuxを利用している場合
pip3 install --upgrade reverse_geocoder
インストールができたら、画像ファイルから地名を求めるプログラムを作ってみよう。以下のプログラムを「get_area.py」という名前で保存しよう。この時、先ほど作ったプログラム「get_gps.py」と同じディレクトリに配置する必要がある。
import get_gps, reverse_geocoder as rg
# 画像ファイルから緯度経度情報を得る
(lat, lon) = get_gps.get_gps("test.jpg")
# 都市名を調べる
results = rg.search([(lat, lon)])
# 分かりやすく表示
area = {t: results[0][t] for t in results[0]}
print(area['cc'], area['admin1'], area['name'])
プログラムを実行するには、以下のコマンドを実行しよう。
python get_area.py
すると、以下のように表示される。正確ではないものの、だいたい合っているという感じだ。
JP Shizuoka Fujinomiya
また、日本語ではなく英語で表示される。というのも、reverse_geocoderは日本語に対応していない。とは言え、「どこで写真を撮影したのか調べる」という目的は果たしたことにしよう。
参考までに、別の写真でもいろいろ試してみよう。海外で撮影した写真や横浜で撮影した写真で試したところ、正しく都市名が表示された。大きな都市やだいたいの場所が知りたいという用途であれば十分実用的だ。
○まとめ
以上、今回は、JPEGファイルに埋め込まれているEXIF情報、特にGPS情報の扱い方について紹介した。緯度経度が分かれば、そこから地名を調べることもできた。pipコマンド一発でインストールできる手軽なライブラリ「reverse_geocoder」でも、大まかに国や都市名を調べることができた。もしも、より詳細な場所を地図上に表示したい場合には、先ほど紹介したように、Google Mapsを使うこともできるだろう。本稿を参考にすれば、ハードディスクの中の未整理写真を撮影場所ごとに分けるプログラムを作るのも難しくないだろう。
自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。