tracksperanto 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. data/Manifest.txt +37 -0
  2. data/README.txt +56 -0
  3. data/Rakefile +13 -0
  4. data/bin/tracksperanto +74 -0
  5. data/lib/export/base.rb +35 -0
  6. data/lib/export/flame_stabilizer.rb +50 -0
  7. data/lib/export/mux.rb +33 -0
  8. data/lib/export/pftrack.rb +30 -0
  9. data/lib/export/shake_text.rb +29 -0
  10. data/lib/export/syntheyes.rb +32 -0
  11. data/lib/import/base.rb +24 -0
  12. data/lib/import/flame_stabilizer.rb +34 -0
  13. data/lib/import/shake_script.rb +127 -0
  14. data/lib/import/shake_text.rb +29 -0
  15. data/lib/import/syntheyes.rb +32 -0
  16. data/lib/middleware/base.rb +28 -0
  17. data/lib/middleware/scaler.rb +24 -0
  18. data/lib/middleware/slipper.rb +12 -0
  19. data/lib/pipeline.rb +54 -0
  20. data/lib/tracksperanto.rb +131 -0
  21. data/test/.DS_Store +0 -0
  22. data/test/helper.rb +5 -0
  23. data/test/samples/.DS_Store +0 -0
  24. data/test/samples/flyover2DP_syntheyes.txt +12687 -0
  25. data/test/samples/megaTrack.action.3dtrack.stabilizer +29793 -0
  26. data/test/samples/one_shake_tracker.txt +49 -0
  27. data/test/samples/one_shake_tracker_from_first.txt +188 -0
  28. data/test/samples/shake_tracker_nodes.shk +921 -0
  29. data/test/samples/shake_tracker_nodes_to_syntheyes.txt +4091 -0
  30. data/test/samples/three_tracks_in_one_stabilizer.shk +320 -0
  31. data/test/samples/two_shake_trackers.txt +78 -0
  32. data/test/samples/two_tracks_in_one_tracker.shk +319 -0
  33. data/test/test_flame_block.rb +19 -0
  34. data/test/test_keyframe.rb +32 -0
  35. data/test/test_shake_export.rb +58 -0
  36. data/test/test_shake_script_import.rb +50 -0
  37. data/test/test_shake_text_import.rb +44 -0
  38. data/test/test_syntheyes_import.rb +25 -0
  39. metadata +111 -0
@@ -0,0 +1,37 @@
1
+ Manifest.txt
2
+ README.txt
3
+ Rakefile
4
+ bin/tracksperanto
5
+ lib/export/base.rb
6
+ lib/export/flame_stabilizer.rb
7
+ lib/export/mux.rb
8
+ lib/export/pftrack.rb
9
+ lib/export/shake_text.rb
10
+ lib/export/syntheyes.rb
11
+ lib/import/base.rb
12
+ lib/import/flame_stabilizer.rb
13
+ lib/import/shake_script.rb
14
+ lib/import/shake_text.rb
15
+ lib/import/syntheyes.rb
16
+ lib/middleware/base.rb
17
+ lib/middleware/scaler.rb
18
+ lib/middleware/slipper.rb
19
+ lib/pipeline.rb
20
+ lib/tracksperanto.rb
21
+ test/.DS_Store
22
+ test/helper.rb
23
+ test/samples/.DS_Store
24
+ test/samples/flyover2DP_syntheyes.txt
25
+ test/samples/megaTrack.action.3dtrack.stabilizer
26
+ test/samples/one_shake_tracker.txt
27
+ test/samples/one_shake_tracker_from_first.txt
28
+ test/samples/shake_tracker_nodes.shk
29
+ test/samples/shake_tracker_nodes_to_syntheyes.txt
30
+ test/samples/three_tracks_in_one_stabilizer.shk
31
+ test/samples/two_shake_trackers.txt
32
+ test/samples/two_tracks_in_one_tracker.shk
33
+ test/test_keyframe.rb
34
+ test/test_shake_export.rb
35
+ test/test_shake_script_import.rb
36
+ test/test_shake_text_import.rb
37
+ test/test_syntheyes_import.rb
@@ -0,0 +1,56 @@
1
+ = Tracksperanto
2
+
3
+ * http://guerilla-di.org/tracksperanto
4
+
5
+ == Description
6
+
7
+ Tracksperanto is a universal 2D-track translator between many apps.
8
+
9
+ Import support:
10
+ * Shake script (one tracker node per tracker)
11
+ * Shake tracker node export (textfile with many tracks per file), also exported by Boujou and others
12
+ * PFTrack 2dt files
13
+ * Syntheyes 2D tracking data exports (UV coordinates)
14
+
15
+ Export support:
16
+
17
+ * Shake text file (many trackers per file), also accepted by Boujou
18
+ * PFTrack 2dt file (with residuals)
19
+ * Syntheyes 2D tracking data import (UV coordinates)
20
+
21
+ The main way to use Tracksperanto is to use the supplied "tracksperanto" binary, like so:
22
+
23
+ tracksperanto -f ShakeScript -w 1920 -h 1080 /Films/Blockbuster/Shots/001/script.shk
24
+
25
+ ShakeScript is the name of the translator that will be used to read the file (many apps export tracks as .txt files so
26
+ there is no way for us to autodetect them all). -w and -h stand for Width and Height and define the size of your comp (different
27
+ tracking apps use different coordinate systems and we need to know the size of the comp to properly convert these). You also have
28
+ additional options like -xs, -ys and --slip - consult the usage info for the tracksperanto binary.
29
+
30
+ The converted files will be saved in the same directory as the source, if resulting converted files already exist ++they will be overwritten without warning++.
31
+
32
+ == Modularity
33
+
34
+ Tracksperanto supports many export and import formats. It also can help when you need to import and export the same format,
35
+ but you need some operation applied to the result (like scaling a proxy track up). Internally, Tracksperanto talks
36
+ Exporters, Importers and Middlewares. Any processing chain usually works like this:
37
+
38
+ 1) The tracker file is read and trackers and their keyframes are extracted, converting them to the internal representation.
39
+ For some formats you need to supply the width and height of the source material
40
+ 2) The trackers and their keyframes are dumped to all export formats Tracksperanto supports, optionally passing through middleware
41
+
42
+ == Internal coordinate system
43
+
44
+ Frame numbers start from zero (frame 0 is first frame of the track).
45
+
46
+ Tracksperanto uses the Shake coordinates as base. Image is Y-positive, X-positive, absolute pixel values up and right (zero is in the
47
+ lower left corner). Some apps use a different coordinate system so translation will take place on import or on export, respectively.
48
+
49
+ We also use residual and not correlation (residual is how far the tracker strolls away, correlation is how sure the tracker is
50
+ about what it's doing). Residual is the inverse of correlation (with total correlation of one the residual excursion becomes zero).
51
+
52
+ == Limitations
53
+
54
+ Information about the search area, reference area and offset is not passed along (outside of scope for the app and different trackers handle
55
+ these differently, if at all). For some modules no residual will be passed along (3D tracking apps generally do not export residual with
56
+ backprojected 3D features).
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+ require './lib/tracksperanto'
4
+
5
+
6
+ # Disable spurious warnings when running tests, ActiveMagic cannot stand -w
7
+ Hoe::RUBY_FLAGS.replace ENV['RUBY_FLAGS'] || "-I#{%w(lib test).join(File::PATH_SEPARATOR)}" +
8
+ (Hoe::RUBY_DEBUG ? " #{RUBY_DEBUG}" : '')
9
+
10
+ Hoe.new('tracksperanto', Tracksperanto::VERSION) do |p|
11
+ p.rubyforge_name = 'guerilla-di'
12
+ p.developer('Julik Tarkhanov', 'me@julik.nl')
13
+ end
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ # == Synopsis
3
+ #
4
+ # Translate a 2D track file from a single format to many others
5
+ #
6
+ # == Usage
7
+ #
8
+ # tracksperanto -f shakescript /Films/Blockbuster/Shots/001/script.shk -w 1920 -h 1080
9
+ #
10
+ # == Author
11
+ # Julik <me@julik.nl>
12
+
13
+ require File.dirname(__FILE__) + '/../lib/tracksperanto'
14
+ require File.dirname(__FILE__) + '/../lib/pipeline'
15
+ require 'optparse'
16
+
17
+ # Sane defaults
18
+ reader_klass = Tracksperanto::Import::ShakeScript
19
+ width = 1920
20
+ height = 1080
21
+ scale_x = 1.0
22
+ scale_y = 1.0
23
+ slip = 0
24
+ golden_tracks = false
25
+
26
+ readers = Tracksperanto::Import.constants.map{|e| e.to_s }.reject{|e| e == 'Base'}
27
+ informative_readers = readers.reject{|e| e == 'ShakeScript'}
28
+
29
+ parser = OptionParser.new do | p |
30
+ p.banner = "Usage: tracksperanto -f ShakeScript -w 1920 -h 1080 /Films/Blockbuster/Shots/001/script.shk"
31
+ p.on(" -f", "--from TRANSLATOR", String, "Use the specific import translator (defaults to ShakeScript, but can be \
32
+ #{informative_readers.join(', ')})") do | f |
33
+ begin
34
+ reader_klass = Tracksperanto::Import.const_get(f)
35
+ rescue NameError => e
36
+ reader_list = readers.join("\n\t")
37
+ STDERR.puts "Unknown reader #{f.inspect}, available readers:\n\t#{readers}"
38
+ exit(-1)
39
+ end
40
+ end
41
+ p.on(" -w", "--width WIDTH_IN_PIXELS", Integer, "Absolute input comp width in pixels") { |w| width = w }
42
+ p.on(" -h", "--height HEIGHT_IN_PIXELS", Integer, "Absolute input comp height in pixels") {|w| height = w }
43
+ p.on(" -xs", "--xscale X_SCALING_FACTOR", Float, "Scale the result in X by this factor (1.0 is the default)") {|sx| scale_x = sx }
44
+ p.on(" -ys", "--yscale Y_SCALING_FACTOR", Float, "Scale the result in Y by this factor (1.0 is the default)") {|sy| scale_y = sy }
45
+ p.on(" -s", "--slip FRAMES", Integer, "Slip the result by this number of frames, positive is 'later'") {|sy| slip = sy }
46
+ p.on(" -g", "--golden", "Reset the residuals of all trackers to 0 (ignore correlation)") {|g_flag|
47
+ golden_tracks = g_flag
48
+ }
49
+ end
50
+
51
+ begin
52
+ parser.parse!
53
+ rescue OptionParser::MissingArgument => e
54
+ STDERR.puts "Unknown argument: #{e.message}"
55
+ puts parser
56
+ exit(-1)
57
+ end
58
+
59
+ input_file = ARGV.pop
60
+ if !input_file
61
+ STDERR.puts "No input file provided - should be the last argument"
62
+ puts parser
63
+ exit(-1)
64
+ end
65
+
66
+ pipe = Tracksperanto::Pipeline.new
67
+ pipe.run(input_file, width, height, reader_klass) do | scaler, slipper, golden |
68
+ slipper.slip = slip
69
+ scaler.x_factor = scale_x
70
+ scaler.y_factor = scale_y
71
+ golden.enabled = golden_tracks
72
+ end
73
+
74
+ puts "Converted #{pipe.converted_points} trackers"
@@ -0,0 +1,35 @@
1
+ # Base exporter
2
+ class Tracksperanto::Export::Base
3
+
4
+ def self.inherited(by)
5
+ Tracksperanto.exporters << by
6
+ super
7
+ end
8
+
9
+ # Should return the suffix and extension of this export file (like "_flame.stabilizer"). It's a class
10
+ # method because it gets requested before the exporter is instantiated
11
+ def self.desc_and_extension
12
+ "data.txt"
13
+ end
14
+
15
+ def initialize(write_to_io)
16
+ @io = write_to_io
17
+ end
18
+
19
+ # Called on export start
20
+ def start_export( img_width, img_height)
21
+ end
22
+
23
+ # Called on export end
24
+ def end_export
25
+ end
26
+
27
+ # Called on tracker start, one for each tracker. Start of the next tracker
28
+ # signifies that the previous tracker has passed by
29
+ def start_tracker_segment(tracker_name)
30
+ end
31
+
32
+ # Called for each tracker keyframe
33
+ def export_point(at_frame_i, abs_float_x, abs_float_y, float_residual)
34
+ end
35
+ end
@@ -0,0 +1,50 @@
1
+ class Tracksperanto::Export::FlameStabilizer < Tracksperanto::Export::Base
2
+ PREAMBLE = '' #__DATA__.read
3
+
4
+ # Should return the suffix and extension of this export file (like "_flame.stabilizer")
5
+ def self.desc_and_extension
6
+ "flame.stabilizer"
7
+ end
8
+
9
+ def start_export(w, h)
10
+ @width = w, @height = h
11
+ end
12
+
13
+ def start_tracker_segment(tracker_name)
14
+ @tracker_count ||= 0
15
+ @tracker_count += 1
16
+ end
17
+
18
+ def end_export
19
+ preamble = PREAMBLE % [ @tracker_count, @width, @height]
20
+ end
21
+
22
+ def export_point(frame, abs_float_x, abs_float_y, float_residual)
23
+ end
24
+ end
25
+
26
+ __END__
27
+ StabilizerFileVersion 5.0
28
+ CreationDate Tue Dec 9 21:02:02 2008
29
+
30
+
31
+ NbTrackers %d
32
+ Selected 0
33
+ FrameWidth %d
34
+ FrameHeight %d
35
+ AutoKey yes
36
+ MotionPath yes
37
+ Icons yes
38
+ AutoPan no
39
+ EditMode 1
40
+ Format 0
41
+ Padding
42
+ Red 0
43
+ Green 0
44
+ Blue 0
45
+ Oversampling no
46
+ Opacity 50
47
+ Zoom 3
48
+ Field no
49
+ Backward no
50
+ Anim
@@ -0,0 +1,33 @@
1
+ # Multiplexor. Does not inherit Base so that it does not get used as a translator
2
+ class Tracksperanto::Export::Mux
3
+ attr_accessor :outputs
4
+
5
+ def initialize(*outputs)
6
+ @outputs = outputs.flatten
7
+ end
8
+
9
+ # Called on export start
10
+ def start_export( img_width, img_height)
11
+ @outputs.each do | output |
12
+ output.start_export( img_width, img_height)
13
+ end
14
+ end
15
+
16
+ # Called on tracker start, one for each tracker
17
+ def start_tracker_segment(tracker_name)
18
+ @outputs.each do | output |
19
+ output.start_tracker_segment(tracker_name)
20
+ end
21
+ end
22
+
23
+ # Called for each tracker keyframe
24
+ def export_point(at_frame_i, abs_float_x, abs_float_y, float_residual)
25
+ @outputs.each do | output |
26
+ output.export_point(at_frame_i, abs_float_x, abs_float_y, float_residual)
27
+ end
28
+ end
29
+
30
+ def end_export
31
+ @outputs.each{|o| o.end_export }
32
+ end
33
+ end
@@ -0,0 +1,30 @@
1
+ class Tracksperanto::Export::Pftrack < Tracksperanto::Export::Base
2
+
3
+ # Should return the suffix and extension of this export file (like "_flame.stabilizer")
4
+ def self.desc_and_extension
5
+ "pftrack.2dt"
6
+ end
7
+
8
+ def start_tracker_segment(tracker_name)
9
+ # If there was a previous tracker, write it out
10
+ # - now we know how many keyframes it has
11
+ if @prev_tracker && @prev_tracker.any?
12
+ block = [
13
+ "\n",
14
+ @tracker_name.inspect, # "autoquotes"
15
+ @prev_tracker.length,
16
+ @prev_tracker.join("\n")
17
+ ]
18
+ @io.puts block.join("\n")
19
+ end
20
+
21
+ # Setup for the next tracker
22
+ @prev_tracker = []
23
+ @tracker_name = tracker_name
24
+ end
25
+
26
+ def export_point(frame, abs_float_x, abs_float_y, float_residual)
27
+ line = "%s %.3f %.3f %.3f" % [frame, abs_float_x, abs_float_y, float_residual]
28
+ @prev_tracker << line
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ class Tracksperanto::Export::ShakeText < Tracksperanto::Export::Base
2
+ PREAMBLE = "TrackName %s\n Frame X Y Correlation\n"
3
+ POSTAMBLE = "\n"
4
+
5
+ # Should return the suffix and extension of this export file (like "_flame.stabilizer")
6
+ def self.desc_and_extension
7
+ "shake_trackers.txt"
8
+ end
9
+
10
+ def start_tracker_segment(tracker_name)
11
+ if @any_tracks
12
+ @io.puts POSTAMBLE
13
+ else
14
+ @any_tracks = true
15
+ end
16
+
17
+ @io.puts PREAMBLE % tracker_name
18
+ end
19
+
20
+ def end_export
21
+ @io << "\n"
22
+ end
23
+
24
+ def export_point(frame, abs_float_x, abs_float_y, float_residual)
25
+ # Shake starts from frame 1, not 0
26
+ line = " %.2f %.3f %.3f %.3f" % [frame + 1, abs_float_x, abs_float_y, 1 - float_residual]
27
+ @io.puts line
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ class Tracksperanto::Export::SynthEyes < Tracksperanto::Export::Base
2
+
3
+ # Should return the suffix and extension of this export file (like "_flame.stabilizer")
4
+ def self.desc_and_extension
5
+ "syntheyes_2dt.txt"
6
+ end
7
+
8
+ def start_export( img_width, img_height)
9
+ @width, @height = img_width, img_height
10
+ end
11
+
12
+ def start_tracker_segment(tracker_name)
13
+ @tracker_name = tracker_name
14
+ end
15
+
16
+ def export_point(frame, abs_float_x, abs_float_y, float_residual)
17
+ values = [@tracker_name, frame] + syntheyes_coords(abs_float_x, abs_float_y)
18
+ @io.puts("%s %d %.6f %.6f 30" % values)
19
+ end
20
+
21
+ private
22
+
23
+ # Syntheyes wants very special coordinates, Y down X right,
24
+ # 0 is center and values are UV float -1 to 1, doubled
25
+ def syntheyes_coords(abs_x, abs_y)
26
+ w, h = 2560, 1080
27
+ x = (abs_x / @width.to_f) - 0.5
28
+ y = (abs_y / @height.to_f) - 0.5
29
+ # .2 to -.3, y is reversed and coords are double
30
+ [x * 2, y * -2]
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ require 'stringio'
2
+
3
+ class Tracksperanto::Import::Base
4
+ include Tracksperanto::Safety
5
+
6
+ # The original width of the tracked image
7
+ # Some importers need it
8
+ attr_accessor :width
9
+
10
+ # The original height of the original image.
11
+ # Some importers need it
12
+ attr_accessor :height
13
+ safe_reader :width, :height
14
+
15
+ def self.inherited(by)
16
+ Tracksperanto.importers << by
17
+ super
18
+ end
19
+
20
+ # Should return an array of Tracksperanto::Tracker objects
21
+ def parse(track_file_content)
22
+ []
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ require 'stringio'
2
+
3
+ class Tracksperanto::Import::FlameStabilizer < Tracksperanto::Import::Base
4
+ # Flame records channels in it's .stabilizer file. Per tracker, we use tracker1/track/x and tracker1/track/y as the base
5
+ # tracking value. Then, the tracker1/shift/x and tracker1/shift/y are added to it. The problem is that when we track backwards
6
+ # the main reference keyframe for the x and y coordinates that sets the base for the animation might be at the last frame, at
7
+ # a frame in the middle or in the end of the setup. We have to pluck it out and then we compute the shift relative to the values
8
+ # in this main frame. Obviously we discard the track width and search width information as we cannot use it in any meaningful way.
9
+ #
10
+ # Here is how the relevant portions look like this (note the tab indents, not spaces, indentation starts at hashmark)
11
+ #Channel tracker1/shift/x
12
+ # Extrapolation linear
13
+ # Value 0
14
+ # Size 185
15
+ # KeyVersion 1
16
+ # Key 0
17
+ # Frame 1
18
+ # Value 0
19
+ # Interpolation linear
20
+ # RightSlope 0.15802
21
+ # LeftSlope 0.15802
22
+ # End
23
+ # Key 1
24
+ #
25
+ # This is in big lines:
26
+ # (tabs)Key(space)Value
27
+ def parse(stabilizer_setup_content)
28
+ trackers = []
29
+
30
+ io = StringIO.new(stabilizer_setup_content)
31
+
32
+ trackers
33
+ end
34
+ end