lib/export/flame_stabilizer.rb in tracksperanto-1.8.1 vs lib/export/flame_stabilizer.rb in tracksperanto-1.8.2

- old
+ new

@@ -1,8 +1,11 @@ +# TODO: this exporter is MAJORLY slow now class Tracksperanto::Export::FlameStabilizer < Tracksperanto::Export::Base COLOR = "50 50 50" + DATETIME_FORMAT = '%a %b %d %H:%M:%S %Y' + def self.desc_and_extension "flame.stabilizer" end def self.human_name @@ -10,26 +13,31 @@ end def start_export( img_width, img_height) @counter = 0 @width, @height = img_width, img_height - @temp = Tempfile.new("flamexp") + @temp = Tracksperanto::BufferIO.new @writer = Tracksperanto::FlameBuilder.new(@temp) end def end_export + # Now make another writer, this time for our main IO @writer = Tracksperanto::FlameBuilder.new(@io) - write_header + # Now we know how many trackers we have so we can write the header + # data along with NbTrackers + write_header_with_number_of_trackers(@counter) + # Now write everything that we accumulated earlier into the base IO @temp.rewind @io.write(@temp.read) until @temp.eof? @temp.close! + # Send the ChannelEnd command and list the trackers @writer.channel_end @counter.times do |i| - @writer.tracker(i) do |t| + @writer.write_unterminated_block!("tracker", i) do |t| t.active true t.color_hash!("colour", 0, 100, 0) t.fixed_ref true t.fixed_x false t.fixed_y false @@ -38,43 +46,52 @@ end end def start_tracker_segment(tracker_name) @counter += 1 - @tracker_name = tracker_name - @x_values, @y_values = [], [] @write_first_frame = true end def export_point(frame, abs_float_x, abs_float_y, float_residual) + flame_frame = frame + 1 if @write_first_frame - write_first_frame(abs_float_x, abs_float_y, frame) - @x_values << [frame, 0] - @y_values << [frame, 0] + @base_x, @base_y = abs_float_x, abs_float_y + write_first_frame(abs_float_x, abs_float_y) + # For Flame to recognize the reference frame of the Shift channel + # we need it to contain zero as an int, not as a float. The shift proceeds + # from there. + @x_shift_values = [[flame_frame, 0]] + @y_shift_values = [[flame_frame, 0]] + @write_first_frame = false else - @x_values << [frame, (@base_x - abs_float_x)] - @y_values << [frame, (@base_y - abs_float_y)] + # Just continue buffering the upcoming shift keyframes and flush them in the end + shift_x, shift_y = @base_x - abs_float_x, @base_y - abs_float_y + @x_shift_values.push([flame_frame, shift_x]) + @y_shift_values.push([flame_frame, shift_y]) end end def end_tracker_segment - write_shift_channel("shift/x", @x_values, @base_x) - write_shift_channel("shift/y", @y_values, @base_y) + # We write these at tracker end since we need to know in advance + # how many keyframes they should contain + write_shift_channel("shift/x", @x_shift_values) + write_shift_channel("shift/y", @y_shift_values) end private - def write_shift_channel(name_without_prefix, values, base) + # The shift channel is what determines how the tracking point moves. + def write_shift_channel(name_without_prefix, values) @writer.channel(prefix(name_without_prefix)) do | c | c.extrapolation :constant c.value values[0][1] c.key_version 1 c.size values.length values.each_with_index do | (f, v), i | c.key(i) do | k | - k.frame(f + 1) - k.value(v) + k.frame f + k.value v k.interpolation :linear k.left_slope 2.4 k.right_slope 2.4 end end @@ -83,16 +100,16 @@ def prefix(tracker_channel) ["tracker#{@counter}", tracker_channel].join("/") end - def write_header + def write_header_with_number_of_trackers(number_of_trackers) @writer.stabilizer_file_version "5.0" - @writer.creation_date(Time.now.strftime('%a %b %d %H:%M:%S %Y')) + @writer.creation_date(Time.now.strftime(DATETIME_FORMAT)) @writer.linebreak!(2) - @writer.nb_trackers(@counter) + @writer.nb_trackers number_of_trackers @writer.selected 0 @writer.frame_width @width @writer.frame_height @height @writer.auto_key true @writer.motion_path true @@ -107,40 +124,61 @@ @writer.field false @writer.backward false @writer.anim end - def write_first_frame(x, y, f) - @write_first_frame = false - @base_x, @base_y = x, y + def write_first_frame(x, y) + write_track_channels + write_track_width_and_height + write_ref_width_and_height + write_ref_channels(x, y) + write_deltax_and_deltay_channels + write_offset_channels + end + + def write_track_channels + ctr_x, ctr_y = @width / 2, @height / 2 - tx, ty = @width / 2, @height / 2 - %w( track/x track/y).map(&method(:prefix)).zip([tx, ty]).each do | cname, default | + # track determines where the tracking box is, and should be in the center + # of the image for Flame to compute all other shifts properly + %w( track/x track/y).map(&method(:prefix)).zip([ctr_x, ctr_y]).each do | cname, default | @writer.channel(cname) do | c | c.extrapolation("constant") c.value(default.to_i) c.colour(COLOR) end end - + end + + # The size of the tracking area + def write_track_width_and_height %w( track/width track/height ).map(&method(:prefix)).each do | channel_name | @writer.channel(channel_name) do | c | c.extrapolation :linear c.value 15 c.colour COLOR end end - + end + + # The size of the reference area + def write_ref_width_and_height %w( ref/width ref/height).map(&method(:prefix)).each do | channel_name | @writer.channel(channel_name) do | c | c.extrapolation :linear c.value 10 c.colour COLOR end end - - %w( ref/x ref/y).map(&method(:prefix)).zip([x, y]).each do | cname, default | + end + + # The Ref channel contains the reference for the shift channel in absolute + # coordinates, and is set as float. Since we do not "snap" the tracker in + # the process it's enough for us to make one keyframe in the ref channels + # at the same frame as the first shift keyframe + def write_ref_channels(ref_x, ref_y) + %w( ref/x ref/y).map(&method(:prefix)).zip([ref_x, ref_y]).each do | cname, default | @writer.channel(cname) do | c | c.extrapolation("constant") c.value(default) c.colour(COLOR) c.key_version 1 @@ -152,11 +190,15 @@ k.left_slope 2.4 k.right_slope 2.4 end end end - + end + + def write_deltax_and_deltay_channels + # This is used for deltax and deltay (offset tracking). + # We set it to zero and lock %w( ref/dx ref/dy).map(&method(:prefix)).each do | chan, v | @writer.channel(chan) do | c | c.extrapolation("constant") c.value 0 c.colour(COLOR) @@ -175,16 +217,18 @@ k.value_lock true k.delete_lock true k.interpolation :constant end end # Chan block - - %w(offset/x offset/y).map(&method(:prefix)).each do | c | - @writer.channel(c) do | chan | - chan.extrapolation :constant - chan.value 0 - end + end + end + + def write_offset_channels + %w(offset/x offset/y).map(&method(:prefix)).each do | c | + @writer.channel(c) do | chan | + chan.extrapolation :constant + chan.value 0 end - end #Iter - end # Meth + end + end end