depix 1.0.1 → 1.0.2

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/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