Created
October 23, 2018 00:48
-
-
Save tatesuke/edccda8aff960633f26ab1c77a51b52c to your computer and use it in GitHub Desktop.
nmeaと突き合わせてjpg画像にexifタグを付加する
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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