tracksperanto 3.3.13 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,7 @@
1
+ === 3.4.0
2
+
3
+ * Add specific Flame/Smoke exporters for IFFS 2014 and above, including updated cornerpin order
4
+
1
5
  === 3.3.13
2
6
 
3
7
  * Fix the bug in Shake script imports where the import would fail if animation curves contained a non-numeric value
data/README.md CHANGED
@@ -119,6 +119,8 @@ If you want your own copy of the web application at your facility we can discuss
119
119
  * Autodesk Softimage nulls Python script
120
120
  * Bare Ruby code
121
121
  * Flame/Smoke 2D Stabilizer setup
122
+ * Flame/Smoke 2D Stabilizer setup (v. 2014 and above)
123
+ * Flame/Smoke 2D Stabilizer setup (v. 2014 and above) for corner pins
122
124
  * Flame/Smoke 2D Stabilizer setup for bilinear corner pins
123
125
  * MatchMover REALVIZ Ascii Point Tracks .rz2 file
124
126
  * Maya ASCII scene with locators on an image plane
@@ -0,0 +1,259 @@
1
+ # -*- encoding : utf-8 -*-
2
+ class Tracksperanto::Export::FlameStabilizer2014 < Tracksperanto::Export::Base
3
+
4
+ COLOR = "50 50 50"
5
+ DATETIME_FORMAT = '%a %b %d %H:%M:%S %Y'
6
+
7
+ def self.desc_and_extension
8
+ "flamesmoke2014.stabilizer"
9
+ end
10
+
11
+ def self.human_name
12
+ "Flame/Smoke 2D Stabilizer setup (v. 2014 and above)"
13
+ end
14
+
15
+ def start_export( img_width, img_height)
16
+ @counter = 0
17
+ @first_ref_frames = []
18
+ @width, @height = img_width, img_height
19
+ @temp = Tracksperanto::BufferIO.new
20
+ @writer = FlameChannelParser::Builder.new(@temp)
21
+ end
22
+
23
+ def end_export
24
+ # Now make another writer, this time for our main IO
25
+ @writer = FlameChannelParser::Builder.new(@io)
26
+
27
+ # Now we know how many trackers we have so we can write the header
28
+ # data along with NbTrackers
29
+ write_header_with_number_of_trackers(@counter)
30
+
31
+ # Now write everything that we accumulated earlier into the base IO
32
+ @temp.rewind
33
+ @io.write(@temp.read) until @temp.eof?
34
+ @temp.close!
35
+
36
+ # Send the ChannelEnd command and list the trackers
37
+ @writer.channel_end
38
+ @first_ref_frames.each_with_index do | ref_frame, i|
39
+ @writer.write_unterminated_block!("tracker", i) do |t|
40
+ t.active true
41
+ t.color_hash!("colour", 0, 100, 0)
42
+ t.fixed_ref true
43
+ t.fixed_x false
44
+ t.fixed_y false
45
+ t.tolerance 100
46
+ t.offsets_x 0
47
+ t.offsets_y 0
48
+ t.first_ref_frame ref_frame
49
+ end
50
+ end
51
+
52
+ # Write the finalizing "End"
53
+ @writer.write_loose!("end")
54
+ end
55
+
56
+ def start_tracker_segment(tracker_name)
57
+ @counter += 1
58
+ @write_first_frame = true
59
+ end
60
+
61
+ def export_point(frame, abs_float_x, abs_float_y, float_residual)
62
+ flame_frame = frame + 1
63
+ if @write_first_frame
64
+ export_first_point(flame_frame, abs_float_x, abs_float_y)
65
+ # Record the first ref frame for this tracker
66
+ @first_ref_frames << flame_frame
67
+ @write_first_frame = false
68
+ else
69
+ export_remaining_point(flame_frame, abs_float_x, abs_float_y)
70
+ end
71
+ end
72
+
73
+ def end_tracker_segment
74
+ # We write these at tracker end since we need to know in advance
75
+ # how many keyframes they should contain
76
+ write_shift_channel("shift/x", @x_shift_values)
77
+ write_shift_channel("shift/y", @y_shift_values)
78
+
79
+ # And finish with the offset channels. The order of channels is important!
80
+ # (otherwise the last tracker's shift animation is not imported by Flame)
81
+ # https://github.com/guerilla-di/tracksperanto/issues/1
82
+ write_offset_channels
83
+ end
84
+
85
+ private
86
+
87
+ def export_remaining_point(flame_frame, abs_float_x, abs_float_y)
88
+ # Just continue buffering the upcoming shift keyframes and flush them in the end
89
+ shift_x, shift_y = @base_x - abs_float_x, @base_y - abs_float_y
90
+ @x_shift_values.push([flame_frame, shift_x])
91
+ @y_shift_values.push([flame_frame, shift_y])
92
+ end
93
+
94
+ def export_first_point(flame_frame, abs_float_x, abs_float_y)
95
+ @base_x, @base_y = abs_float_x, abs_float_y
96
+ write_first_frame(abs_float_x, abs_float_y)
97
+ # For Flame to recognize the reference frame of the Shift channel
98
+ # we need it to contain zero as an int, not as a float. The shift proceeds
99
+ # from there.
100
+ @x_shift_values = [[flame_frame, 0]]
101
+ @y_shift_values = [[flame_frame, 0]]
102
+ end
103
+
104
+ # The shift channel is what determines how the tracking point moves.
105
+ def write_shift_channel(name_without_prefix, values)
106
+ @writer.channel(prefix(name_without_prefix)) do | c |
107
+ c.extrapolation :constant
108
+ c.value values[0][1]
109
+ c.key_version 1
110
+ c.size values.length
111
+ values.each_with_index do | (f, v), i |
112
+ c.key(i) do | k |
113
+ k.frame f
114
+ k.value v
115
+ # Omit:
116
+ # RHandle_dX 0.333333343
117
+ # RHandle_dY -5.7284646
118
+ # LHandle_dX -0.333333343
119
+ # LHandle_dY 5.7284646
120
+ k.curve_mode :hermite
121
+ k.curve_order :linear
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ def prefix(tracker_channel)
128
+ "tracker%d/%s" % [@counter, tracker_channel]
129
+ end
130
+
131
+ def write_header_with_number_of_trackers(number_of_trackers)
132
+ @writer.stabilizer_file_version "6.0"
133
+ @writer.creation_date(Time.now.strftime(DATETIME_FORMAT))
134
+ @writer.linebreak!(2)
135
+
136
+ @writer.nb_trackers number_of_trackers
137
+ @writer.selected 0
138
+ @writer.frame_width @width
139
+ @writer.frame_height @height
140
+ @writer.auto_key true
141
+ @writer.motion_path true
142
+ @writer.icons true
143
+ @writer.auto_pan false # hate it!
144
+ @writer.edit_mode 0
145
+ @writer.format 0
146
+ @writer.color_hash!("padding", 0, 100, 0)
147
+ @writer.oversampling false
148
+ @writer.opacity 50
149
+ @writer.zoom 3
150
+ @writer.field false
151
+ @writer.backward false
152
+ @writer.anim
153
+ end
154
+
155
+ def write_first_frame(x, y)
156
+ write_track_channels
157
+ write_track_width_and_height
158
+ write_ref_width_and_height
159
+ write_ref_channels(x, y)
160
+ write_deltax_and_deltay_channels
161
+ end
162
+
163
+ def write_track_channels
164
+ ctr_x, ctr_y = @width / 2, @height / 2
165
+
166
+ # track determines where the tracking box is, and should be in the center
167
+ # of the image for Flame to compute all other shifts properly
168
+ %w( track/x track/y).map(&method(:prefix)).zip([ctr_x, ctr_y]).each do | cname, default |
169
+ @writer.channel(cname) do | c |
170
+ c.extrapolation("constant")
171
+ c.value(default.to_i)
172
+ c.colour(COLOR)
173
+ end
174
+ end
175
+ end
176
+
177
+ # The size of the tracking area
178
+ def write_track_width_and_height
179
+ %w( track/width track/height ).map(&method(:prefix)).each do | channel_name |
180
+ @writer.channel(channel_name) do | c |
181
+ c.extrapolation :linear
182
+ c.value 15
183
+ c.colour COLOR
184
+ end
185
+ end
186
+ end
187
+
188
+ # The size of the reference area
189
+ def write_ref_width_and_height
190
+ %w( ref/width ref/height).map(&method(:prefix)).each do | channel_name |
191
+ @writer.channel(channel_name) do | c |
192
+ c.extrapolation :linear
193
+ c.value 10
194
+ c.colour COLOR
195
+ end
196
+ end
197
+ end
198
+
199
+ # The Ref channel contains the reference for the shift channel in absolute
200
+ # coordinates, and is set as float. Since we do not "snap" the tracker in
201
+ # the process it's enough for us to make one keyframe in the ref channels
202
+ # at the same frame as the first shift keyframe
203
+ def write_ref_channels(ref_x, ref_y)
204
+ %w( ref/x ref/y).map(&method(:prefix)).zip([ref_x, ref_y]).each do | cname, default |
205
+ @writer.channel(cname) do | c |
206
+ c.extrapolation("constant")
207
+ c.value(default)
208
+ c.colour(COLOR)
209
+ c.key_version 1
210
+ c.size 1
211
+ c.key(0) do | k |
212
+ k.frame 1
213
+ k.value default
214
+ k.interpolation :constant
215
+ k.left_slope 2.4
216
+ k.right_slope 2.4
217
+ end
218
+ end
219
+ end
220
+ end
221
+
222
+ def write_deltax_and_deltay_channels
223
+ # This is used for deltax and deltay (offset tracking).
224
+ # We set it to zero and lock
225
+ %w( ref/dx ref/dy).map(&method(:prefix)).each do | chan, v |
226
+ @writer.channel(chan) do | c |
227
+ c.extrapolation("constant")
228
+ c.value 0
229
+ c.colour(COLOR)
230
+ c.size 2
231
+ c.key_version 1
232
+ c.key(0) do | k |
233
+ k.frame 0
234
+ k.value 0
235
+ k.value_lock true
236
+ k.delete_lock true
237
+ k.interpolation :constant
238
+ end
239
+ c.key(1) do | k |
240
+ k.frame 1
241
+ k.value 0
242
+ k.value_lock true
243
+ k.delete_lock true
244
+ k.interpolation :constant
245
+ end
246
+ end # Chan block
247
+ end
248
+ end
249
+
250
+ def write_offset_channels
251
+ %w(offset/x offset/y).map(&method(:prefix)).each do | c |
252
+ @writer.channel(c) do | chan |
253
+ chan.extrapolation :constant
254
+ chan.value 0
255
+ end
256
+ end
257
+ end
258
+
259
+ end
@@ -0,0 +1,108 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # Exports setups with tracker naming that works with the Action bilinears
3
+ class Tracksperanto::Export::FlameStabilizer2014Cornerpin < Tracksperanto::Export::FlameStabilizer2014
4
+
5
+ def self.desc_and_extension
6
+ "flamesmoke_2014_cornerpin.stabilizer"
7
+ end
8
+
9
+ def self.human_name
10
+ "Flame/Smoke 2D Stabilizer setup (v. 2014 and above) for corner pins"
11
+ end
12
+
13
+ # The trackers for cornerpins should go in Z order, but now
14
+ # in N order, unline it did previously. Like so:
15
+ #
16
+ # TL(1) TR(3)
17
+ # ˆ \ ˆ
18
+ # | \ |
19
+ # | \ |
20
+ # | \ |
21
+ # BL(0) BR(2)
22
+ #
23
+ # This "kinda tool" ensures that this is indeed taking place
24
+ class Sorter
25
+ include Tracksperanto::SimpleExport # so that it calls OUR methods
26
+
27
+ def initialize(exporter)
28
+ @exp = exporter
29
+ end
30
+
31
+ def start_export(w,h)
32
+ @width, @height = w, h
33
+ @corners, @four_done = [], false
34
+ end
35
+
36
+ def start_tracker_segment(name)
37
+ @four_done = (@corners.length == 4)
38
+ return if @four_done
39
+ @corners.push(Tracksperanto::Tracker.new(:name => name))
40
+ end
41
+
42
+ def export_point(f, x, y, r)
43
+ return if @four_done
44
+ @corners[-1].keyframe! :frame => f, :abs_x => x, :abs_y => y, :residual => r
45
+ end
46
+
47
+ def end_tracker_segment
48
+ # Just leave that
49
+ end
50
+
51
+ def end_export
52
+ # We will have problems sorting if we have too few trackers
53
+ return @exp.just_export(@corners, @width, @height) unless @corners.length == 4
54
+
55
+ # Sort the trackers, first in Y of the first keyframe
56
+ in_y = sort_on_first_keyframe(@corners, :abs_y)
57
+
58
+ # then on the X for the two separate blocks for top and bottom
59
+ tl, tr = sort_on_first_keyframe(in_y[2..3], :abs_x)
60
+ bl, br = sort_on_first_keyframe(in_y[0..1], :abs_x)
61
+
62
+ bulk = [bl, tl, br, tr] # New Flame 2014 order
63
+
64
+ @exp.just_export(bulk, @width, @height)
65
+ end
66
+
67
+ private
68
+
69
+ def sort_on_first_keyframe(enum, property)
70
+ enum.sort{|a, b| a[0].send(property) <=> b[0].send(property) }
71
+ end
72
+ end
73
+
74
+ # Initialize the exporter with a preconfigured sorter around it.
75
+ # When this object receives the commands they will come from the Sorter instead,
76
+ # and the trackers will already be in their Z-order
77
+ def self.new(*arguments)
78
+ object = super
79
+ Sorter.new(object)
80
+ end
81
+
82
+ # Now instead of names we got vague indices. YAY for Rue Duc!
83
+ CORNERPIN_NAMING = %w( none tracker0_0 tracker0_1 tracker1_0 tracker1_1 )
84
+
85
+ # Overridden to give the right names to trackers
86
+ def prefix(tracker_channel)
87
+ tracker_name = CORNERPIN_NAMING[@counter]
88
+ [tracker_name, tracker_channel].join("/")
89
+ end
90
+
91
+ def start_tracker_segment(tracker_name)
92
+ if (@counter == 4)
93
+ @skip = true
94
+ else
95
+ super
96
+ end
97
+ end
98
+
99
+ def export_point(frame, abs_float_x, abs_float_y, float_residual)
100
+ return if @skip
101
+ super
102
+ end
103
+
104
+ def end_tracker_segment
105
+ return if @skip
106
+ super
107
+ end
108
+ end
data/lib/tracksperanto.rb CHANGED
@@ -5,7 +5,7 @@ require 'tempfile'
5
5
 
6
6
  module Tracksperanto
7
7
  PATH = File.expand_path(File.dirname(__FILE__))
8
- VERSION = '3.3.13'
8
+ VERSION = '3.4.0'
9
9
 
10
10
  module Import; end
11
11
  module Export; end