Python - 写真から位置情報を読み取って csv ファイルとして保存する
概要
写真から緯度経度の情報を取り出して、csv ファイルにする Python Script を作りました。
地名と緯度経度の情報を別ファイルで用意すれば、一番近い場所の地名を返します。
準備
1.Pillow をインストールします。
sudo pip3 install pillow で行けるでしょうか。
この意味がわからない人は下の方を読んでください。
2.Address_List.csv ファイルを用意します。
場所と緯度経度の対応表です。
ファイルの仕様
1行目に、Location Name および Lat and Lon と記入してください。これがないとエラーになります。画像は、LibreOffice Version 7.0.0.3 の画面です。
A列の Location Name として、コードではなく地名を書き込めば応用範囲が広がると思います。
csv 形式で保存します。設定は↓
このファイルがなくても動作します。ない場合には、ファイル名と緯度経度、サイズだけの csv ファイルが出来上がります。
3.Script ファイルを用意します。
下のコードをテキストファイルで保存。拡張子は py です。
それでうまくいくかはわかりません。
私の場合は、アプリケーションフォルダー > Python 3.8 フォルダー > IDLE を起動。メニューから New File を実行し、開いたウィンドウに貼り付けます。
※ 最初に開いたウィンドウに貼り付けるのではなく、New File で開いたウィンドウに貼り付けてから保存、です。
4.次のようにファイルを配置します。
あるフォルダー
┣ このスクリプト
┣ Address_List.csv
┣ jpeg ファイル1
┣ jpeg ファイル2
┣ .......
┣ .......
┣ .......
┣ jpeg ファイルX
┗ Picture_Address.csv(Script が生成するファイル)
GPS データを含まない写真の場合は、ファイル名とサイズだけのリストができあがります。サイズの表記は (1280, 960) です。何かに使うと思って用意しましたが、結局使っていません。
*.jpg 限定で処理しています。
*.jpeg ではだめです。ファイル形式を増やす場合は、Script を修正してください。
手順
Script をダブルクリックして実行します。
Picture_Address.csv というファイルができあがります。
コード
いろいろなところからもらってきて作りあげた継ぎ接ぎだらのコードです。正直、よく理解していません。変数名をのつけ方とか癖のある書き方になっていると思いますが、ご容赦いただければと思います。
# -*- coding: utf-8 -*-
import os
import glob
from PIL import Image
import PIL.ExifTags as ExifTags
import csv
from math import sin, cos, radians, sqrt, asin
def get_gps(fname, dic_address):
MyLocation = ''
Address_Code = ''
Distance = ''
#
# 画像ファイルを開く
#
im = Image.open(fname)
exif = im._getexif()
#
# EXIF情報をゲット
# 辞書に格納する
#
exif = {
ExifTags.TAGS[MyKey]: MyValue
for MyKey, MyValue in exif.items()
if MyKey in ExifTags.TAGS
}
# print("GPSInfo" in exif, ':', os.path.basename(file))
#
# EXIF に GPSInfo タグが含まれているときに処理
#
if 'GPSInfo' in exif:
#
# GPS情報を得る
#
gps_tags = exif['GPSInfo']
#
# GPS情報をゲット
# 辞書に格納する
#
gps = {
ExifTags.GPSTAGS.get(t, t): gps_tags[t]
for t in gps_tags
}
# タグがあるかどうか
is_lat = 'GPSLatitude' in gps
is_lat_ref = 'GPSLatitudeRef' in gps
is_lon = 'GPSLongitude' in gps
is_lon_ref = 'GPSLongitudeRef' in gps
if is_lat and is_lat_ref and is_lon and is_lon_ref:
# 緯度経度情報を得る
# 度分秒 > 十進経緯度
#
# 緯度
lat = gps['GPSLatitude']
lat_ref = gps['GPSLatitudeRef']
# Plus or Minus Sign for lat_Dec
if lat_ref == 'N':
lat_sign = 1.0
elif lat_ref == 'S':
lat_sign = -1.0
# 経度
lon = gps['GPSLongitude']
lon_ref = gps['GPSLongitudeRef']
# Plus or Minus Sign for lon_Dac
if lon_ref == 'E':
lon_sign = 1.0
elif lon_ref == 'W':
lon_sign = -1.0
lat_ang0 = lat_sign * lat[0] + lat[1] / 60 + lat[2] / 3600
lon_ang0 = lon_sign * lon[0] + lon[1] / 60 + lon[2] / 3600
# 角度をラジアンに変換
# Haversine formula では、それぞれφ、λと表記している。
phi0 = radians(lat_ang0)
lam0 = radians(lon_ang0)
EARTH_RADIUS_m = 6378137
radius = EARTH_RADIUS_m
# dic_address: 既知の地番と緯度経度の対応リスト
# データがあるときだけ処理
if dic_address:
MyDic = {}
for k in dic_address:
ref_address = k
ref_location = dic_address[k]
if ',' in ref_location:
lat_ang1 = float(ref_location.split(',')[0])
lon_ang1 = float(ref_location.split(',')[1])
# 角度をラジアンに変換
phi1 = radians(lat_ang1)
lam1 = radians(lon_ang1)
# Haversine formula を使い距離を計算
term0 = sin((phi1 - phi0) / 2.0) ** 2
term1 = sin((lam1 - lam0) / 2.0) ** 2
term1 = cos(phi0) * cos(phi1) * term1
wrk = sqrt(term0 + term1)
d = 2.0 * radius * asin(wrk)
# print(row[0], wrk)
MyDic[ref_address] = d
# 最も近い番地とそこまでの距離を求める
min_Key, min_Value = min(MyDic.items(), key=lambda x: x[1])
#
# print(u'最短は',min_Key, min_Value, 'm')
#
Address_Code = min_Key
Distance = str(min_Value)
MyLocation = str(lat_ang0) + ', ' + str(lon_ang0)
MySize = im.size
return [os.path.basename(file), MyLocation, Address_Code, Distance, MySize]
#
if __name__ == '__main__':
# アドレスリストを辞書型で読み込む
# 同じフォルダーにある Address.csv
# ファイルがなかったら辞書は空に
Address_File = './Address_List.csv'
if os.path.exists(Address_File):
with open(Address_File, 'rt') as csvfile:
reader = csv.DictReader(csvfile)
dic_address = {}
for row in reader:
dic_address[row['Location Name']] = row[u'Lat and Lon']
else:
dic_address = {}
# 結果を書き出す
with open('./Picture_Address.csv', 'w') as f:
writer = csv.writer(f)
# 同じフォルダーにある JPEG ファイルを処理する
files = glob.glob('./*.jpg')
for file in files:
# 写真のGPS情報をゲット
loc = get_gps(file, dic_address)
# 1行書き込む
writer.writerow(loc)
print(f.read)
2020年9月6日追記
最後の行に f.close() と書いていましたが、不要だったので削除しました。
参考にしたサイト
主に次のサイトを参考にしました。
フォルダー内のファイル一覧
EXIF タグの扱い
辞書内包表記が使われていたりしますが、正直理解できていません。
緯度経度から2地点間の距離を求める
辞書の値が最大・最小となるキーと値を同時に取得
各地点間の距離を算出した後、最も近い場所を判定するために使いました。
csv ファイルを辞書型で読み込む
いろいろ検索していると、TECHACADEMY magazine が上位に出てきますが、対話形式の記事がうざいと思います。
MacOX で 初めて Python を使う人のために
Python を使うのが初めての人で、Script が走ればいいや、後のことはなんとかなるさという楽観的な人は、ここに書く手順を参考にしてやってみてください。
あとあと問題になるとしたら、元に戻すのが面倒くさそうだということくらいですかね。戻したいけど戻せない、と文句を言わないようお願いします。
まあ、解決策はネットに書いてあるものと思います。
手順
Python のインストーラーをもらってきてインストールします。次に、モジュールをインストールします。
1.Python のインストール
インストーラーをゲットします。
インストールします。
2.モジュールのインストール
ターミナルを起動します。
※ Finder のメニューバー > 移動 > ユーティリティ とやって、ターミナルという名前のアイコンをダブルクリック
モジュールをインストールするときに、上のサイトを参考にしました。
先に pip をインストールします。
※ pip さえもいらないという人は、Mac には標準で easy_install というのが装備されているようなので、それを使えば良いかと思います。
ターミナルのウィンドウに sudo easy_install pip とタイプして、エンターキーを押します。
ここでパスワードを聞いてきますかね。パスワードは、MacOS の現在のユーザーのパスワードです。
次は pillow のインストールです。
sudo pip3 install pillow とタイプして、エンターキーを押します。
インストールがうまくいったら、ターミナルは閉じて良いです。
正しい閉じ方があるのでしょうか。ありました。
3.開くアプリケーションの設定
私の環境では、Python Script の *.py というファイルには XCode が関連づけられていました。
Script ファイルを右クリック > 情報を見る > このアプリケーションで開く > Python Launcher を設定しました。
以上。
手順が抜けていたら申し訳ありません。
余談
なんでこういうのを作ったかというと、自然観察の記事を効率的に作成したかったからです。というか、面倒くさいのが嫌いなんです。
このスクリプトで得られた緯度経度は Google Map に貼り付けて使えます。最初、Google Map に緯度経度を貼り付けて位置を確認していたのですが、写真が何十枚もあるので面倒になりました。Google Map が検索するたびに縮尺を戻してくるのも面倒でした。
緯度経度から距離を算出できたら自動化できるじゃないかと。実現すれば手作業から解放されるわけです。面倒臭いことをやらなくて良くなるならば、モチベーションも上がります。
Mac でも Windows でも、Python のインストーラーをもらってきてインストールすれば、いつも使っている OS 上で Python Script を動かせるようになります。もちろん自分でもScript を作れるようになります。
しかも、Linux 領域ではなく、いつも使っている OS 上で。
本格的に Python をやる人の場合、環境構築がどうのこうのと難しいことばかり言っていますよね。正直何を言っているのかわかりません。調べれば調べるほど、Python が遠くなっていきます。ま、そんな中で、インストールしてしまえ、となったわけです。やってみたら簡単でした。
そもそも本気で Python やるなら、マシンを用意した方が良くないですか。ていうか、自分の場合、Raspberry PI だったり、Jetson Nano だったりしますけど。
Windows の場合、VBA から Script を起動して、VBA と Python の合わせ技で処理するなんてやっていました。
LibreOffice Basic から Script を起動するのは、まだやってみていません。できるかどうか調べてもいないです。
話は変わりますが、MacOS で Script をダブルクリックすると、余計なウィンドウが開くなあと不満に思っています。
Mac の場合、Python 本体はどこかに隠れていて、ファインダーから Script を起動すると、Python Launcher が隠れている人に Script を取り次いでくれるようです。というか、そんなイメージで見ています。Python Launcher も隠れていていいのですけどね。
ちなみに、OS は MacOSX El Capitan です。
Python Launcher の Preference Run in a Terminal window をオフにすれば、ターミナルウィンドウは表示されませんが、Python Launcher が起動したままになります。Script の処理が終わったら、何事もなかったかのように静けさを取り戻す、それがいいです。
t.koba
この記事が気に入ったらサポートをしてみませんか?