radiation-tagger/rad_tag.py

167 lines
4.5 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Iterates over a bunch of .jpg or .cr2 files and matches
DateTimeOriginal from Exif tags to DateTime in a csv log
of a GeigerMuellerCounter and writes its value to Exif/ITPC/XMP tags in µS/h
'''
import csv
import argparse
import pytz
import gpxpy
from functions import Radiation, Photo, Match, Exif, Output
# SIFACTOR for GQ Geiger counters
# 300 series: 0.0065 µSv/h / CPM
# 320 series: 0.0065 µSv/h / CPM
# 500 series: 0.0065 µSv/h / CPM
# 500+ series: 0.0065 µSv/h / CPM for the first tube
# 600 series: 0.0065 µSv/h / CPM
# 600+ series: 0.002637 µSv/h / CPM
# Configure argument parser for cli options
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description='''A unix-tyle tool that extracts GPS and/or radiation data
from GPX/CSV files and writes them into the Exif/ITPC/XMP tags of given
photos.'''
)
parser.add_argument(
'-si', '--sifactor',
type=float,
default=0.0065,
help='Factor to multiply recorded CPM with.'
)
parser.add_argument(
'-tz', '--timezone',
type=str,
metavar='Timezone',
default='utc',
help='''Manually set timezone of CSV / and Photo timestamp, defaults to
UTC if omitted. This is useful, if the GPS-Logger saves the time in local
time (without timezone).'''
)
parser.add_argument(
'-d', '--dry',
action='store_true',
help='Dry-run, do not actually write anything.'
)
parser.add_argument(
'csv',
metavar='CSV',
type=str,
help='Geiger counter history file in CSV format.'
)
parser.add_argument(
'-g', '--gpx',
metavar='GPX',
type=str,
help='GPS track in GPX format'
)
parser.add_argument(
'photos',
metavar='Photo',
type=str,
nargs='+',
help='One or multiple photo image files to process.'
)
parser.add_argument(
'-o', '--outdir',
type=str,
default='.',
help='Directory to output processed photos.'
)
args = parser.parse_args()
# Create timezone datetime object
local_timezone = pytz.timezone(args.timezone)
# Initialize two empty lists for all radiation / gps values
radiation_list = []
position_list = []
# Import GeigerCounter log
with open(args.csv, "r") as f:
# Read csv file, filter out lines beginning with #
csv = csv.reader(
filter(lambda row: row[0] != '#', f),
delimiter=',',
skipinitialspace=True
)
# Import only relevant values, that's timestamp and CP/M
for _, csv_raw_time, csv_raw_cpm, _ in csv:
radiation = Radiation(
csv_raw_time,
csv_raw_cpm,
local_timezone,
args.sifactor
)
radiation_list.append(radiation)
# close CSV file
f.close()
# Import GPX track(s)print
if args.gpx:
gpx_file = open(args.gpx, 'r')
gpx_reader = gpxpy.parse(gpx_file)
for track in gpx_reader.tracks:
for segment in track.segments:
for point in segment.points:
point_aware_time = point.time.localize(local_timezone)
position = (
point_aware_time,
point.latitude,
point.longitude,
point.elevation
)
position_list.append(position)
# Inform the user about what is going to happen
if args.dry:
print('Not modifying anything. Just print what would happen without --dry')
else:
if args.outdir == ".":
print('Modifying photos in place (overwrite)')
else:
print('Modifying photos in', str(args.outdir), '(copy)')
# Print table header
print('{:<15} {:<25} {:<22}'.format('filename', 'date / time', 'Matched Data'))
# Iterate over list of photos
for src_photo in args.photos:
# Instantiate photo, copy it to destdir if needed and receive filename
# to work on
photo = Photo(src_photo, local_timezone, args.outdir, args.dry)
# Here the matching magic takes place
match = Match(photo.get_date, radiation_list, position_list)
# Formatted output:
data = Output(
match.radiation_value,
match.position_latitude,
match.position_longitude,
match.position_altitude
)
print(
'{:<15} {:<25} {:<22}'.format(photo.get_photo_basename,
str(photo.get_date),
str(data))
)
# Write exif data
Exif(
photo.get_photo_filename,
args.dry,
match.radiation_value,
match.position_latitude,
match.position_longitude,
match.position_altitude
)