Skip to content

Instantly share code, notes, and snippets.

@spraints
Last active April 13, 2017 00:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save spraints/cd978d7cfcaa160a0d7dc309fb9bc182 to your computer and use it in GitHub Desktop.
Save spraints/cd978d7cfcaa160a0d7dc309fb9bc182 to your computer and use it in GitHub Desktop.
Stitch a folder of images into a movie
#!/bin/bash
root=$(cd $(dirname "$0"); pwd)
cd $root
exec ruby process.rb "$@"
require "shellwords"
require "tmpdir"
FRAMES_PER_SECOND = 6
def main
log_all_output!
puts "#{Time.now} -- Start"
dirin = getdir("in")
dirdone = getdir("done")
dirout = getdir("out")
mm = MovieMaker.new dirin, dirdone, dirout
Dir.entries(dirin).each do |entry|
next if entry == "." || entry == ".."
mm.make_movie(entry)
end
ensure
puts "#{Time.now} -- Done"
end
class MovieMaker
def initialize(from, done, to)
@from = from
@done = done
@to = to
end
attr_reader :from, :to, :done
def make_movie(name)
image_dir = File.join(from, name)
return skipnondir(name) unless File.directory?(image_dir)
done_dir = File.join(done, name)
return doneexists(done_dir) if File.exist?(done_dir)
File.rename(image_dir, done_dir)
image_dir = done_dir
images = Dir["#{image_dir}/*"].select { |p| File.file?(p) }.sort
return noimages(image_dir) if images.empty?
movie = File.join(to, "#{Time.now.strftime("%Y-%m-%d-%H-%M-%S")}-#{name}.mp4")
Dir.mktmpdir do |numbered_dir|
prep_images images, numbered_dir
convert_movie numbered_dir, movie
end
end
def skipnondir(name)
puts "skipping #{name}: not a directory"
end
def doneexists(dir)
puts "#{dir} exists, not reprocessing"
end
def noimages(dir)
puts "skipping #{dir}: empty"
end
FORMAT = "%09d.jpg".freeze
HR = ("-"*40).freeze
def prep_images(image_paths, numbered_dir)
image_paths.each_with_index do |file, i|
File.link file, File.join(numbered_dir, FORMAT % i)
end
end
def convert_movie(numbered_dir, outpath)
# quicktime can't seem to handle the gigantic images from the dslr.
# so shrink them to 1024 pixels wide, maintain the current aspect ratio,
# but also ensure that the height is divisible by two. ?!?!?!?!!
cmd = [ "/usr/local/bin/ffmpeg", "-hide_banner",
"-f", "image2",
"-r", FRAMES_PER_SECOND.to_s,
"-i", FORMAT,
"-vcodec", "libx264",
"-vf", "scale=1024:-2,format=yuv420p",
"-r", "30",
outpath ]
puts HR, "MAKE MOVIE: #{cmd.shelljoin} (in #{numbered_dir})"
system "ls", "-l", numbered_dir or return
system(*cmd, :chdir => numbered_dir) or return
puts HR
system "ffprobe", "-hide_banner", outpath
end
end
ROOT = File.expand_path(File.dirname(__FILE__)).freeze
def getdir(name)
path = File.join(ROOT, name)
Dir.mkdir(path) unless Dir.exist?(path)
path
end
def log_all_output!(path = File.join(getdir("log"), Time.now.strftime("%Y-%m-%d-%H-%M-%S-#$$.log")))
r, w = IO.pipe
tee_pid = spawn "tee", "-a", path, :in => r
r.close
$stdout.reopen(w)
$stderr.reopen(w)
end
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment