tracksperanto 3.3.13 → 3.4.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 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