Skip to content

Instantly share code, notes, and snippets.

@bhautikj
Created May 25, 2016 06:58
Show Gist options
  • Save bhautikj/8d9514421c720aa1f91491e237eb1aa9 to your computer and use it in GitHub Desktop.
Save bhautikj/8d9514421c720aa1f91491e237eb1aa9 to your computer and use it in GitHub Desktop.
Tool to sync timelapse sequences across multiple cameras.
#
# lapseSync.py
# ------------
#
# author: Bhautik Joshi, 2016
# license: Apache 2.0 http://www.apache.org/licenses/LICENSE-2.0
#
# Tool to ingest timelapse sequences from multiple cameras and generate a list
# of images across the cameras to be synced to a single clock specified by a
# given interval. The images are synced to the earliest image in each directory.
#
# Requirements:
# * python 2.7 (ideally)
# * exiftool
#
# on osx, exiftool can be installed using:
# /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
# brew install exiftool
#
# Usage:
# python lapseSync.py <interval> <dir1> <dir2> <dir3> ....
#
# Interval is in seconds, and dir1, dir2 etc are the directory paths with the
# images.
#
# Output is of the format (first line is just a header):
# time,sample/1,sample/2,sample/3,sample/4
# 2016-04-12T16:50:09,sample/1/echeng160412_165020.arw,sample/2/echeng160412_165022.arw,sample/3/echeng160412_165009.arw,sample/4/echeng160412_165017.arw
# 2016-04-12T16:50:11,sample/1/echeng160412_165022.arw,sample/2/echeng160412_165024.arw,sample/3/echeng160412_165011.arw,sample/4/echeng160412_165019.arw
#
# requires: exiftool
# brew install exiftool
import sys, subprocess, os
from datetime import datetime
import time
# run a process
def runProcess(cmd):
return subprocess.Popen(cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE).communicate()
# convert the timesamp from exiftool into a unix timestamp
def dateToUnixTime(dt):
# input format:
# 2016:04:12 16:50:20
time_tuple = time.strptime(dt, "%Y:%m:%d %H:%M:%S")
dts = datetime.fromtimestamp(time.mktime(time_tuple))
timestamp = (dts - datetime(1970, 1, 1)).total_seconds()
return int(timestamp)
# get the timestamps for the images in a given directory
def exifCommandForDirectory(loc):
ret = []
cmd = "exiftool -p '$filename , $dateTimeOriginal' -q -f " + loc
output = runProcess(cmd)
for line in output[0].split('\n'):
spl = line.split(',')
if len(spl) < 2:
continue
fn = os.path.join(loc, spl[0].strip())
dt = spl[1].strip()
ts = dateToUnixTime(dt)
ret.append((ts,fn))
return ret
def getTimestampBrackets(lst):
lst = sorted(lst)
return [lst[0][0], lst[-1][0]]
def getClosestInTime(ts, lst):
diff = sys.maxsize
for item in lst:
if abs(ts - item[0]) < diff:
diff = abs(ts - item[0])
rv = item[1]
return rv
def unix_to_iso8601(timestamp):
return datetime.utcfromtimestamp(timestamp).isoformat()
def main(argv):
# assume that the earliest image in each directory is the sync frame
# if clocks on cameras are sync accurately, set this to False
syncToFirst = True
interval = int(argv[1])
numdirs = len(argv) - 1
workDict = {}
workDict["interval"] = interval
workDict["dirs"] = {}
for directory in argv[2:]:
workDict["dirs"][directory] = exifCommandForDirectory(directory)
# get earliest and latest timestamp
earliestList = []
latestList = []
for dr in workDict["dirs"].keys():
bracket = getTimestampBrackets(workDict["dirs"][dr])
earliestList.append(bracket[0])
latestList.append(bracket[1])
earliestTimestamp = sorted(earliestList)[0]
latestTimestamp = sorted(latestList, reverse=True)[0]
if syncToFirst == True:
latestList = []
for dr in workDict["dirs"].keys():
bracket = getTimestampBrackets(workDict["dirs"][dr])
earliest = bracket[0]
latest = bracket[1]
diff = earliestTimestamp - earliest
latest += diff
latestList.append(latest)
newTimes = []
for item in workDict["dirs"][dr]:
newTimes.append((item[0] + diff, item[1]))
workDict["dirs"][dr] = newTimes
latestTimestamp = sorted(latestList, reverse=True)[0]
currentTime = earliestTimestamp
dirkeys = sorted(workDict["dirs"].keys())
print ','.join(['time'] + dirkeys)
while currentTime <= latestTimestamp:
outstr = []
outstr.append(str(unix_to_iso8601(currentTime)))
for dr in dirkeys:
outstr.append(getClosestInTime(currentTime, workDict["dirs"][dr]))
print ','.join(outstr)
currentTime += interval
if __name__ == "__main__":
main(sys.argv)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment