depix 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/depix/structs.rb CHANGED
@@ -1,257 +1,137 @@
1
+ require File.dirname(__FILE__) + '/dict'
2
+
3
+
1
4
  module Depix
2
- # Basically a copy of http://trac.imagemagick.org/browser/ImageMagick/trunk/coders/dpx.c
3
- #
4
- # Which is a reformulation of http://www.cineon.com/ff_draft.php
5
- #
6
- # Which is a preamble to some SMPTE crap that you have to buy for 14 bucks. Or download from http://www.cinesite.com/static/scanning/techdocs/dpx_spec.pdf
7
- module Structs
8
-
9
- COLORIMETRIC = {
10
- :UserDefined => 0,
11
- :PrintingDensity => 1,
12
- :Linear => 2,
13
- :Logarithmic => 3,
14
- :UnspecifiedVideo => 4,
15
- :SMTPE_274M => 5,
16
- :ITU_R709 => 6,
17
- :ITU_R601_625L => 7,
18
- :ITU_R601_525L => 8,
19
- :NTSCCompositeVideo => 9,
20
- :PALCompositeVideo => 10,
21
- :ZDepthLinear => 11,
22
- :DepthHomogeneous => 12
23
- }
24
-
25
- COMPONENT_TYPE = {
26
- :Undefined => 0,
27
- :Red => 1,
28
- :Green => 2,
29
- :Blue => 3,
30
- :Alpha => 4,
31
- :Luma => 6,
32
- :ColorDifferenceCbCr => 7,
33
- :Depth => 8,
34
- :CompositeVideo => 9,
35
- :RGB => 50,
36
- :RGBA => 51,
37
- :ABGR => 52,
38
- :CbYCrY422 => 100,
39
- :CbYACrYA4224 => 101,
40
- :CbYCr444 => 102,
41
- :CbYCrA4444 => 103,
42
- :UserDef2Element => 150,
43
- :UserDef3Element => 151,
44
- :UserDef4Element => 152,
45
- :UserDef5Element => 153,
46
- :UserDef6Element => 154,
47
- :UserDef7Element => 155,
48
- :UserDef8Element => 156,
49
- }
50
-
51
- #:stopdoc:
52
-
53
- # To avoid fucking up with sizes afterwards
54
- U32, R32, U16, U8, UCHAR = 4, 4, 2, 1, 1
55
-
56
- def self.struct_size(struct_const) #:nodoc:
57
- struct_const.inject(0){| s, e | s + e[2]}
58
- end
59
-
60
- # Used to distinguish structs from repeated values
61
- class Struct < Array; end
62
5
 
63
-
64
- FILE_INFO = Struct[
65
- [:magic, String, 4],
66
- [:image_offset, Integer, U32],
67
-
68
- [:version, String, 8],
6
+ class FileInfo < Dict
7
+ char :magic, 4, :desc => 'Endianness (SDPX is big endian)', :req => true
8
+ u32 :image_offset, :desc => 'Offset to image data in bytes', :req => true
9
+ char :version, 8, :desc => 'Version of header format', :req => true
69
10
 
70
- [:file_size, Integer, U32],
71
- [:ditto_key, Integer, U32],
72
- [:generic_size, Integer, U32],
73
- [:industry_size, Integer, U32],
74
- [:user_size, Integer, U32],
11
+ u32 :file_size, :desc => "Total image size in bytes", :req => true
12
+ u32 :ditto_key, :desc => 'Whether the basic headers stay the same through the sequence (1 means they do)'
13
+ u32 :generic_size, :desc => 'Generic header length'
14
+ u32 :industry_size, :desc => 'Industry header length'
15
+ u32 :user_size, :desc => 'User header length'
75
16
 
76
- [:filename, String, 100],
77
- [:timestamp, String, 24],
78
- [:creator, String, 100],
79
- [:project, String, 200],
80
- [:copyright, String, 200],
81
-
82
- [:encrypt_key, Integer, U32],
83
- [:reserve, String, 104],
84
- ]
85
-
86
- FILM_INFO = Struct[
87
- [:id, String, 2],
88
- [:type, String, 2],
89
- [:offset, String, 2],
90
- [:prefix, String, 6],
91
- [:count, String, 4],
92
- [:format, String, 32],
93
-
94
- [:frame_position, Integer, U32],
95
- [:sequence_extent, Integer, U32],
96
- [:held_count, Integer, U32],
17
+ char :filename, 100, :desc => 'Original filename'
18
+ char :timestamp, 24, :desc => 'Creation 15'
19
+ char :creator, 100, :desc => 'Creator application'
20
+ char :roject, 200, :desc => 'Project name'
21
+ char :copyright, 200, :desc => 'Copyright'
97
22
 
98
- [:frame_rate, Float, R32],
99
- [:shutter_angle, Float, R32],
100
-
101
- [:frame_id, String, 32],
102
- [:slate, String, 100],
103
- [:reserve, String, 56],
104
- ]
23
+ u32 :encrypt_key, :desc => 'Encryption key'
24
+ char :reserve, 104
25
+ end
105
26
 
27
+ class FilmInfo < Dict
28
+ char :id, 2, :desc => 'Film mfg. ID code (2 digits from film edge code)'
29
+ char :type, 2, :desc => 'Film type (2 digits from film edge code)'
30
+ char :offset, 2, :desc => 'Offset in perfs (2 digits from film edge code)'
31
+ char :prefix, 6, :desc => 'Prefix (6 digits from film edge code'
32
+ char :count, 4, :desc => 'Count (4 digits from film edge code)'
33
+ char :format, 32, :desc => 'Format (e.g. Academy)'
106
34
 
107
- IMAGE_ELEMENT = Struct[
108
- [:data_sign, Integer, U32],
109
- [:low_data, Integer, U32],
110
- [:low_quantity, Float, R32],
111
- [:high_data, Integer, U32],
112
- [:high_quantity, Float, R32],
113
-
114
- # TODO: Autoreplace with enum values. Note: with these we will likely be addressing the enums
115
- [:descriptor, Integer, U8],
116
- [:transfer, Integer, U8],
117
- [:colorimetric, Integer, U8],
118
- [:bit_size, Integer, U8],
119
-
120
- [:packing, Integer, U16],
121
- [:encoding, Integer, U16],
122
- [:data_offset, Integer, U32],
123
- [:end_of_line_padding, Integer, U32],
124
- [:end_of_image_padding, Integer, U32],
125
- [:description, String, 32],
126
- ]
127
-
128
- IMAGE_ELEMENTS = (0..7).map{|e| [e, IMAGE_ELEMENT, struct_size(IMAGE_ELEMENT)] }
35
+ u32 :frame_position, :desc => 'Frame position in sequence'
36
+ u32 :sequence_extent, :desc => 'Sequence length'
37
+ u32 :held_count, :desc => 'For how many frames the frame is held'
38
+
39
+ r32 :frame_rate, :desc => 'Frame rate'
40
+ r32 :shutter_angle, :desc => 'Shutter angle'
41
+
42
+ char :frame_id, 32, :desc => 'Frame identification (keyframe)'
43
+ char :slate, 100, :desc => 'Slate information'
44
+ char :reserve, 56
45
+ end
129
46
 
130
- IMAGE_INFO = Struct[
131
- [:orientation, Integer, U16],
132
- [:number_elements, Integer, U16],
47
+ class ImageElement < Dict
48
+ u32 :data_sign, :desc => 'Data sign (0=unsigned, 1=signed). Core is unsigned', :req => true
49
+
50
+ u32 :low_data, :desc => 'Reference low data code value'
51
+ r32 :low_quantity, :desc => 'Reference low quantity represented'
52
+ u32 :high_data, :desc => 'Reference high data code value (1023 for 10bit per channel)'
53
+ r32 :high_quantity, :desc => 'Reference high quantity represented'
133
54
 
134
- [:pixels_per_line, Integer, U32],
135
- [:lines_per_element, Integer, U32],
55
+ # TODO: Autoreplace with enum values.
56
+ u8 :descriptor, :desc => 'Descriptor for this image element (ie Video or Film), by enum', :req => true
57
+ u8 :transfer, :desc => 'Transfer function (ie Linear), by enum', :req => true
58
+ u8 :colorimetric, :desc => 'Colorimetric (ie YcbCr), by enum', :req => true
59
+ u8 :bit_size, :desc => 'Bit size for element (ie 10)', :req => true
136
60
 
137
- [:image_elements, IMAGE_ELEMENTS, struct_size(IMAGE_ELEMENTS) ],
61
+ u16 :packing, :desc => 'Packing (0=Packed into 32-bit words, 1=Filled to 32-bit words))', :req => true
62
+ u16 :encoding, :desc => "Encoding (0=None, 1=RLE)", :req => true
63
+ u32 :data_offset, :desc => 'Offset to data for this image element', :req => true
64
+ u32 :end_of_line_padding, :desc => "End-of-line padding for this image element"
65
+ u32 :end_of_image_padding, :desc => "End-of-line padding for this image element"
66
+ char :description, 32
67
+ end
138
68
 
139
- [:reserve, String, 52],
140
- ]
141
-
142
- BORDER = (0..3).map{|s| [s, Integer, U16] }
69
+ class OrientationInfo < Dict
143
70
 
144
- ASPECT_RATIO = [
145
- [0, Integer, U32],
146
- [1, Integer, U32],
147
- ]
71
+ u32 :x_offset
72
+ u32 :y_offset
148
73
 
149
- ORIENTATION_INFO = Struct[
74
+ r32 :x_center
75
+ r32 :y_center
150
76
 
151
- [:x_offset, Integer, U32],
152
- [:y_offset, Integer, U32],
153
-
154
- [:x_center, Float, R32],
155
- [:y_center, Float, R32],
156
-
157
- [:x_size, Integer, U32],
158
- [:y_size, Integer, U32],
159
-
160
- [:filename, String, 100],
161
- [:timestamp, String, 24],
162
- [:device, String, 32],
163
- [:serial, String, 32],
164
-
165
- [:border, BORDER, struct_size(BORDER)],
166
- [:aspect_ratio, ASPECT_RATIO, struct_size(ASPECT_RATIO)],
77
+ u32 :x_size, :desc => 'Original X size'
78
+ u32 :y_size, :desc => 'Original Y size'
167
79
 
168
- [:reserve, String, 28],
169
- ]
80
+ char :filename, 100, :desc => "Source image filename"
81
+ char :timestamp, 24, :desc => "Source image/tape timestamp"
82
+ char :device, 32, :desc => "Input device or tape"
83
+ char :serial, 32, :desc => "Input device serial number"
170
84
 
171
- TELEVISION_INFO = Struct[
172
- [:time_code, Integer, U32],
173
- [:user_bits, Integer, U32],
85
+ array :border, :u16, 4, :desc => 'Border validity: XL, XR, YT, YB'
86
+ array :aspect_ratio , :u32, 2, :desc => "Aspect (H:V)"
174
87
 
175
- [:interlace, Integer, U8],
176
- [:field_number, Integer, U8],
177
- [:video_signal, Integer, U8],
178
- [:padding, Integer, U8],
179
-
180
- [:horizontal_sample_rate, Float, R32],
181
- [:vertical_sample_rate, Float, R32],
182
- [:frame_rate, Float, R32],
183
- [:time_offset, Float, R32],
184
- [:gamma, Float, R32],
185
- [:black_level, Float, R32],
186
- [:black_gain, Float, R32],
187
- [:break_point, Float, R32],
188
- [:white_level, Float, R32],
189
- [:integration_times, Float, R32],
190
- [:reserve, String, 76],
191
- ]
192
-
193
- USER_INFO = Struct[
194
- [:id, String, 32],
195
- [:user_data, Integer, U32],
196
- ]
197
-
198
- DPX_INFO = Struct[
199
- [:file, FILE_INFO, struct_size(FILE_INFO)],
200
- [:image, IMAGE_INFO, struct_size(IMAGE_INFO)],
201
- [:orientation, ORIENTATION_INFO, struct_size(ORIENTATION_INFO)],
202
- [:film, FILM_INFO, struct_size(FILM_INFO)],
203
- [:television, TELEVISION_INFO, struct_size(TELEVISION_INFO)],
204
- [:user, USER_INFO, struct_size(USER_INFO)],
205
- ]
88
+ char :reserve, 28
89
+ end
206
90
 
207
- # Converts the nexted structs to one template that can be fed to Ruby pack/unpack. This yields
208
- # some impressive performance improvements (about 1.4 times faster) over reading fields bytewise
209
- def self.struct_to_template(struct, big_endian)
210
- keys, template = [], ''
211
- struct.each do | elem |
212
- key, cast, size = elem
213
- pattern = case true
214
- when cast.is_a?(Struct) # Nested structs
215
- inner_keys, inner_template = struct_to_template(cast, big_endian)
216
- # Use a dot as a divider. We will detect it later on and merge into nested hashes
217
- keys += inner_keys.map{|k| [key, k].join('.') }
218
- inner_template
219
- when cast.is_a?(Array) # Repeat values
220
- inner_keys, inner_template = struct_to_template(cast, big_endian)
221
- # Use a dot as a divider. We will detect it later on and merge into nested hashes
222
- keys += inner_keys.map{|k| [key, k].join('.') }
223
- inner_template
224
- when cast == Integer || cast == Timecode
225
- keys << key.to_s
226
- integer_template(size, big_endian)
227
- when cast == String
228
- keys << key.to_s
229
- "A#{size}"
230
- when cast == Float
231
- keys << key.to_s
232
- big_endian ? "g" : "f"
233
- end
234
-
235
- template << pattern
236
- end
237
- [keys, template]
91
+ class TelevisionInfo < Dict
92
+ u32 :time_code, :desc => "Timecode, formatted as HH:MM:SS:FF in the 4 higher bits of each 8bit group"
93
+ u32 :user_bits, :desc => "Timecode UBITs"
94
+ u8 :interlace, :desc => "Interlace (0 = noninterlaced; 1 = 2:1 interlace"
95
+
96
+ u8 :field_number, :desc => 'Field number'
97
+ u8 :video_signal, :desc => "Video signal (by enum)"
98
+ u8 :padding, :desc => "Zero (for byte alignment)"
99
+
100
+ r32 :horizontal_sample_rate, :desc => 'Horizontal sampling Hz'
101
+ r32 :vertical_sample_rate, :desc => 'Vertical sampling Hz'
102
+ r32 :frame_rate, :desc => 'Frame rate'
103
+ r32 :time_offset, :desc => 'From sync pulse to first pixel'
104
+ r32 :gamma, :desc => 'Gamma'
105
+ r32 :black_level, :desc => 'Black pedestal code value'
106
+ r32 :black_gain, :desc => 'Black gain code value'
107
+ r32 :break_point, :desc => 'Break point (?)'
108
+ r32 :white_level, :desc => 'White level'
109
+ r32 :integration_times, :desc => 'Integration times (S)'
110
+ r32 :reserve
238
111
  end
239
112
 
240
- def self.integer_template(size, big_endian) #:nodoc:
241
- if size == 1
242
- "c"
243
- elsif size == 2
244
- big_endian ? "n" : "v"
245
- elsif size == 4
246
- big_endian ? "N" : "V"
247
- end
113
+ class UserInfo < Dict
114
+ char :id, 32, :desc => 'Name of the user data tag'
115
+ u32 :user_data_ptr
248
116
  end
249
117
 
250
- # Shortcuts used to speed up parsing
251
- TEMPLATE_KEYS, TEMPLATE_BE = struct_to_template(DPX_INFO, true)
252
- TEMPLATE_LE = struct_to_template(DPX_INFO, false)
253
- TEMPLATE_LENGTH = struct_size(DPX_INFO)
118
+ class ImageInfo < Dict
119
+ u16 :orientation, OrientationInfo, :desc => 'Orientation descriptor', :req => true
120
+ u16 :number_elements, :desc => 'How many elements to scan', :req => true
121
+
122
+ u32 :pixels_per_line, :desc => 'Pixels per horizontal line', :req => true
123
+ u32 :lines_per_element, :desc => 'Line count', :req => true
124
+ array :image_elements, ImageElement, 8, :desc => "Image elements"
125
+ char :reserve, 52
126
+ end
254
127
 
255
- #:startdoc:
128
+ #:include:DPX_HEADER_STRUCTURE.txt
129
+ class DPX < Dict
130
+ inner :file, FileInfo, :desc => "File information"
131
+ inner :image, ImageInfo, :desc => "Image information"
132
+ inner :orientation, OrientationInfo, :desc => "Orientation"
133
+ inner :film, FilmInfo, :desc => "Film industry info"
134
+ inner :television, TelevisionInfo, :desc => "TV industry info"
135
+ inner :user, UserInfo, :desc => "User info"
256
136
  end
257
137
  end
data/test/test_depix.rb CHANGED
@@ -1,95 +1,13 @@
1
1
  require File.dirname(__FILE__) + '/../lib/depix'
2
2
  require 'test/unit'
3
3
 
4
- class StructsTest < Test::Unit::TestCase
5
- def test_struct_size
6
- int = [[:some, Integer, 13]]
7
- assert_equal 13, Depix::Structs.struct_size(int)
8
-
9
- two_ints = [[:some, String, 13], [:some, String, 13]]
10
- assert_equal 26, Depix::Structs.struct_size(two_ints)
11
-
12
- nested_struct = [[:some, String, 10], [:some, two_ints, Depix::Structs.struct_size(two_ints)]]
13
- assert_equal 36, Depix::Structs.struct_size(nested_struct)
14
- end
15
-
16
- def test_integer_template
17
- assert_equal "N", Depix::Structs.integer_template(4, true)
18
- assert_equal "V", Depix::Structs.integer_template(4, false)
19
-
20
- assert_equal "n", Depix::Structs.integer_template(2, true)
21
- assert_equal "v", Depix::Structs.integer_template(2, false)
22
- end
23
-
24
- def test_struct_to_template
25
- one_int = [[:some, Integer, 4]]
26
- assert_equal [["some"], "N"], Depix::Structs.struct_to_template(one_int, true)
27
- assert_equal [["some"], "V"], Depix::Structs.struct_to_template(one_int, false)
28
-
29
- float = [[:afloat, Float, 4]]
30
- assert_equal [["afloat"], "g"], Depix::Structs.struct_to_template(float, true)
31
- assert_equal [["afloat"], "f"], Depix::Structs.struct_to_template(float, false)
32
-
33
- two_ints = [[:some, Integer, 4], [:another, Integer, 4]]
34
- assert_equal [["some", "another"], "NN"], Depix::Structs.struct_to_template(two_ints, true)
35
- assert_equal [["some", "another"], "VV"], Depix::Structs.struct_to_template(two_ints, false)
36
-
37
- two_ints = [[:some, Integer, 4], [:another, Integer, 2]]
38
- assert_equal [["some", "another"], "Nn"], Depix::Structs.struct_to_template(two_ints, true)
39
-
40
- two_ints = [[:some, Integer, 4], [:another, Integer, 2], [:str, String, 5]]
41
- assert_equal [["some", "another", "str"], "NnA5"], Depix::Structs.struct_to_template(two_ints, true)
42
-
43
- int_and_nest = [
44
- [:some, Integer, 4],
45
- [:another, [[:inner, String, 3]], 3]
46
- ]
47
- assert_equal [["some", "another.inner"], "NA3"], Depix::Structs.struct_to_template(int_and_nest, true)
48
- end
49
-
50
- def test_template_length
51
- assert_equal 2084, Depix::Structs::TEMPLATE_LENGTH
52
- end
53
-
54
- def test_dpx_info_present
55
- assert_nothing_raised { Depix::Structs.const_get(:DPX_INFO) }
56
- end
57
- end
58
-
59
- class EichTest < Test::Unit::TestCase
60
- def test_eich
61
- eich = Depix::H.new
62
- assert_nothing_raised { assert_not_nil eich.foo = 1 }
63
- assert_nothing_raised { eich.foo }
64
- assert_equal ['foo'], eich.keys
65
- assert_equal 1, eich.foo
66
- end
67
- end
68
-
69
4
  class ReaderTest < Test::Unit::TestCase
70
- def test_nestify
71
- k, v = ["foo", "bar"], [1, 2]
72
- assert_equal( {"foo"=>1, "bar"=> 2}, Depix::Reader.nestify(k,v))
73
-
74
- k, v = ["foo", "bar.baz"], [1, 2]
75
- assert_equal( {"foo"=>1, "bar"=> {"baz"=> 2}}, Depix::Reader.nestify(k,v))
76
-
77
- k, v = ["foo", "bar.baz", "bar.bam"], [1, 2, 3]
78
- assert_equal( {"foo"=>1, "bar"=> {"baz"=> 2, "bam" => 3}}, Depix::Reader.nestify(k,v))
79
-
80
- k, v = ["foo", "bar.baz.boo", "bar.baz.doo"], [1, 2, 3]
81
- assert_equal( {"foo"=>1, "bar"=> {"baz"=> {"boo" => 2, "doo" => 3}}}, Depix::Reader.nestify(k,v))
82
-
83
- k, v = ["foo", "bar.0", "bar.1"], [1, 2, 3]
84
- assert_equal( {"foo"=>1, "bar"=>{0=>2, 1=>3}}, Depix::Reader.nestify(k,v))
85
-
86
- k, v = ["foo", "bar.0.baz", "bar.0.dam"], [1, 2, 3]
87
- assert_equal( {"foo"=>1, "bar"=>{0=>{"dam"=>3, "baz"=>2}}}, Depix::Reader.nestify(k,v))
88
- end
5
+
6
+ SAMPLE_DPX = File.dirname(__FILE__) + '/samples/E012_P001_L000002_lin.0001.dpx'
89
7
 
90
8
  def test_parsed_properly
91
- file = 'samples/E012_P001_L000002_lin.0001.dpx'
92
- parsed = Depix::Reader.from_file(file)
9
+ file = SAMPLE_DPX
10
+ parsed = Depix.from_file(file)
93
11
  assert_equal "SDPX", parsed.file.magic
94
12
  assert_equal 8192, parsed.file.image_offset
95
13
  assert_equal "V1.0", parsed.file.version
@@ -121,7 +39,7 @@ class ReaderTest < Test::Unit::TestCase
121
39
  assert_equal 0, parsed.image.image_elements[0].end_of_line_padding
122
40
  assert_equal 0, parsed.image.image_elements[0].end_of_image_padding
123
41
  assert_equal "IMAGE DESCRIPTION DATA P", parsed.image.image_elements[0].description
124
- # assert_equal "E012x�", parsed.orientation.device - this is where Flame writes the reel
42
+ assert_equal "E012x\340\264\020\005", parsed.orientation.device #- this is where Flame writes the reel
125
43
  assert_equal 853, parsed.orientation.aspect_ratio[0]
126
44
  assert_equal 640, parsed.orientation.aspect_ratio[1]
127
45
 
@@ -141,11 +59,26 @@ class ReaderTest < Test::Unit::TestCase
141
59
  def test_syntethics
142
60
  assert_nothing_raised { Depix::Synthetics }
143
61
 
144
- file = 'samples/E012_P001_L000002_lin.0001.dpx'
145
- parsed = Depix::Reader.from_file(file)
62
+ file = SAMPLE_DPX
63
+ parsed = Depix.from_file(file)
64
+ assert_equal false, parsed.le?
146
65
  assert_equal "75 00 19 740612 9841", parsed.keycode
66
+ assert_equal "01:15:11:18", parsed.time_code.to_s
147
67
  assert_equal :RGB, parsed.component_type
148
68
  assert_equal :Linear, parsed.colorimetric
149
- assert_equal "E012", parsed.flame_reel
69
+ assert_equal "E012x", parsed.flame_reel
70
+ end
71
+
72
+ def test_parsed_properly_using_compact_structs
73
+ file = SAMPLE_DPX
74
+ assert_nothing_raised { Depix.from_file(file, compact = true) }
75
+ end
76
+
77
+ def test_describe
78
+ assert_nothing_raised do
79
+ desc = Depix.describe_file(SAMPLE_DPX)
80
+ assert_match(/320/, desc)
81
+ assert_match(/Offset to data for this image element/, desc)
82
+ end
150
83
  end
151
84
  end