tracksperanto 1.8.4 → 1.9.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.
- data/History.txt +6 -0
- data/README.txt +30 -12
- data/Rakefile +1 -0
- data/bin/tracksperanto +51 -74
- data/lib/export/flame_stabilizer.rb +19 -11
- data/lib/export/shake_text.rb +2 -1
- data/lib/import/flame_stabilizer.rb +2 -5
- data/lib/import/shake_text.rb +1 -2
- data/lib/pipeline/base.rb +52 -58
- data/lib/tracksperanto.rb +11 -2
- data/lib/tracksperanto/buffer_io.rb +4 -5
- data/test/import/test_boujou_import.rb +1 -3
- data/test/import/test_pftrack_import.rb +1 -1
- data/test/test_tracksperanto.rb +5 -0
- metadata +12 -2
data/History.txt
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
=== 1.9.0 / 2010-03-21
|
|
2
|
+
|
|
3
|
+
* Use proper progress bars
|
|
4
|
+
* Allow proper chaining of middlewares in the commandline app - you can now apply many middlewares of the same nature in chainn, and they will be ordered correctly
|
|
5
|
+
* Small rewrites to Pipeline::Base (changes API slightly - please consult the docs)
|
|
6
|
+
|
|
1
7
|
=== 1.8.4 / 2010-03-10
|
|
2
8
|
|
|
3
9
|
* Fix PFTrack imports neglecting the feature name if it only contained digits
|
data/README.txt
CHANGED
|
@@ -88,24 +88,42 @@ Middlewares. Any processing chain (called a Pipeline) usually works like this:
|
|
|
88
88
|
* Trackers and their keyframes are dumped to all export
|
|
89
89
|
formats Tracksperanto supports, optionally passing through Middleware
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
|
|
92
|
+
== Limitations
|
|
93
|
+
|
|
94
|
+
Information about the search area, reference area and offset is not passed along (outside
|
|
95
|
+
of scope for the app and different trackers handle these differently, if at all). For some
|
|
96
|
+
modules no residual will be passed along (3D tracking apps generally do not export residual
|
|
97
|
+
with backprojected 3D features).
|
|
98
|
+
|
|
99
|
+
== Extending Tracksperanto
|
|
100
|
+
|
|
101
|
+
=== Importing your own formats
|
|
92
102
|
|
|
93
103
|
You can easily write a Tracksperanto import module - refer to Tracksperanto::Import::Base
|
|
94
|
-
docs. Your importer
|
|
104
|
+
docs. Your importer will be configured with width and height of the comp that it is importing, and will get an IO
|
|
105
|
+
object with the file that you are processing. The parse method should then return an array of Tracksperanto::Tracker objects which
|
|
95
106
|
are themselves arrays of Tracksperanto::Keyframe objects.
|
|
96
107
|
|
|
97
|
-
|
|
108
|
+
=== Exporting your own formats
|
|
98
109
|
|
|
99
|
-
You can easily write an exporter. Refer to the Tracksperanto::Export::Base docs.
|
|
110
|
+
You can easily write an exporter. Refer to the Tracksperanto::Export::Base docs. Note that your exporter should be able to chew alot of data
|
|
111
|
+
(hundreds of trackers with thousands of keyframes with exported files growing up to 5-10 megs in size are not uncommon!). This means that
|
|
112
|
+
the exporter should work with streams (smaller parts of the file being exported will be held in memory at a time).
|
|
100
113
|
|
|
101
|
-
|
|
114
|
+
=== Ading your own processing steps
|
|
102
115
|
|
|
103
|
-
You probably want to write a Middleware (consult the Tracksperanto::Middleware::Base docs)
|
|
104
|
-
|
|
116
|
+
You probably want to write a Middleware (consult the Tracksperanto::Middleware::Base docs) if you need some processing applied to the tracks
|
|
117
|
+
or their data. A Middleware is just like an export module, except that instead it sits between the exporter and the exporting routine. Middlewares wrap export
|
|
118
|
+
modules or each other, so you can stack different middleware modules together (like "scale first, then move").
|
|
105
119
|
|
|
106
|
-
|
|
120
|
+
=== Writing your own processing routines
|
|
107
121
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
122
|
+
You probably want to write a descendant of Tracksperanto::Pipeline::Base. This is a class that manages a conversion from start to finish, including detecting the
|
|
123
|
+
input format, allocating output files and building a chain of Middlewares to process the export. If you want to make a GUI for Tracksperanto you will likely need
|
|
124
|
+
to write your own Pipeline class or reimplement parts of it.
|
|
125
|
+
|
|
126
|
+
=== Reporting status from long-running operations
|
|
127
|
+
|
|
128
|
+
Almost every module in Tracksperanto has a method called report_progress. This method is used to notify an external callback of what you are doing, and helps keep
|
|
129
|
+
the software user-friendly. A well-behaved Tracksperanto module should manage it's progress reports properly.
|
data/Rakefile
CHANGED
|
@@ -10,6 +10,7 @@ begin
|
|
|
10
10
|
Hoe.spec('tracksperanto') do | p |
|
|
11
11
|
p.clean_globs = %w( **/.DS_Store )
|
|
12
12
|
p.version = Tracksperanto::VERSION
|
|
13
|
+
p.extra_deps = {"progressbar" => ">=0"}
|
|
13
14
|
p.extra_dev_deps = {"flexmock" => ">=0"}
|
|
14
15
|
p.rubyforge_name = 'guerilla-di'
|
|
15
16
|
p.developer('Julik Tarkhanov', 'me@julik.nl')
|
data/bin/tracksperanto
CHANGED
|
@@ -12,23 +12,16 @@
|
|
|
12
12
|
|
|
13
13
|
require File.dirname(__FILE__) + '/../lib/tracksperanto'
|
|
14
14
|
require 'optparse'
|
|
15
|
+
require 'rubygems'
|
|
16
|
+
require 'progressbar'
|
|
15
17
|
|
|
16
18
|
# Sane defaults
|
|
17
19
|
reader_klass = nil
|
|
18
20
|
width = nil
|
|
19
21
|
height = nil
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
scale_x = 1.0
|
|
23
|
-
scale_y = 1.0
|
|
24
|
-
slip = 0
|
|
25
|
-
golden_tracks = false
|
|
26
|
-
x_shift = 0
|
|
27
|
-
y_shift = 0
|
|
28
|
-
length_gate = 0
|
|
29
|
-
set_prefix = ''
|
|
22
|
+
|
|
23
|
+
middlewares = []
|
|
30
24
|
sole_format = nil
|
|
31
|
-
lerp = false
|
|
32
25
|
reader_name = "ShakeScript"
|
|
33
26
|
readers = Tracksperanto.importer_names
|
|
34
27
|
writers = Tracksperanto.exporter_names
|
|
@@ -42,34 +35,52 @@ parser = OptionParser.new do | p |
|
|
|
42
35
|
"Use the specific import translator (will try to autodetect, but can be #{Tracksperanto.importer_names.join(', ')})") do | f |
|
|
43
36
|
reader_name = f
|
|
44
37
|
end
|
|
45
|
-
p.on(" -p", "--prefix PREFIX", String, "A prefix to prepend to tracker names in bulk") { |w| set_prefix = w }
|
|
46
38
|
p.on(" -w", "--width WIDTH_IN_PIXELS", Integer, "Absolute input comp width in pixels (will try to autodetect)") { |w| width = w }
|
|
47
39
|
p.on(" -h", "--height HEIGHT_IN_PIXELS", Integer, "Absolute input comp height in pixels (will try to autodetect)") {|w| height = w }
|
|
48
|
-
|
|
49
|
-
p.on(" -
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
40
|
+
|
|
41
|
+
p.on(" -xs", "--xscale X_SCALING_FACTOR", Float, "Scale the result in X by this factor (1.0 is the default)") do |sx|
|
|
42
|
+
middlewares << ["Scaler", {:x_factor => sx}]
|
|
43
|
+
end
|
|
44
|
+
p.on(" -ys", "--yscale Y_SCALING_FACTOR", Float, "Scale the result in Y by this factor (1.0 is the default)") do |sy|
|
|
45
|
+
middlewares << ["Scaler", {:y_factor => sx}]
|
|
46
|
+
end
|
|
47
|
+
p.on(" -s", "--slip FRAMES", Integer, "Slip the result by this number of frames, positive is 'later'") do |sy|
|
|
48
|
+
middlewares << ["Slipper", {:slip => sx}]
|
|
49
|
+
end
|
|
50
|
+
p.on(" -g", "--golden", "Reset the residuals of all trackers to 0 (ignore correlation)") do |g_flag|
|
|
51
|
+
middlewares << ["Golden", {:enabled => true}]
|
|
52
|
+
end
|
|
53
|
+
p.on(" -rx", "--reformat-x NEW_PIX_WIDTH", Integer, "Reformat the comp to this width and scale all tracks to it") do |rw|
|
|
54
|
+
middlewares << ["Reformat", {:width => rw}]
|
|
55
|
+
end
|
|
56
|
+
p.on(" -ry", "--reformat-y NEW_PIX_HEIGHT", Integer, "Reformat the comp to this height and scale all tracks to it") do |rh|
|
|
57
|
+
middlewares << ["Reformat", {:height => rh}]
|
|
58
|
+
end
|
|
59
|
+
p.on(" -m", "--min-length LENGTH_IN_FRAMES", Integer, "Only export trackers having more than X keyframes") do | min_kf |
|
|
60
|
+
middlewares << ["LengthCutoff", {:min_length => min_kf}]
|
|
61
|
+
end
|
|
62
|
+
p.on(" -xm", "--xshift X_IN_PIXELS", Float, "Move the points left or right") do |sx|
|
|
63
|
+
middlewares << ["Shift", {:x_shift => sx}]
|
|
64
|
+
end
|
|
65
|
+
p.on(" -ym", "--yshift Y_IN_PIXELS", Float, "Move the points up or down") do |sx|
|
|
66
|
+
middlewares << ["Shift", {:y_shift => sx}]
|
|
67
|
+
end
|
|
68
|
+
p.on(" -p", "--prefix PREFIX", String, "A prefix to prepend to tracker names in bulk") do |w|
|
|
69
|
+
middlewares << ["Prefix", {:prefix => w}]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
p.on("--lerp", "Linearly interpolate missing keyframes") do
|
|
73
|
+
middlewares << ["Lerp", {:enabled => true}]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
p.on(" -o", "--only EXPORTER_NAME", String, "Only export the selected format, format must be one of #{writers.join(", ")}") do |f|
|
|
61
77
|
sole_format = f
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
length_gate = f
|
|
65
|
-
}
|
|
66
|
-
p.on(" -xm", "--xshift X_IN_PIXELS", Float, "Move the points left or right") {|sx| x_shift = sx }
|
|
67
|
-
p.on(" -ym", "--yshift Y_IN_PIXELS", Float, "Move the points up or down") {|sx| y_shift = sx }
|
|
68
|
-
p.on("--lerp", "Linearly interpolate missing keyframes") {|v| lerp = true }
|
|
78
|
+
end
|
|
79
|
+
|
|
69
80
|
p.on("--version", "Show the version and exit") {|v|
|
|
70
|
-
puts "
|
|
81
|
+
puts "Tracksperanto v.#{Tracksperanto::VERSION} running on Ruby #{RUBY_VERSION} on #{RUBY_PLATFORM}"
|
|
71
82
|
puts "Copyright 2008-#{Time.now.year} by Guerilla-DI (Julik Tarkhanov and contributors)"
|
|
72
|
-
puts "For questions and support contact info
|
|
83
|
+
puts "For questions and support contact info#{64.chr}guerilla-di.org"
|
|
73
84
|
exit(0)
|
|
74
85
|
}
|
|
75
86
|
end
|
|
@@ -91,34 +102,12 @@ end
|
|
|
91
102
|
|
|
92
103
|
input_file = ARGV.pop
|
|
93
104
|
if !input_file
|
|
94
|
-
STDERR.puts "No input file provided - should be the last argument"
|
|
105
|
+
STDERR.puts "No input file provided - should be the last argument. Also use the --help option."
|
|
95
106
|
exit(-1)
|
|
96
107
|
end
|
|
97
108
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def initialize
|
|
102
|
-
@counter, @last_len = 0, 0
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def spin(percent, msg)
|
|
106
|
-
current = RING[(@counter += 1) % RING.length]
|
|
107
|
-
output = " [%s %d%%]" % [current, percent]
|
|
108
|
-
STDOUT.write(output + ("\r" * output.length))
|
|
109
|
-
STDOUT.flush
|
|
110
|
-
@last_len = output.length
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def finish(with_message = '')
|
|
114
|
-
STDOUT.write("\r" * @last_len.to_i)
|
|
115
|
-
STDOUT.flush
|
|
116
|
-
STDOUT.puts(with_message)
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
spinner = Spinner.new
|
|
121
|
-
pipe = Tracksperanto::Pipeline::Base.new(:progress_block => spinner.method(:spin).to_proc)
|
|
109
|
+
pbar = ProgressBar.new("Converting", 100)
|
|
110
|
+
pipe = Tracksperanto::Pipeline::Base.new(:progress_block => lambda{|p,m| pbar.set(p) }, :middleware_tuples => middlewares)
|
|
122
111
|
|
|
123
112
|
if sole_format
|
|
124
113
|
begin
|
|
@@ -129,18 +118,6 @@ if sole_format
|
|
|
129
118
|
end
|
|
130
119
|
end
|
|
131
120
|
|
|
132
|
-
pipe.run(input_file, :
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
scaler.y_factor = scale_y
|
|
136
|
-
golden.enabled = golden_tracks
|
|
137
|
-
reformat.width = reformat_w if reformat_w
|
|
138
|
-
reformat.height = reformat_h if reformat_h
|
|
139
|
-
shift.x_shift = x_shift
|
|
140
|
-
shift.y_shift = y_shift
|
|
141
|
-
prefix.prefix = set_prefix
|
|
142
|
-
lerpm.enabled = lerp
|
|
143
|
-
len.min_length = length_gate
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
spinner.finish("Converted #{pipe.converted_points} trackers with #{pipe.converted_keyframes} keys")
|
|
121
|
+
pipe.run(input_file, :width => width, :height => height, :parser => reader_klass)
|
|
122
|
+
pbar.finish
|
|
123
|
+
puts ("Converted #{pipe.converted_points} trackers with #{pipe.converted_keyframes} keys")
|
|
@@ -54,19 +54,10 @@ class Tracksperanto::Export::FlameStabilizer < Tracksperanto::Export::Base
|
|
|
54
54
|
def export_point(frame, abs_float_x, abs_float_y, float_residual)
|
|
55
55
|
flame_frame = frame + 1
|
|
56
56
|
if @write_first_frame
|
|
57
|
-
|
|
58
|
-
write_first_frame(abs_float_x, abs_float_y)
|
|
59
|
-
# For Flame to recognize the reference frame of the Shift channel
|
|
60
|
-
# we need it to contain zero as an int, not as a float. The shift proceeds
|
|
61
|
-
# from there.
|
|
62
|
-
@x_shift_values = [[flame_frame, 0]]
|
|
63
|
-
@y_shift_values = [[flame_frame, 0]]
|
|
57
|
+
export_first_point(flame_frame, abs_float_x, abs_float_y)
|
|
64
58
|
@write_first_frame = false
|
|
65
59
|
else
|
|
66
|
-
|
|
67
|
-
shift_x, shift_y = @base_x - abs_float_x, @base_y - abs_float_y
|
|
68
|
-
@x_shift_values.push([flame_frame, shift_x])
|
|
69
|
-
@y_shift_values.push([flame_frame, shift_y])
|
|
60
|
+
export_remaining_point(flame_frame, abs_float_x, abs_float_y)
|
|
70
61
|
end
|
|
71
62
|
end
|
|
72
63
|
|
|
@@ -79,6 +70,23 @@ class Tracksperanto::Export::FlameStabilizer < Tracksperanto::Export::Base
|
|
|
79
70
|
|
|
80
71
|
private
|
|
81
72
|
|
|
73
|
+
def export_remaining_point(flame_frame, abs_float_x, abs_float_y)
|
|
74
|
+
# Just continue buffering the upcoming shift keyframes and flush them in the end
|
|
75
|
+
shift_x, shift_y = @base_x - abs_float_x, @base_y - abs_float_y
|
|
76
|
+
@x_shift_values.push([flame_frame, shift_x])
|
|
77
|
+
@y_shift_values.push([flame_frame, shift_y])
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def export_first_point(flame_frame, abs_float_x, abs_float_y)
|
|
81
|
+
@base_x, @base_y = abs_float_x, abs_float_y
|
|
82
|
+
write_first_frame(abs_float_x, abs_float_y)
|
|
83
|
+
# For Flame to recognize the reference frame of the Shift channel
|
|
84
|
+
# we need it to contain zero as an int, not as a float. The shift proceeds
|
|
85
|
+
# from there.
|
|
86
|
+
@x_shift_values = [[flame_frame, 0]]
|
|
87
|
+
@y_shift_values = [[flame_frame, 0]]
|
|
88
|
+
end
|
|
89
|
+
|
|
82
90
|
# The shift channel is what determines how the tracking point moves.
|
|
83
91
|
def write_shift_channel(name_without_prefix, values)
|
|
84
92
|
@writer.channel(prefix(name_without_prefix)) do | c |
|
data/lib/export/shake_text.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
class Tracksperanto::Export::ShakeText < Tracksperanto::Export::Base
|
|
3
3
|
PREAMBLE = "TrackName %s\n Frame X Y Correlation\n"
|
|
4
4
|
POSTAMBLE = "\n"
|
|
5
|
+
TEMPLATE = " %.2f %.3f %.3f %.3f"
|
|
5
6
|
|
|
6
7
|
def self.desc_and_extension
|
|
7
8
|
"shake_trackers.txt"
|
|
@@ -21,7 +22,7 @@ class Tracksperanto::Export::ShakeText < Tracksperanto::Export::Base
|
|
|
21
22
|
|
|
22
23
|
def export_point(frame, abs_float_x, abs_float_y, float_residual)
|
|
23
24
|
# Shake starts from frame 1, not 0
|
|
24
|
-
line =
|
|
25
|
+
line = TEMPLATE % [frame + 1, abs_float_x, abs_float_y, 1 - float_residual]
|
|
25
26
|
@io.puts line
|
|
26
27
|
end
|
|
27
28
|
end
|
|
@@ -14,9 +14,6 @@ class Tracksperanto::Import::FlameStabilizer < Tracksperanto::Import::Base
|
|
|
14
14
|
"Flame .stabilizer file"
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
T = ::Tracksperanto::Tracker
|
|
18
|
-
K = ::Tracksperanto::Keyframe
|
|
19
|
-
|
|
20
17
|
class ChannelBlock < Array
|
|
21
18
|
include ::Tracksperanto::Casts
|
|
22
19
|
cast_to_string :name
|
|
@@ -175,7 +172,7 @@ Channel tracker1/ref/x
|
|
|
175
172
|
end
|
|
176
173
|
|
|
177
174
|
def grab_tracker(channels, track_x)
|
|
178
|
-
t =
|
|
175
|
+
t = Tracksperanto::Tracker.new(:name => track_x.name.split('/').shift)
|
|
179
176
|
|
|
180
177
|
report_progress("Extracting tracker #{t.name}")
|
|
181
178
|
|
|
@@ -200,7 +197,7 @@ Channel tracker1/ref/x
|
|
|
200
197
|
kf_x, kf_y = base_x - x.to_f, base_y - y.to_f
|
|
201
198
|
|
|
202
199
|
report_progress("Extracting keyframe #{total_kf += 1} of #{t.name}")
|
|
203
|
-
|
|
200
|
+
Tracksperanto::Keyframe.new(:frame => (at - 1), :abs_x => kf_x, :abs_y => kf_y)
|
|
204
201
|
end
|
|
205
202
|
|
|
206
203
|
return t
|
data/lib/import/shake_text.rb
CHANGED
|
@@ -7,8 +7,7 @@ class Tracksperanto::Import::ShakeText < Tracksperanto::Import::Base
|
|
|
7
7
|
|
|
8
8
|
def parse(io)
|
|
9
9
|
trackers = []
|
|
10
|
-
|
|
11
|
-
line = io.gets
|
|
10
|
+
io.each do | line |
|
|
12
11
|
if line =~ /TrackName (.+)/
|
|
13
12
|
trackers << Tracksperanto::Tracker.new{|t| t.name = $1 }
|
|
14
13
|
# Toss the next following string - header
|
data/lib/pipeline/base.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# pipe = Tracksperanto::Pipeline::Base.new
|
|
5
5
|
# pipe.progress_block = lambda{|percent, msg| puts("#{msg}..#{percent.to_i}%") }
|
|
6
6
|
#
|
|
7
|
-
# pipe.run("/tmp/shakescript.shk", :
|
|
7
|
+
# pipe.run("/tmp/shakescript.shk", :width => 720, :height => 576) do | *all_middlewares |
|
|
8
8
|
# # configure middlewares here
|
|
9
9
|
# end
|
|
10
10
|
#
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# at the same place where the original file resides,
|
|
13
13
|
# and setup outputs for all supported export formats.
|
|
14
14
|
class Tracksperanto::Pipeline::Base
|
|
15
|
+
EXTENSION = /\.([^\.]+)$/ #:nodoc:
|
|
15
16
|
|
|
16
17
|
include Tracksperanto::BlockInit
|
|
17
18
|
|
|
@@ -31,69 +32,77 @@ class Tracksperanto::Pipeline::Base
|
|
|
31
32
|
# Assign an array of exporters to use them instead of the standard ones
|
|
32
33
|
attr_accessor :exporters
|
|
33
34
|
|
|
34
|
-
DEFAULT_OPTIONS = {:
|
|
35
|
+
DEFAULT_OPTIONS = {:width => 720, :height => 576, :parser => Tracksperanto::Import::ShakeScript }
|
|
36
|
+
|
|
37
|
+
# Contains arrays of the form ["MiddewareName", {:param => value}]
|
|
38
|
+
attr_accessor :middleware_tuples
|
|
39
|
+
|
|
40
|
+
def wrap_output_with_middlewares(output)
|
|
41
|
+
return output unless (middleware_tuples && middleware_tuples.any?)
|
|
42
|
+
|
|
43
|
+
middleware_tuples.reverse.inject(output) do | wrapped, (middleware_name, options) |
|
|
44
|
+
Tracksperanto.get_middleware(middleware_name).new(wrapped, options || {})
|
|
45
|
+
end
|
|
46
|
+
end
|
|
35
47
|
|
|
36
48
|
# Runs the whole pipeline. Accepts the following options
|
|
37
|
-
# *
|
|
38
|
-
# *
|
|
49
|
+
# * width - The comp width, for the case that the format does not support auto size
|
|
50
|
+
# * height - The comp height, for the case that the format does not support auto size
|
|
39
51
|
# * parser - The parser class, for the case that it can't be autodetected from the file name
|
|
40
52
|
def run(from_input_file_path, passed_options = {}) #:yields: *all_middlewares
|
|
41
53
|
o = DEFAULT_OPTIONS.merge(passed_options)
|
|
42
|
-
pix_w, pix_h, parser_class = detect_importer_or_use_options(from_input_file_path, o)
|
|
43
54
|
|
|
44
55
|
# Reset stats
|
|
45
56
|
@converted_keyframes, @converted_points = 0, 0
|
|
46
57
|
|
|
47
|
-
# Grab the input
|
|
48
|
-
read_data = File.open(from_input_file_path, "rb")
|
|
49
|
-
|
|
50
58
|
# Assign the parser
|
|
51
|
-
importer =
|
|
59
|
+
importer = initialize_importer_with_path_and_options(from_input_file_path, o)
|
|
52
60
|
|
|
61
|
+
# Open the file
|
|
62
|
+
read_data = File.open(from_input_file_path, "rb")
|
|
63
|
+
|
|
53
64
|
# Setup a multiplexer
|
|
54
65
|
mux = setup_outputs_for(from_input_file_path)
|
|
55
66
|
|
|
56
67
|
# Setup middlewares
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# Yield middlewares to the block
|
|
60
|
-
yield(*middlewares) if block_given?
|
|
61
|
-
|
|
62
|
-
@converted_points, @converted_keyframes = run_export(read_data, importer, middlewares[-1])
|
|
68
|
+
endpoint = wrap_output_with_middlewares(mux)
|
|
69
|
+
@converted_points, @converted_keyframes = run_export(read_data, importer, endpoint)
|
|
63
70
|
end
|
|
64
71
|
|
|
65
72
|
def report_progress(percent_complete, message)
|
|
66
73
|
@progress_block.call(percent_complete, message) if @progress_block
|
|
67
74
|
end
|
|
68
75
|
|
|
69
|
-
def
|
|
70
|
-
d = Tracksperanto::FormatDetector.new(
|
|
76
|
+
def initialize_importer_with_path_and_options(from_input_file_path, options)
|
|
77
|
+
d = Tracksperanto::FormatDetector.new(from_input_file_path)
|
|
71
78
|
if d.match? && d.auto_size?
|
|
72
|
-
|
|
79
|
+
d.importer_klass.new
|
|
73
80
|
elsif d.match?
|
|
74
|
-
|
|
75
|
-
opts[:
|
|
81
|
+
require_dimensions_in!(opts)
|
|
82
|
+
d.importer_klass.new(:width => opts[:width], :height => opts[:height])
|
|
76
83
|
else
|
|
77
84
|
raise "Cannot autodetect the file format - please specify the importer explicitly" unless opts[:parser]
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
klass = Tracksperanto.get_exporter(opts[:parser])
|
|
86
|
+
require_dimensions_in!(opts) unless klass.autodetects_size?
|
|
87
|
+
klass.new(:width => opts[:width], :height => opts[:height])
|
|
81
88
|
end
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def require_dimensions_in!(opts)
|
|
92
|
+
raise "Width and height must be provided for this importer" unless (opts[:width] && opts[:height])
|
|
84
93
|
end
|
|
85
94
|
|
|
86
95
|
# Runs the export and returns the number of points and keyframes processed.
|
|
87
96
|
# If a block is passed, the block will receive the percent complete and the last
|
|
88
97
|
# status message that you can pass back to the UI
|
|
89
|
-
def run_export(tracker_data_io, parser,
|
|
98
|
+
def run_export(tracker_data_io, parser, exporter)
|
|
90
99
|
points, keyframes, percent_complete = 0, 0, 0.0
|
|
91
|
-
|
|
100
|
+
|
|
92
101
|
report_progress(percent_complete, "Starting the parser")
|
|
93
|
-
|
|
102
|
+
|
|
94
103
|
# Report progress from the parser
|
|
95
104
|
parser.progress_block = lambda { | m | report_progress(percent_complete, m) }
|
|
96
|
-
|
|
105
|
+
|
|
97
106
|
# Wrap the input in a progressive IO, setup a lambda that will spy on the reader and
|
|
98
107
|
# update the percentage. We will only broadcast messages that come from the parser
|
|
99
108
|
# though (complementing it with a percentage)
|
|
@@ -101,34 +110,34 @@ class Tracksperanto::Pipeline::Base
|
|
|
101
110
|
percent_complete = (50.0 / of_total) * offset
|
|
102
111
|
end
|
|
103
112
|
@ios << io_with_progress
|
|
104
|
-
|
|
113
|
+
|
|
105
114
|
trackers = parser.parse(io_with_progress)
|
|
106
|
-
|
|
115
|
+
|
|
107
116
|
report_progress(percent_complete = 50.0, "Validating #{trackers.length} imported trackers")
|
|
108
|
-
|
|
117
|
+
|
|
109
118
|
validate_trackers!(trackers)
|
|
110
|
-
|
|
119
|
+
|
|
111
120
|
report_progress(percent_complete, "Starting export")
|
|
112
|
-
|
|
121
|
+
|
|
113
122
|
percent_per_tracker = (100.0 - percent_complete) / trackers.length
|
|
114
|
-
|
|
123
|
+
|
|
115
124
|
# Use the width and height provided by the parser itself
|
|
116
|
-
|
|
125
|
+
exporter.start_export(parser.width, parser.height)
|
|
117
126
|
trackers.each_with_index do | t, tracker_idx |
|
|
118
127
|
kf_weight = percent_per_tracker / t.keyframes.length
|
|
119
128
|
points += 1
|
|
120
|
-
|
|
129
|
+
exporter.start_tracker_segment(t.name)
|
|
121
130
|
t.each_with_index do | kf, idx |
|
|
122
131
|
keyframes += 1
|
|
123
|
-
|
|
132
|
+
exporter.export_point(kf.frame, kf.abs_x, kf.abs_y, kf.residual)
|
|
124
133
|
report_progress(percent_complete += kf_weight, "Writing keyframe #{idx+1} of #{t.name.inspect}, #{trackers.length - tracker_idx} trackers to go")
|
|
125
134
|
end
|
|
126
|
-
|
|
135
|
+
exporter.end_tracker_segment
|
|
127
136
|
end
|
|
128
|
-
|
|
129
|
-
|
|
137
|
+
exporter.end_export
|
|
138
|
+
|
|
130
139
|
report_progress(100.0, "Wrote #{points} points and #{keyframes} keyframes")
|
|
131
|
-
|
|
140
|
+
|
|
132
141
|
[points, keyframes]
|
|
133
142
|
ensure
|
|
134
143
|
@ios.reject!{|e| e.close unless (!e.respond_to?(:closed?) || e.closed?) } if @ios
|
|
@@ -137,7 +146,7 @@ class Tracksperanto::Pipeline::Base
|
|
|
137
146
|
# Setup output files and return a single output
|
|
138
147
|
# that replays to all of them
|
|
139
148
|
def setup_outputs_for(input_file_path)
|
|
140
|
-
file_name = File.basename(input_file_path).gsub(
|
|
149
|
+
file_name = File.basename(input_file_path).gsub(EXTENSION, '')
|
|
141
150
|
export_klasses = exporters || Tracksperanto.exporters
|
|
142
151
|
Tracksperanto::Export::Mux.new(
|
|
143
152
|
export_klasses.map do | exporter_class |
|
|
@@ -148,21 +157,6 @@ class Tracksperanto::Pipeline::Base
|
|
|
148
157
|
)
|
|
149
158
|
end
|
|
150
159
|
|
|
151
|
-
# Setup and return the output multiplexor wrapped in all necessary middlewares.
|
|
152
|
-
# Middlewares must be returned in an array to be passed to the configuration block
|
|
153
|
-
# afterwards
|
|
154
|
-
def setup_middleware_chain_with(output)
|
|
155
|
-
scaler = Tracksperanto::Middleware::Scaler.new(output)
|
|
156
|
-
slipper = Tracksperanto::Middleware::Slipper.new(scaler)
|
|
157
|
-
golden = Tracksperanto::Middleware::Golden.new(slipper)
|
|
158
|
-
reformat = Tracksperanto::Middleware::Reformat.new(golden)
|
|
159
|
-
shift = Tracksperanto::Middleware::Shift.new(reformat)
|
|
160
|
-
prefix = Tracksperanto::Middleware::Prefix.new(shift)
|
|
161
|
-
lerp = Tracksperanto::Middleware::Lerp.new(prefix)
|
|
162
|
-
lengate = Tracksperanto::Middleware::LengthCutoff.new(lerp)
|
|
163
|
-
[scaler, slipper, golden, reformat, shift, prefix, lerp, lengate]
|
|
164
|
-
end
|
|
165
|
-
|
|
166
160
|
# Open the file for writing and register it to be closed automatically
|
|
167
161
|
def open_owned_export_file(path_to_file)
|
|
168
162
|
@ios ||= []
|
data/lib/tracksperanto.rb
CHANGED
|
@@ -4,7 +4,7 @@ require 'tempfile'
|
|
|
4
4
|
|
|
5
5
|
module Tracksperanto
|
|
6
6
|
PATH = File.expand_path(File.dirname(__FILE__))
|
|
7
|
-
VERSION = '1.
|
|
7
|
+
VERSION = '1.9.0'
|
|
8
8
|
|
|
9
9
|
module Import; end
|
|
10
10
|
module Export; end
|
|
@@ -38,7 +38,16 @@ module Tracksperanto
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
self.exporters, self.importers, self.middlewares = [], [], []
|
|
41
|
-
|
|
41
|
+
|
|
42
|
+
# Case-insensitive search for a middleware class by name
|
|
43
|
+
def self.get_middleware(name)
|
|
44
|
+
middlewares.each do | x |
|
|
45
|
+
return x if x.const_name.downcase == name.downcase
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
raise NameError, "Unknown middleware #{name}"
|
|
49
|
+
end
|
|
50
|
+
|
|
42
51
|
# Case-insensitive search for an export module by name
|
|
43
52
|
def self.get_exporter(name)
|
|
44
53
|
exporters.each do | x |
|
|
@@ -5,7 +5,7 @@ require "tempfile"
|
|
|
5
5
|
class Tracksperanto::BufferIO < DelegateClass(IO)
|
|
6
6
|
include Tracksperanto::Returning
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
MAX_IN_MEM_BYTES = 100_000
|
|
9
9
|
|
|
10
10
|
def initialize
|
|
11
11
|
__setobj__(StringIO.new)
|
|
@@ -37,12 +37,11 @@ class Tracksperanto::BufferIO < DelegateClass(IO)
|
|
|
37
37
|
def replace_with_tempfile_if_needed
|
|
38
38
|
return if @tempfile_in
|
|
39
39
|
io = __getobj__
|
|
40
|
-
if io.pos >
|
|
41
|
-
@tempfile_in = true
|
|
40
|
+
if io.pos > MAX_IN_MEM_BYTES
|
|
42
41
|
tf = Tempfile.new("tracksperanto-xbuf")
|
|
43
|
-
io.
|
|
44
|
-
tf.write(io.read) until io.eof?
|
|
42
|
+
tf.write(io.string)
|
|
45
43
|
__setobj__(tf)
|
|
44
|
+
@tempfile_in = true
|
|
46
45
|
end
|
|
47
46
|
end
|
|
48
47
|
end
|
|
@@ -12,9 +12,7 @@ class BoujouImportTest < Test::Unit::TestCase
|
|
|
12
12
|
def test_parsing_from_fixture
|
|
13
13
|
fixture = File.open(File.dirname(__FILE__) + '/samples/boujou_features_text/boujou_txt_export.txt')
|
|
14
14
|
|
|
15
|
-
parser = Tracksperanto::Import::Boujou.new
|
|
16
|
-
parser.width = 2560
|
|
17
|
-
parser.height = 1200
|
|
15
|
+
parser = Tracksperanto::Import::Boujou.new(:width => 2560, :height => 1200)
|
|
18
16
|
|
|
19
17
|
trackers = parser.parse(fixture)
|
|
20
18
|
assert_equal 20, trackers.length
|
|
@@ -9,7 +9,7 @@ class PFTrackImportTest < Test::Unit::TestCase
|
|
|
9
9
|
assert !i.autodetects_size?
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def
|
|
12
|
+
def test_parsing_from_pftrack5_with_digit_names
|
|
13
13
|
fixture = File.open(File.dirname(__FILE__) + '/samples/pftrack5/apft.2dt')
|
|
14
14
|
parser = Tracksperanto::Import::PFTrack.new(:width => 1920, :height => 1080)
|
|
15
15
|
|
data/test/test_tracksperanto.rb
CHANGED
|
@@ -42,6 +42,11 @@ class TracksperantoTest < Test::Unit::TestCase
|
|
|
42
42
|
assert_equal i1, Tracksperanto::Import::Syntheyes
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
def test_get_middleware
|
|
46
|
+
i1 = Tracksperanto.get_middleware("lERP")
|
|
47
|
+
assert_equal i1, Tracksperanto::Middleware::Lerp
|
|
48
|
+
end
|
|
49
|
+
|
|
45
50
|
def test_get_importer_multicase
|
|
46
51
|
i1 = Tracksperanto.get_importer("ShakeScript")
|
|
47
52
|
assert_equal i1, Tracksperanto::Import::ShakeScript
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tracksperanto
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Julik Tarkhanov
|
|
@@ -9,9 +9,19 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2010-03-
|
|
12
|
+
date: 2010-03-21 00:00:00 +01:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: progressbar
|
|
17
|
+
type: :runtime
|
|
18
|
+
version_requirement:
|
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
20
|
+
requirements:
|
|
21
|
+
- - ">="
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: "0"
|
|
24
|
+
version:
|
|
15
25
|
- !ruby/object:Gem::Dependency
|
|
16
26
|
name: flexmock
|
|
17
27
|
type: :development
|