lib/import/shake_script.rb in tracksperanto-1.1.1 vs lib/import/shake_script.rb in tracksperanto-1.2.0

- old
+ new

@@ -1,129 +1,215 @@ -require 'strscan' +require File.dirname(__FILE__) + "/shake_grammar/lexer" +require File.dirname(__FILE__) + "/shake_grammar/catcher" class Tracksperanto::Import::ShakeScript < Tracksperanto::Import::Base - # Allow literal strings instead of Regexps - class SloppyParser < StringScanner - def skip_until(pattern) - pattern.is_a?(String) ? super(/#{Regexp.escape(pattern)}/m) : super(pattern) - end - - def scan_until(pattern) - pattern.is_a?(String) ? super(/#{Regexp.escape(pattern)}/m) : super(pattern) - end + def self.human_name + "Shake .shk script file" end - - class ValueAt - attr_accessor :value, :frame - def self.from_string(str) - v = new - val_s, frame_s = str.scan(/(.+)@(\d+)/).to_a.flatten - v.value, v.frame = val_s.to_f, frame_s.to_i - v - end + + def self.distinct_file_ext + ".shk" end - - class CurveParser < SloppyParser - BLOCK_ENDS = /\d,|\)/ + + class Traxtractor < Tracksperanto::ShakeGrammar::Catcher + include Tracksperanto::ZipTuples - attr_reader :values - def initialize(with_curve_arg) - @values = [] - - super(with_curve_arg.to_s) - - # Skip the interpolation name, we assume it to be linear anyway - skip_until '(' - - # Skip the first defining parameter whatever that might be - skip_until ',' - - loop do - break unless (value_at = scan_until(BLOCK_ENDS)) - # Grab the value - val = ValueAt.from_string(value_at) - - @values << val + class << self + attr_accessor :accumulator + attr_accessor :progress_block + end + + self.accumulator = [] + + # For Linear() curve calls. If someone selected JSpline or Hermite it's his problem. + # We put the frame number at the beginning since it works witih oru tuple zipper + def linear(first_arg, *keyframes) + report_progress("Translating Linear animation") + keyframes.map do | kf | + [kf[1][1], kf[0][1]] end end - - end - - class TrackerParser < SloppyParser - attr_reader :x_curve, :y_curve, :c_curve, :name - def initialize(with_tracker_args) - super(with_tracker_args) - - # Name me! - @name = scan_until(/(\w+) /).strip + # image Tracker( + # image In, + # const char * trackRange, + # const char * subPixelRes, + # const char * matchSpace, + # float referenceTolerance, + # const char * referenceBehavior, + # float failureTolerance, + # const char * failureBehavior, + # int limitProcessing, + # float referencFrame + # ... + # ); + def tracker(input, trackRange, subPixelRes, matchSpace, + referenceTolerance, referenceBehavior, failureTolerance, failureBehavior, limitProcessing, referencFrame, s1, s2, + s3, s4, s5, s6, *trackers) - # All the tracker arguments - 17.times { skip_until ',' } # input data + report_progress("Parsing Tracker node") + collect_trackers_from(get_variable_name, trackers) + true + end + + # stabilize { + # image In, + # int applyTransform, + # int inverseTransform + # const char * trackType, + # float track1X, + # float track1Y, + # int stabilizeX, + # int stabilizeY, + # float track2X, + # float track2Y, + # int matchScale, + # int matchRotation, + # float track3X, + # float track3Y, + # float track4X, + # float track4Y, + # const char * xFilter, + # const char * yFilter, + # const char * transformationOrder, + # float motionBlur, + # float shutterTiming, + # float shutterOffset, + # float referenceFrame, + # float aspectRatio, + # ... + # }; + def stabilize(imageIn, applyTransform, inverseTransform, trackType, + track1X, track1Y, + stabilizeX, stabilizeY, + track2X, track2Y, + matchScale, + matchRotation, + track3X, track3Y, + track4X, track4Y, + *useless_args) - # Grab the curves - @x_curve, @y_curve, @c_curve = (0..2).map{ CurveParser.new(scan_until('),')).values } + report_progress("Parsing Stabilize node") + node_name = get_variable_name + collect_stabilizer_tracker("#{node_name}_track1", track1X, track1Y) + collect_stabilizer_tracker("#{node_name}_track2", track2X, track2Y) + collect_stabilizer_tracker("#{node_name}_track3", track3X, track3Y) + collect_stabilizer_tracker("#{node_name}_track4", track4X, track4Y) + end + + # image = MatchMove( + # Background, + # Foreground, + # applyTransform, + # "trackType", + # track1X, + # track1Y, + # matchX, + # matchY, + # track2X, + # track2Y, + # scale, + # rotation, + # track3X, + # track3Y, + # track4X, + # track4Y, + # x1, + # y1, + # x2, + # y2, + # x3, + # y3, + # x4, + # y4, + # "xFilter", + # "yFilter", + # motionBlur, + # shutterTiming, + # shutterOffset, + # referenceFrame, + # "compositeType", + # clipMode, + # "trackRange", + # "subPixelRes", + # "matchSpace", + # float referenceTolerance, + # "referenceBehavior", + # float failureTolerance, + # "failureBehavior", + # int limitProcessing, + # ... + # ); + def matchmove(bgImage, fgImage, applyTransform, + trackType, + track1X, + track1Y, + matchX, + matchY, + track2X, + track2Y, + scale, + rotation, + track3X, + track3Y, + track4X, + track4Y, *others) - # if the next argument is an integer, we reached the end of the tracks. If not - make a nested one. + report_progress("Parsing MatchMove node") + node_name = get_variable_name + collect_stabilizer_tracker("#{node_name}_track1", track1X, track1Y) + collect_stabilizer_tracker("#{node_name}_track2", track2X, track2Y) + collect_stabilizer_tracker("#{node_name}_track3", track3X, track3Y) + collect_stabilizer_tracker("#{node_name}_track4", track4X, track4Y) + end - def curves - [@x_curve, @y_curve, @c_curve] + private + + def report_progress(with_message) + self.class.progress_block.call(with_message) if self.class.progress_block end - end - - class TrackParser < SloppyParser - def initialize(with_tracker_block) - # scan until the first " - name of the track - skip_until ',' - # scan values - # discard the 8 box determinators - 7.times { skip_until ',' } - # discard the box animation - 2.times { skip_until ',' } + + def collect_trackers_from(name, array) + parameters_per_node = 16 + nb_trackers = array.length / parameters_per_node + nb_trackers.times do | idx | + from_index, to_index = (idx * parameters_per_node), (idx+1) * parameters_per_node + tracker_args = array[from_index...to_index] + tracker_args[0] = "#{name}_#{tracker_args[0]}" + collect_tracker(*tracker_args) + end end - end - - TRACKER_PATTERN = /((\w+) = Tracker\(([^;]+))/m - - def parse(sript_file_content) - trackers = [] - - sript_file_content.scan(TRACKER_PATTERN).each_with_index do | tracker_text_block, idx | + def collect_stabilizer_tracker(name, x_curve, y_curve) + return if (x_curve == :unknown || y_curve == :unknown) - parser = TrackerParser.new(tracker_text_block.to_s) + keyframes = zip_curve_tuples(x_curve, y_curve).map do | (frame, x, y) | + Tracksperanto::Keyframe.new(:frame => frame - 1, :abs_x => x, :abs_y => y) + end - tracker = Tracksperanto::Tracker.new{|t| t.name = parser.name } - - report_progress("Reading tracker #{tracker.name}") - - x_keyframes, y_keyframes, residual_keyframes = TrackerParser.new(tracker_text_block.to_s).curves - x_keyframes.each_with_index do | value_at, kf_index | - - # Find the Y keyframe with the same frame - matching_y = y_keyframes.find{|f| f.frame == value_at.frame } - - # Find the correlation keyframe with the same frame - matching_residual = residual_keyframes.find{|f| f.frame == value_at.frame } - - # Skip frame if only one keyframe is present - if !matching_y - STDERR.puts "Cannot find matching Y for frame #{value_at.frame} in tracker #{parser.name}, skipping keyframe" - next - end - - tracker.keyframes << Tracksperanto::Keyframe.new do |k| - k.frame = (value_at.frame - 1) - k.abs_x = value_at.value - k.abs_y = matching_y.value - k.residual = 1 - (matching_residual.value rescue 1.0) - end + t = Tracksperanto::Tracker.new(:name => name, :keyframes => keyframes ) + self.class.accumulator.push(t) + end + + def collect_tracker(name, x_curve, y_curve, corr_curve, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12) + report_progress("Scavenging tracker #{name}") + keyframes = zip_curve_tuples(x_curve, y_curve, corr_curve).map do | (frame, x, y, corr) | + Tracksperanto::Keyframe.new(:frame => frame - 1, :abs_x => x, :abs_y => y, :residual => (1 - corr)) end - trackers << tracker + t = Tracksperanto::Tracker.new(:name => name, :keyframes => keyframes ) + self.class.accumulator.push(t) end + end + + def parse(script_io) + trackers = [] + + Traxtractor.accumulator = trackers + Traxtractor.progress_block = lambda{|msg| report_progress(msg) } + Traxtractor.new(script_io) trackers end end \ No newline at end of file