|
|
|
@ -26,11 +26,13 @@ from optparse import OptionParser
@@ -26,11 +26,13 @@ from optparse import OptionParser
|
|
|
|
|
from numpy import genfromtxt |
|
|
|
|
import shutil |
|
|
|
|
import csv |
|
|
|
|
from datetime import datetime,timedelta |
|
|
|
|
from datetime import datetime, timedelta |
|
|
|
|
from pytz import timezone |
|
|
|
|
|
|
|
|
|
class TriggerList( object ): |
|
|
|
|
def __init__( self ): |
|
|
|
|
|
|
|
|
|
class TriggerList(object): |
|
|
|
|
|
|
|
|
|
def __init__(self): |
|
|
|
|
self.CAMT_seq = [] |
|
|
|
|
self.CAMT_timestamp = [] |
|
|
|
|
self.GPOS_Lat = [] |
|
|
|
@ -38,11 +40,14 @@ class TriggerList( object ):
@@ -38,11 +40,14 @@ class TriggerList( object ):
|
|
|
|
|
self.GPOS_Alt = [] |
|
|
|
|
self.GPS_GPSTime = [] |
|
|
|
|
|
|
|
|
|
class ImageList( object ): |
|
|
|
|
def __init__( self ): |
|
|
|
|
|
|
|
|
|
class ImageList(object): |
|
|
|
|
|
|
|
|
|
def __init__(self): |
|
|
|
|
self.jpg = [] |
|
|
|
|
self.raw = [] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def to_degree(value, loc): |
|
|
|
|
if value < 0: |
|
|
|
|
loc_value = loc[0] |
|
|
|
@ -51,12 +56,13 @@ def to_degree(value, loc):
@@ -51,12 +56,13 @@ def to_degree(value, loc):
|
|
|
|
|
else: |
|
|
|
|
loc_value = "" |
|
|
|
|
absolute_value = abs(value) |
|
|
|
|
deg = int(absolute_value) |
|
|
|
|
t1 = (absolute_value-deg)*60 |
|
|
|
|
deg = int(absolute_value) |
|
|
|
|
t1 = (absolute_value - deg) * 60 |
|
|
|
|
min = int(t1) |
|
|
|
|
sec = round((t1 - min)* 60, 5) |
|
|
|
|
sec = round((t1 - min) * 60, 5) |
|
|
|
|
return (deg, min, sec, loc_value) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def SetGpsLocation(file_name, gps_datetime, lat, lng, alt): |
|
|
|
|
""" |
|
|
|
|
Adding GPS tag |
|
|
|
@ -65,8 +71,10 @@ def SetGpsLocation(file_name, gps_datetime, lat, lng, alt):
@@ -65,8 +71,10 @@ def SetGpsLocation(file_name, gps_datetime, lat, lng, alt):
|
|
|
|
|
lat_deg = to_degree(lat, ["S", "N"]) |
|
|
|
|
lng_deg = to_degree(lng, ["W", "E"]) |
|
|
|
|
|
|
|
|
|
exiv_lat = (pyexiv2.Rational(lat_deg[0] * 60 + lat_deg[1], 60), pyexiv2.Rational(lat_deg[2] * 100, 6000), pyexiv2.Rational(0, 1)) |
|
|
|
|
exiv_lng = (pyexiv2.Rational(lng_deg[0] * 60 + lng_deg[1], 60), pyexiv2.Rational(lng_deg[2] * 100, 6000), pyexiv2.Rational(0, 1)) |
|
|
|
|
exiv_lat = (pyexiv2.Rational(lat_deg[0] * 60 + lat_deg[1], 60), pyexiv2.Rational( |
|
|
|
|
lat_deg[2] * 100, 6000), pyexiv2.Rational(0, 1)) |
|
|
|
|
exiv_lng = (pyexiv2.Rational(lng_deg[0] * 60 + lng_deg[1], 60), pyexiv2.Rational( |
|
|
|
|
lng_deg[2] * 100, 6000), pyexiv2.Rational(0, 1)) |
|
|
|
|
|
|
|
|
|
exiv_image = pyexiv2.ImageMetadata(file_name) |
|
|
|
|
exiv_image.read() |
|
|
|
@ -77,7 +85,8 @@ def SetGpsLocation(file_name, gps_datetime, lat, lng, alt):
@@ -77,7 +85,8 @@ def SetGpsLocation(file_name, gps_datetime, lat, lng, alt):
|
|
|
|
|
date_min = min(date_tag.value, gps_datetime) |
|
|
|
|
time_diff = date_max - date_min |
|
|
|
|
if (time_diff > timedelta(seconds=5)): |
|
|
|
|
print("WARNING, camera trigger and photo time different by {}".format(time_diff)) |
|
|
|
|
print( |
|
|
|
|
"WARNING, camera trigger and photo time different by {}".format(time_diff)) |
|
|
|
|
print(" Photo tag time: {}".format(date_tag.value)) |
|
|
|
|
print(" Camera trigger time: {}".format(gps_datetime)) |
|
|
|
|
|
|
|
|
@ -93,11 +102,13 @@ def SetGpsLocation(file_name, gps_datetime, lat, lng, alt):
@@ -93,11 +102,13 @@ def SetGpsLocation(file_name, gps_datetime, lat, lng, alt):
|
|
|
|
|
|
|
|
|
|
exiv_image.write(True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def LoadPX4log(px4_log_file): |
|
|
|
|
""" |
|
|
|
|
load px4 log file and extract trigger locations |
|
|
|
|
""" |
|
|
|
|
os.system('python sdlog2_dump.py ' + px4_log_file + ' -t time -m TIME -m CAMT -m GPOS -m GPS -f log.csv') |
|
|
|
|
os.system('python sdlog2_dump.py ' + px4_log_file + |
|
|
|
|
' -t time -m TIME -m CAMT -m GPOS -m GPS -f log.csv') |
|
|
|
|
f = open('log.csv', 'rb') |
|
|
|
|
reader = csv.reader(f) |
|
|
|
|
headers = reader.next() |
|
|
|
@ -121,6 +132,7 @@ def LoadPX4log(px4_log_file):
@@ -121,6 +132,7 @@ def LoadPX4log(px4_log_file):
|
|
|
|
|
|
|
|
|
|
return trigger_list |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def LoadImageList(input_folder): |
|
|
|
|
""" |
|
|
|
|
load the image list |
|
|
|
@ -140,25 +152,29 @@ def LoadImageList(input_folder):
@@ -140,25 +152,29 @@ def LoadImageList(input_folder):
|
|
|
|
|
image_list.raw = sorted(image_list.raw) |
|
|
|
|
return image_list |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def FilterTrigger(trigger_list, image_list): |
|
|
|
|
""" |
|
|
|
|
filter triggers to allow exact matching with recorded images |
|
|
|
|
""" |
|
|
|
|
# filter trigger list to match the number of pics |
|
|
|
|
if len(image_list.jpg) != len(trigger_list.CAMT_seq): |
|
|
|
|
print('WARNING! differ number of jpg images ({}) and camera triggers ({})'.format(len(image_list.jpg), len(trigger_list.CAMT_seq))) |
|
|
|
|
print('WARNING! differ number of jpg images ({}) and camera triggers ({})'.format( |
|
|
|
|
len(image_list.jpg), len(trigger_list.CAMT_seq))) |
|
|
|
|
|
|
|
|
|
n_overlap = min(len(image_list.jpg), len(trigger_list.CAMT_seq)) |
|
|
|
|
del image_list.jpg[n_overlap:] |
|
|
|
|
|
|
|
|
|
if len(image_list.raw) != len(trigger_list.CAMT_seq): |
|
|
|
|
print('WARNING! differ number of raw images ({}) and camera triggers ({})'.format(len(image_list.raw), len(trigger_list.CAMT_seq))) |
|
|
|
|
print('WARNING! differ number of raw images ({}) and camera triggers ({})'.format( |
|
|
|
|
len(image_list.raw), len(trigger_list.CAMT_seq))) |
|
|
|
|
|
|
|
|
|
n_overlap = min(len(image_list.raw), len(trigger_list.CAMT_seq)) |
|
|
|
|
del image_list.raw[n_overlap:] |
|
|
|
|
|
|
|
|
|
return trigger_list |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def TagImages(trigger_list, image_list, output_folder): |
|
|
|
|
""" |
|
|
|
|
load px4 log file and extract trigger locations |
|
|
|
@ -166,41 +182,44 @@ def TagImages(trigger_list, image_list, output_folder):
@@ -166,41 +182,44 @@ def TagImages(trigger_list, image_list, output_folder):
|
|
|
|
|
for image in range(len(image_list.jpg)): |
|
|
|
|
|
|
|
|
|
print("############################################################") |
|
|
|
|
print('Photo {}: {}'.format(image, image_list.jpg[image])) |
|
|
|
|
print('Photo {}: {}'.format(image, image_list.jpg[image])) |
|
|
|
|
|
|
|
|
|
cam_time = int(trigger_list.GPS_GPSTime[image]) / 1000000 |
|
|
|
|
gps_datetime = datetime.fromtimestamp(cam_time) |
|
|
|
|
|
|
|
|
|
base_path, filename = os.path.split(image_list.jpg[image]) |
|
|
|
|
copyfile(image_list.jpg[image], output_folder + "/" + filename) |
|
|
|
|
SetGpsLocation(output_folder + "/" + filename, gps_datetime, float(trigger_list.GPOS_Lat[image]), float(trigger_list.GPOS_Lon[image]), float(trigger_list.GPOS_Alt[image])) |
|
|
|
|
SetGpsLocation(output_folder + "/" + filename, gps_datetime, float( |
|
|
|
|
trigger_list.GPOS_Lat[image]), float(trigger_list.GPOS_Lon[image]), float(trigger_list.GPOS_Alt[image])) |
|
|
|
|
|
|
|
|
|
for image in range(len(image_list.raw)): |
|
|
|
|
|
|
|
|
|
print("############################################################") |
|
|
|
|
print('Photo {}: {}'.format(image, image_list.raw[image])) |
|
|
|
|
print('Photo {}: {}'.format(image, image_list.raw[image])) |
|
|
|
|
|
|
|
|
|
cam_time = int(trigger_list.GPS_GPSTime[image]) / 1000000 |
|
|
|
|
gps_datetime = datetime.fromtimestamp(cam_time) |
|
|
|
|
|
|
|
|
|
base_path, filename = os.path.split(image_list.raw[image]) |
|
|
|
|
copyfile(image_list.raw[image], output_folder + "/" + filename) |
|
|
|
|
SetGpsLocation(output_folder + "/" + filename, gps_datetime, float(trigger_list.GPOS_Lat[image]), float(trigger_list.GPOS_Lon[image]), float(trigger_list.GPOS_Alt[image])) |
|
|
|
|
SetGpsLocation(output_folder + "/" + filename, gps_datetime, float( |
|
|
|
|
trigger_list.GPOS_Lat[image]), float(trigger_list.GPOS_Lon[image]), float(trigger_list.GPOS_Alt[image])) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main(): |
|
|
|
|
""" |
|
|
|
|
Main method |
|
|
|
|
""" |
|
|
|
|
parser = OptionParser() |
|
|
|
|
parser.add_option("-l", "--logfile", dest = "LogFile", |
|
|
|
|
help = "PX4 log file containing recorded positions", |
|
|
|
|
metavar = "string") |
|
|
|
|
parser.add_option("-i", "--input", dest = "InputFolder", |
|
|
|
|
help = "Input folder containing untagged images in alphabetical order", |
|
|
|
|
type = "string") |
|
|
|
|
parser.add_option("-o", "--output", dest = "OutputFolder", |
|
|
|
|
help = "Output folder to contain tagged images", |
|
|
|
|
type = "string") |
|
|
|
|
parser.add_option("-l", "--logfile", dest="LogFile", |
|
|
|
|
help="PX4 log file containing recorded positions", |
|
|
|
|
metavar="string") |
|
|
|
|
parser.add_option("-i", "--input", dest="InputFolder", |
|
|
|
|
help="Input folder containing untagged images in alphabetical order", |
|
|
|
|
type="string") |
|
|
|
|
parser.add_option("-o", "--output", dest="OutputFolder", |
|
|
|
|
help="Output folder to contain tagged images", |
|
|
|
|
type="string") |
|
|
|
|
|
|
|
|
|
(options, args) = parser.parse_args() |
|
|
|
|
if not options.LogFile: |
|
|
|
|