Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions ProcessInputDir.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#! /bin/sh -f
rm -rf ${1}/tmp
rm -rf ${1}/tmpOutput
mkdir ${1}/tmp
mkdir ${1}/tmpOutput

python timeLapseRegistrator.py --glob "*.jpg" --convert /c/Program\ Files/ImageMagick-7.0.3-Q16/convert.exe --maskFile ${1}/mask.jpg --tmpDir ${1}/tmp ${1}/input ${1}/tmpOutput
python timeLapseOpticFlowInterpolator.py --glob "*_RegAtlas.png" --interp /c/src/TimeLapse-OpticalFlow-Release/InterpByOpticalFlow.exe ./${1}/tmpOutput
python timeLapseMovieMaker.py --convert /c/Program\ Files/ImageMagick-7.0.3-Q16/convert.exe --glob "*_RegAtlas_?.png" ${1}/tmpOutput ${1}/movie.gif
convert -coalesce ${1}/movie.gif ${1}/tmp/movie%04d.png
ffmpeg -r 10 -i ${1}/tmp/movie%04d.png -vcodec mpeg4 -y ${1}/movie.mp4

rm -rf ${1}/tmpOutputOrg
cp -r ${1}/input ${1}/tmpOutputOrg

python timeLapseOpticFlowInterpolator.py --glob "*.jpg" --interp /c/src/TimeLapse-OpticalFlow-Release/InterpByOpticalFlow.exe ${1}/tmpOutputOrg
python timeLapseMovieMaker.py --convert /c/Program\ Files/ImageMagick-7.0.3-Q16/convert.exe --glob "*_?.jpg" ${1}/tmpOutputOrg ${1}/movie-org.gif
convert -coalesce ${1}/movie-org.gif ${1}/tmpOutputOrg/movie-org%04d.png
ffmpeg -r 10 -i ${1}/tmpOutputOrg/movie-org%04d.png -vcodec mpeg4 -y ${1}/movie-org.mp4
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# TimeLapse-OpticalFlow
# TimeLapse-OpticalFlow

This repo currently consists of:
* Two small c++ programs: one for registering two images, one for interpolating between two registered images
* A python wrapper script to create a TimeLapse from a set of images

C++ build dependencies are ITK and OpenCV.

Locations of some programs/data are hard-coded in the Python wrapper, those will need to be changed depending on where its running.
39 changes: 39 additions & 0 deletions timeLapseMovieMaker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/python

# This program will convert sequence of images into a movie

import os
import imp
import fnmatch
import argparse
from subprocess import run

def ApplyTimeLapseMovieMaker( convert, folder, glob, speed, movie ):
command_string = [convert, "-delay", speed, "-loop", "0"]
for root, dirnames, filenames in os.walk( folder ):
for filename in fnmatch.filter( filenames, glob ):
command_string.append( str( os.path.join( root, filename ) ) )
break # Only use files in top level subdir
command_string.append( movie )

print( command_string )

run( command_string )

if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Apply timeLapseMovieMaker to files in a folder.' )
parser.add_argument( 'folder', help='Folder to apply the script to.' )
parser.add_argument( 'movie', help='Output movie name.' )
parser.add_argument( '--convert',
help='Path to the convert executable from ImageMagick.',
default='convert' )
parser.add_argument( '--glob',
help='Glob to find files recursively in the given folder.',
default='*.???' )
parser.add_argument( '--speed',
help='playback speedup.',
default='10' )
args = parser.parse_args()
ApplyTimeLapseMovieMaker( args.convert, args.folder, args.glob,
args.speed, args.movie )
34 changes: 34 additions & 0 deletions timeLapseOpticFlowInterpolator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/python
import os
import fnmatch
import argparse
import shutil as sh
from subprocess import run

def ApplyTimeLapseOpticFlowInterpolator(folder, glob, interp):
file_list = []
for root, dirnames, filenames in os.walk(folder):
for filename in fnmatch.filter(filenames, glob):
file_list.append(os.path.join(root, filename))
break # only use files in first subdir
number_of_files = len(file_list)
print("Number of files = " + str(number_of_files))
for i, input_file in enumerate(file_list):
fileName1 = input_file
if i<number_of_files-1:
fileName2 = file_list[i+1]
run( [ interp, fileName1, fileName2 ] )

if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Apply timeLapseInterpolator to files in a folder.')
parser.add_argument('folder', help='Folder to apply the script to.')
parser.add_argument('--interp',
help='optic flow interpolator executable.',
default='InterpByOpticalFlow.exe')
parser.add_argument( '--glob',
help='Glob to find files recursively in the given folder.',
default='*.???')
args = parser.parse_args()
ApplyTimeLapseOpticFlowInterpolator(args.folder,
args.glob, args.interp )
181 changes: 181 additions & 0 deletions timeLapseRegistrator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#!/usr/bin/python

# This program will register a sequence of photographs to align a masked
# region

import os
import imp
import fnmatch
import argparse
from subprocess import run
from shutil import copyfile

import itk

def ApplyTimeLapseRegistrator( inDir, outDir, glob, maskFile, tmpDir, convert ):
# TO DO: This scale should be proportional to the mask size or adjusted
# based on some similar logic
edge_scale = "5"

file_list = []
filename_list = []
for root, dirnames, filenames in os.walk( inDir ):
for filename in fnmatch.filter( filenames, glob ):
filename_list.append( filename )
file_list.append( os.path.join( root, filename ) )
break
number_of_files = len( file_list )

print( "Number of files = " + str( number_of_files ) )

# Make a training mask
# Label pixels in the mask at 255 and pixels just outside that
# region as 127.
run( ["ImageMath", maskFile, "-M", "1", "30", "255", "0",
"-a", "1", "-1", maskFile,
"-a", "0.5", "1", maskFile,
"-W", "0", tmpDir+"/maskPlus.mha" ] )

# Register everything with the time 0 image
atlasFile = file_list[0]
run( [convert, atlasFile, outDir + "/" + filename_list[0] + "_RegAtlas.png" ] )

# Optimize use of color images
# Split the image into color channels and determine optimum
# linear combination of color channels that best distinguish
# the pixels in the mask from the pixels just outside of the mask
run( [convert, "-separate", atlasFile, tmpDir+"/base.png" ] )
run( ["EnhanceUsingDiscriminantAnalysis",
"--saveBasisInfo", tmpDir+"/basis.mlda",
"--objectId", "255,127",
"--labelmap", tmpDir+"/maskPlus.mha",
tmpDir + "/base-0.png," + \
tmpDir + "/base-1.png," + \
tmpDir + "/base-2.png",
tmpDir+"/atlasMasked"] )
run( ["EnhanceUsingDiscriminantAnalysis",
"--loadBasisInfo", tmpDir+"/basis.mlda",
"--objectId", "255,127",
tmpDir + "/base-0.png," + \
tmpDir + "/base-1.png," + \
tmpDir + "/base-2.png",
tmpDir+"/atlas"] )
run( ["ImageMath", tmpDir+"/atlas.basis00.mha",
"-b", edge_scale,
"-w", tmpDir+"/atlasB.mha"] )

# Create an registration ROI from the object of interest mask
# Dilate the mask file to create an interesting ROI for registration
# TO DO: Dilation should be proportional to the mask size or
# some similar logic used to define this parameter
run( ["ImageMath", maskFile, "-M", "1", "15", "255", "0",
"-W", "0", tmpDir+"/maskDilate.jpg" ] )
run( [convert, tmpDir+"/maskDilate.jpg", tmpDir + "/atlasMask.png"] )
regMaskFile = tmpDir + "/atlasMask.png"

# Generate an identity transform
run( ["RegisterImages", atlasFile, maskFile,
"--saveTransform", tmpDir+"/identity.tfm",
"--registration", "None",
"--initialization", "None",
"--rigidMaxIterations", "0"] )
copyfile( tmpDir+"/identity.tfm",
tmpDir + "/" + filename_list[0] + "_RegAtlas.tfm" )

for i, curFile in enumerate( file_list ):

print( "*** " + curFile )

if i>0:
tmpCurFile = tmpDir + "/" + filename_list[i]
outCurFile = outDir + "/" + filename_list[i]

preTransformFile = tmpDir + "/" + filename_list[i-1] + "_RegAtlas.tfm"

# Blend color channels using basis
run( [convert, "-separate", curFile, tmpDir+"/base.png" ] )
run( ["EnhanceUsingDiscriminantAnalysis",
"--loadBasisInfo", tmpDir+"/basis.mlda",
"--objectId", "255,127",
tmpDir + "/base-0.png," + \
tmpDir + "/base-1.png," + \
tmpDir + "/base-2.png",
tmpDir + "/cur"] )
run( ["ImageMath", tmpDir+"/cur.basis00.mha",
"-b", edge_scale,
"-w", tmpDir + "/curB.mha"] )

# Register the current image (blurred) to the atlas
run( ["RegisterImages", tmpDir+"/AtlasB.mha",
tmpDir+"/curB.mha",
"--resampledImage", tmpCurFile + "_RegAtlasB.mha",
"--saveTransform", tmpCurFile + "_RegAtlas.tfm",
"--registration", "Affine",
"--loadTransform", preTransformFile,
"--initialization", "None",
"--expectedRotation", "0.001",
"--expectedScale", "0.01",
"--expectedSkew", "0.0001",
"--expectedOffset", "60",
"--fixedImageMask", regMaskFile,
"--affineSamplingRatio", "0.5",
"--affineMaxIterations", "1000",
"--rigidSamplingRatio", "0.5",
"--rigidMaxIterations", "1000"] )
#"--metric", "MeanSqrd",

# Apply the image to atlas transform to the current image (raw)
run( ["RegisterImages", atlasFile,
tmpDir+"/base-0.png",
"--resampledImage", tmpDir+"/RegAtlas-0.mha",
"--loadTransform",tmpCurFile + "_RegAtlas.tfm",
"--registration", "None",
"--initialization", "None",
"--rigidMaxIterations", "0"] )
run( ["RegisterImages", tmpDir+"/AtlasB.mha",
tmpDir+"/base-1.png",
"--resampledImage", tmpDir+"/RegAtlas-1.mha",
"--loadTransform",tmpCurFile + "_RegAtlas.tfm",
"--registration", "None",
"--initialization", "None",
"--rigidMaxIterations", "0"] )
run( ["RegisterImages", tmpDir+"/AtlasB.mha",
tmpDir+"/base-2.png",
"--resampledImage", tmpDir+"/RegAtlas-2.mha",
"--loadTransform",tmpCurFile + "_RegAtlas.tfm",
"--registration", "None",
"--initialization", "None",
"--rigidMaxIterations", "0"] )
run( ["ImageMath", tmpDir+"/RegAtlas-0.mha",
"-W", "0", tmpDir+"/RegAtlas-0.png"] )
run( ["ImageMath", tmpDir+"/RegAtlas-1.mha",
"-W", "0", tmpDir+"/RegAtlas-1.png"] )
run( ["ImageMath", tmpDir+"/RegAtlas-2.mha",
"-W", "0", tmpDir+"/RegAtlas-2.png"] )
run( [convert, "-combine",
tmpDir+"/RegAtlas-0.png",
tmpDir+"/RegAtlas-1.png",
tmpDir+"/RegAtlas-2.png",
"-colorspace", "sRGB",
"-type", "truecolor",
outCurFile + "_RegAtlas.png"] )


if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Apply timeLapseRegistrator to files in a folder.' )
parser.add_argument( 'inputFolder', help='Folder to apply the script to.' )
parser.add_argument( 'outputFolder', help='Folder to save the results to.' )
parser.add_argument( '--glob',
help='Glob to find files recursively in the given folder.',
default='*.???' )
parser.add_argument( '--convert',
help='Path to the convert executable from ImageMagick.',
default='convert' )
parser.add_argument( '--maskFile', help='File for masking ROI.',
default='mask.png' )
parser.add_argument( '--tmpDir', help='Folder for Temp files.',
default='tmp' )
args = parser.parse_args()
ApplyTimeLapseRegistrator( args.inputFolder, args.outputFolder,
args.glob, args.maskFile, args.tmpDir, args.convert )