Skip to content

Instantly share code, notes, and snippets.

@tatesuke
Created October 23, 2018 00:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tatesuke/edccda8aff960633f26ab1c77a51b52c to your computer and use it in GitHub Desktop.
Save tatesuke/edccda8aff960633f26ab1c77a51b52c to your computer and use it in GitHub Desktop.
nmeaと突き合わせてjpg画像にexifタグを付加する
import piexif
import re
import datetime
class GpsIdf(object):
def __init__(self, gpsIdf):
self.gpsIdf = gpsIdf
def __lt__(self, other):
if not isinstance(other, GpsIdf):
return NotImplemented
return self.getDateTime() < other.getDateTime()
def getDateTime(self):
date = self.gpsIdf[piexif.GPSIFD.GPSDateStamp].split(":") #2018:09:10
time = self.gpsIdf[piexif.GPSIFD.GPSTimeStamp] #((23, 1), (4, 1), (2455, 100))
return datetime.datetime(int(date[0]), int(date[1]), int(date[2]), int(time[0][0]/time[0][1]), int(time[1][0]/time[1][1]), int(time[2][0]/time[2][1]), tzinfo=datetime.timezone.utc)
def getGpsIdf(self):
return self.gpsIdf
@staticmethod
def loadNmea(path):
nmeas = GpsIdf._getNmeas(path)
gpsIdfs = []
for nmea in nmeas:
gpsIdfs.append(GpsIdf._createGpsIdf(nmea))
gpsIdfs.sort()
return gpsIdfs
@staticmethod
def _getNmeas(paths):
nmeas = []
for path in paths:
with open(path, "r") as f:
lines = f.readlines()
for i in range(0, len(lines), 3):
nmea = {}
assert lines[i].startswith("$GPGGA")
nmea["$GPGGA"] = [lines[i].split(",")]
assert lines[i + 1].startswith("$GPRMC")
nmea["$GPRMC"] = [lines[i + 1].split(",")]
nmeas.append(nmea)
return nmeas
@staticmethod
def _createGpsIdf(nmea):
if nmea.get("$GPRMC") is None:
return None
if GpsIdf._parseLatitudeRef(nmea) == "":
return None
gpsIdf = {}
gpsIdf[piexif.GPSIFD.GPSVersionID] = (2, 0, 0, 0)
gpsIdf[piexif.GPSIFD.GPSLatitudeRef] = GpsIdf._parseLatitudeRef(nmea)
gpsIdf[piexif.GPSIFD.GPSLatitude] = GpsIdf._parseLatitude(nmea)
gpsIdf[piexif.GPSIFD.GPSLongitudeRef] = GpsIdf._parseLongitudeRef(nmea)
gpsIdf[piexif.GPSIFD.GPSLongitude] = GpsIdf._parseLongitude(nmea)
gpsIdf[piexif.GPSIFD.GPSDateStamp] = GpsIdf._parseDateStamp(nmea)
gpsIdf[piexif.GPSIFD.GPSTimeStamp] = GpsIdf._parseTimeStamp(nmea)
if GpsIdf._parseTrack(nmea) != "":
gpsIdf[piexif.GPSIFD.GPSSpeedRef] = GpsIdf._parseSpeedRef(nmea)
gpsIdf[piexif.GPSIFD.GPSSpeed] = GpsIdf._parseSpeed(nmea)
gpsIdf[piexif.GPSIFD.GPSTrackRef] = GpsIdf._parseTrackRef(nmea)
gpsIdf[piexif.GPSIFD.GPSTrack] = GpsIdf._parseTrack(nmea)
if GpsIdf._parseAltitudeRef(nmea) is not None:
gpsIdf[piexif.GPSIFD.GPSAltitudeRef] = GpsIdf._parseAltitudeRef(nmea)
gpsIdf[piexif.GPSIFD.GPSAltitude] = GpsIdf._parseAltitude(nmea)
return GpsIdf(gpsIdf)
# GPSAltitude (11843, 333)
@staticmethod
def _parseAltitude(nmea):
altitude = float(nmea.get("$GPGGA")[0][9])
return (int(altitude * 100 + 0.5), 100)
# GPSAltitudeRef 0
@staticmethod
def _parseAltitudeRef(nmea):
if nmea.get("$GPGGA")[0][9] is None:
return None
altitude = float(nmea.get("$GPGGA")[0][9])
return 0 if altitude >= 0 else 1
# GPSTrack (25832, 227)
@staticmethod
def _parseTrack(nmea):
degreeStr = nmea.get("$GPRMC")[0][8]
if degreeStr == "":
return ""
degree = float(degreeStr)
return (int(degree * 10 + 0.5), 10)
# GPSTrackRef T
@staticmethod
def _parseTrackRef(nmea):
return "T"
# GPSSpeed (0, 1)
@staticmethod
def _parseSpeed(nmea):
knot = float(nmea.get("$GPRMC")[0][7])
km = knot * 0.53996
return (int(km * 10 + 0.5), 10)
# GPSSpeedRef b'K'
@staticmethod
def _parseSpeedRef(nmea):
return "K"
# GPSLongitude ((139, 1), (41, 1), (3390, 100))
@staticmethod
def _parseLongitude(nmea):
m = re.search("(.{2,3})(..)\.(....)", nmea.get("$GPRMC")[0][5])
degree = int(m.group(1))
minutes = int(m.group(2))
sec = int(int(m.group(3)) / 10000 * 60 * 100 + 0.5)
return ((degree, 1), (minutes, 1), (sec, 100))
# GPSLongitudeRef b'E'
@staticmethod
def _parseLongitudeRef(nmea):
return nmea.get("$GPRMC")[0][6]
# GPSLatitude ((35, 1), (44, 1), (260, 100))
@staticmethod
def _parseLatitude(nmea):
m = re.search("(.{2,3})(..)\.(....)", nmea.get("$GPRMC")[0][3])
degree = int(m.group(1))
minutes = int(m.group(2))
sec = int(int(m.group(3)) / 10000 * 60 * 100 + 0.5)
return ((degree, 1), (minutes, 1), (sec, 100))
# GPSLatitudeRef b'N'
@staticmethod
def _parseLatitudeRef(nmea):
return nmea.get("$GPRMC")[0][4]
# GPSTimeStamp ((23, 1), (4, 1), (2455, 100))
@staticmethod
def _parseTimeStamp(nmea):
m = re.search("(..)(..)(..)\\.(..)", nmea.get("$GPRMC")[0][1])
assert m, nmea.get("$GPRMC")[0][1]
hour = (int(m.group(1)), 1)
minute = (int(m.group(2)), 1)
sec = (int(m.group(3)) * 100 + int(m.group(4)), 100)
return (hour, minute, sec)
# GPSDateStamp b'2018:09:10'
@staticmethod
def _parseDateStamp(nmea):
m = re.search("(..)(..)(..)", nmea.get("$GPRMC")[0][9])
assert m, nmea.get("$GPRMC")[0][9]
dd = m.group(1)
mm = m.group(2)
yy = m.group(3)
return "20%s:%s:%s" % (yy, mm, dd)
import glob
import piexif
import re
from GpsIdf import GpsIdf
import datetime
import pytz
def main():
gpsIdfs = GpsIdf.loadNmea(glob.glob('nmea/*.nme*'))
# print([gpsIdf.getDateTime() for gpsIdf in gpsIdfs])
photos = glob.glob('photos/*.jpg')
for photo in photos:
exifDict = piexif.load(photo)
photoDateTime = getDateTime(exifDict)
index = search(gpsIdfs, photoDateTime)
print(photo, index)
if index < 0:
continue
exifDict["GPS"] = gpsIdfs[index].getGpsIdf()
exifBytes = piexif.dump(exifDict)
piexif.insert(exifBytes, photo, photo)
def getDateTime(exifDict):
org = exifDict["Exif"][piexif.ExifIFD.DateTimeOriginal]
year = int(org[0:4].decode())
month =int(org[5:7].decode())
day = int(org[8:10].decode())
hour = int(org[11:13].decode())
minute = int(org[14:16].decode())
sec = int(org[17:19].decode())
jst = pytz.timezone('Asia/Tokyo')
return jst.localize(datetime.datetime(year, month, day, hour, minute, sec))
def search(gpsIdfs, dateTime):
left = 0
right = len(gpsIdfs) - 1
center = 0
while (left <= right):
center = left + int((right - left) / 2)
target = gpsIdfs[center].getDateTime()
if target == dateTime:
return center
if dateTime > target:
left = center + 1
else:
right = center - 1
delta = gpsIdfs[center].getDateTime() - dateTime
if delta.total_seconds() <= (60 * 5):
return center
return -1
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment