depix 1.0.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/DPX_HEADER_STRUCTURE.txt +100 -0
- data/History.txt +6 -0
- data/Manifest.txt +14 -0
- data/README.txt +57 -0
- data/Rakefile +14 -0
- data/bin/depix-describe +8 -0
- data/lib/depix.rb +123 -0
- data/lib/depix/struct_explainer.rb +62 -0
- data/lib/depix/structs.rb +259 -0
- data/test/samples/E012_P001_L000002_lin.0001.dpx +0 -0
- data/test/samples/E012_P001_L000002_lin.0002.dpx +0 -0
- data/test/samples/E012_P001_L000002_log.0001.dpx +0 -0
- data/test/samples/E012_P001_L000002_log.0002.dpx +0 -0
- data/test/test_depix.rb +87 -0
- metadata +80 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
= DPX header structure description
|
2
|
+
|
3
|
+
DPX metadata gets returned as a hash containing other nested hashes. You can address hash keys by symbol, string
|
4
|
+
and method name
|
5
|
+
|
6
|
+
meta.file.magic # same as meta[:file][:magic]
|
7
|
+
|
8
|
+
== Metadata structure
|
9
|
+
|
10
|
+
* <tt>file</tt> hash of
|
11
|
+
* <tt>magic</tt> String
|
12
|
+
* <tt>image_offset</tt> Integer
|
13
|
+
* <tt>version</tt> String
|
14
|
+
* <tt>file_size</tt> Integer
|
15
|
+
* <tt>ditto_key</tt> Integer
|
16
|
+
* <tt>generic_size</tt> Integer
|
17
|
+
* <tt>industry_size</tt> Integer
|
18
|
+
* <tt>user_size</tt> Integer
|
19
|
+
* <tt>filename</tt> String
|
20
|
+
* <tt>timestamp</tt> String
|
21
|
+
* <tt>creator</tt> String
|
22
|
+
* <tt>project</tt> String
|
23
|
+
* <tt>copyright</tt> String
|
24
|
+
* <tt>encrypt_key</tt> Integer
|
25
|
+
* <tt>reserve</tt> String
|
26
|
+
* <tt>image</tt> hash of
|
27
|
+
* <tt>orientation</tt> Integer
|
28
|
+
* <tt>number_elements</tt> Integer
|
29
|
+
* <tt>pixels_per_line</tt> Integer
|
30
|
+
* <tt>lines_per_element</tt> Integer
|
31
|
+
* <tt>image_elements</tt> (array , 8 members):
|
32
|
+
* <tt>data_sign</tt> Integer
|
33
|
+
* <tt>low_data</tt> Integer
|
34
|
+
* <tt>low_quantity</tt> Float
|
35
|
+
* <tt>high_data</tt> Integer
|
36
|
+
* <tt>high_quantity</tt> Float
|
37
|
+
* <tt>descriptor</tt> String
|
38
|
+
* <tt>transfer</tt> String
|
39
|
+
* <tt>colorimetric</tt> String
|
40
|
+
* <tt>bit_size</tt> String
|
41
|
+
* <tt>packing</tt> Integer
|
42
|
+
* <tt>encoding</tt> Integer
|
43
|
+
* <tt>data_offset</tt> Integer
|
44
|
+
* <tt>end_of_line_padding</tt> Integer
|
45
|
+
* <tt>end_of_image_padding</tt> Integer
|
46
|
+
* <tt>description</tt> String
|
47
|
+
* <tt>reserve</tt> String
|
48
|
+
* <tt>orientation</tt> hash of
|
49
|
+
* <tt>x_offset</tt> Integer
|
50
|
+
* <tt>y_offset</tt> Integer
|
51
|
+
* <tt>x_center</tt> Float
|
52
|
+
* <tt>y_center</tt> Float
|
53
|
+
* <tt>x_size</tt> Integer
|
54
|
+
* <tt>y_size</tt> Integer
|
55
|
+
* <tt>filename</tt> String
|
56
|
+
* <tt>timestamp</tt> String
|
57
|
+
* <tt>device</tt> String
|
58
|
+
* <tt>serial</tt> String
|
59
|
+
* <tt>border</tt> (array , 4 members):
|
60
|
+
* <tt></tt> Integer
|
61
|
+
* <tt>aspect_ratio</tt> (array , 2 members):
|
62
|
+
* <tt></tt> Integer
|
63
|
+
* <tt>reserve</tt> String
|
64
|
+
* <tt>film</tt> hash of
|
65
|
+
* <tt>id</tt> String
|
66
|
+
* <tt>type</tt> String
|
67
|
+
* <tt>offset</tt> String
|
68
|
+
* <tt>prefix</tt> String
|
69
|
+
* <tt>count</tt> String
|
70
|
+
* <tt>format</tt> String
|
71
|
+
* <tt>frame_position</tt> Integer
|
72
|
+
* <tt>sequence_extent</tt> Integer
|
73
|
+
* <tt>held_count</tt> Integer
|
74
|
+
* <tt>frame_rate</tt> Float
|
75
|
+
* <tt>shutter_angle</tt> Float
|
76
|
+
* <tt>frame_id</tt> String
|
77
|
+
* <tt>slate</tt> String
|
78
|
+
* <tt>reserve</tt> String
|
79
|
+
* <tt>television</tt> hash of
|
80
|
+
* <tt>time_code</tt> Integer
|
81
|
+
* <tt>user_bits</tt> Integer
|
82
|
+
* <tt>interlace</tt> String
|
83
|
+
* <tt>field_number</tt> String
|
84
|
+
* <tt>video_signal</tt> String
|
85
|
+
* <tt>padding</tt> String
|
86
|
+
* <tt>horizontal_sample_rate</tt> Float
|
87
|
+
* <tt>vertical_sample_rate</tt> Float
|
88
|
+
* <tt>frame_rate</tt> Float
|
89
|
+
* <tt>time_offset</tt> Float
|
90
|
+
* <tt>gamma</tt> Float
|
91
|
+
* <tt>black_level</tt> Float
|
92
|
+
* <tt>black_gain</tt> Float
|
93
|
+
* <tt>break_point</tt> Float
|
94
|
+
* <tt>white_level</tt> Float
|
95
|
+
* <tt>integration_times</tt> Float
|
96
|
+
* <tt>reserve</tt> String
|
97
|
+
* <tt>user</tt> hash of
|
98
|
+
* <tt>id</tt> String
|
99
|
+
* <tt>user_data</tt> Integer
|
100
|
+
|
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
DPX_HEADER_STRUCTURE.txt
|
5
|
+
Rakefile
|
6
|
+
bin/depix-describe
|
7
|
+
lib/depix.rb
|
8
|
+
lib/depix/struct_explainer.rb
|
9
|
+
lib/depix/structs.rb
|
10
|
+
test/test_depix.rb
|
11
|
+
test/samples/E012_P001_L000002_lin.0001.dpx
|
12
|
+
test/samples/E012_P001_L000002_lin.0002.dpx
|
13
|
+
test/samples/E012_P001_L000002_log.0001.dpx
|
14
|
+
test/samples/E012_P001_L000002_log.0002.dpx
|
data/README.txt
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
= depix
|
2
|
+
|
3
|
+
* http://rubyforge.org/julik/depix
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Read DPX file metadata
|
8
|
+
|
9
|
+
== SYNOPSIS:
|
10
|
+
|
11
|
+
meta = Depix::Reader.new.from_file(dpx_file_path)
|
12
|
+
puts meta.television.time_code #=> 10:00:00:02
|
13
|
+
|
14
|
+
The data returned is described in the DPX_HEADER_STRUCTURE[link:files/DPX_HEADER_STRUCTURE_txt.html]. The structs
|
15
|
+
used for actual parsing are in the Depix::Structs module (but in a much less readable form, obviously)
|
16
|
+
|
17
|
+
The gem also contains an executable called depix-desribe which can be used from the command line
|
18
|
+
|
19
|
+
$book depix-describe 001_PTAPE_001.001.dpx
|
20
|
+
|
21
|
+
== NOTES:
|
22
|
+
|
23
|
+
The reader tries to be efficient - fast Ruby unpacking is used, some shortcuts are taken. Also don't worry - we do not need to read
|
24
|
+
the whole DPX file (which usually is around 8mb per frame) to know the details
|
25
|
+
|
26
|
+
== REQUIREMENTS:
|
27
|
+
|
28
|
+
* timecode gem (sudo gem install timecode)
|
29
|
+
|
30
|
+
== INSTALL:
|
31
|
+
|
32
|
+
* sudo gem install depix
|
33
|
+
|
34
|
+
== LICENSE:
|
35
|
+
|
36
|
+
(The MIT License)
|
37
|
+
|
38
|
+
Copyright (c) 2008 Julik Tarkhanov
|
39
|
+
|
40
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
41
|
+
a copy of this software and associated documentation files (the
|
42
|
+
'Software'), to deal in the Software without restriction, including
|
43
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
44
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
45
|
+
permit persons to whom the Software is furnished to do so, subject to
|
46
|
+
the following conditions:
|
47
|
+
|
48
|
+
The above copyright notice and this permission notice shall be
|
49
|
+
included in all copies or substantial portions of the Software.
|
50
|
+
|
51
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
52
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
53
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
54
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
55
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
56
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
57
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hoe'
|
3
|
+
require './lib/depix.rb'
|
4
|
+
|
5
|
+
Hoe.new('depix', Depix::VERSION) do |p|
|
6
|
+
p.developer('Julik Tarkhanov', 'me@julik.nl')
|
7
|
+
p.rubyforge_name = 'wiretap'
|
8
|
+
p.extra_deps.reject! {|e| e[0] == 'hoe' }
|
9
|
+
end
|
10
|
+
|
11
|
+
task :describe_structs do
|
12
|
+
require File.dirname(__FILE__) + '/lib/depix/struct_explainer'
|
13
|
+
File.open('DPX_HEADER_STRUCTURE.txt', 'w') {|f| f << RdocExplainer.new.get_rdoc_for(Depix::Structs::DPX_INFO) }
|
14
|
+
end
|
data/bin/depix-describe
ADDED
data/lib/depix.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'timecode'
|
4
|
+
|
5
|
+
require File.dirname(__FILE__) + '/depix/structs'
|
6
|
+
|
7
|
+
module Depix
|
8
|
+
VERSION = '1.0.0'
|
9
|
+
BLANK_4, BLANK_2 = 0xFFFFFFFF, 0xFFFF
|
10
|
+
|
11
|
+
# Methodic hash - stolen from Camping
|
12
|
+
class H < Hash
|
13
|
+
# Gets or sets keys in the hash.
|
14
|
+
#
|
15
|
+
# @cookies.my_favorite = :macadamian
|
16
|
+
# @cookies.my_favorite
|
17
|
+
# => :macadamian
|
18
|
+
#
|
19
|
+
def method_missing(m,*a)
|
20
|
+
m.to_s=~/=$/?self[$`]=a[0]:a==[]? (self.key?(m.to_sym) ? self[m.to_sym] : super ) : super
|
21
|
+
end
|
22
|
+
undef id, type
|
23
|
+
end
|
24
|
+
|
25
|
+
# Reads the metadata
|
26
|
+
class Reader
|
27
|
+
|
28
|
+
class << self
|
29
|
+
# Read the header from file (no worries, only the needed number of bytes will be read into memory). Returns a H with the metadata.
|
30
|
+
def from_file(path)
|
31
|
+
new.from_file(path)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Read the metadata from an in-memory string. Returns a H with the metadata.
|
35
|
+
def from_string(str)
|
36
|
+
new.from_string(str)
|
37
|
+
end
|
38
|
+
|
39
|
+
def describe_string(str)
|
40
|
+
reader = new
|
41
|
+
result = reader.deep_parse(str, Structs::DPX_INFO)
|
42
|
+
reader.inform(result)
|
43
|
+
end
|
44
|
+
|
45
|
+
def describe_file(path)
|
46
|
+
header = File.open(path, 'r') { |f| f.read(Structs::TEMPLATE_LENGTH) }
|
47
|
+
describe_string(header)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#:stopdoc:
|
52
|
+
def from_file(path)
|
53
|
+
header = File.open(path, 'r') { |f| f.read(Structs::TEMPLATE_LENGTH) }
|
54
|
+
from_string(header)
|
55
|
+
end
|
56
|
+
|
57
|
+
def from_string(str) #:nodoc:
|
58
|
+
wrap(deep_parse(str, Structs::DPX_INFO))
|
59
|
+
end
|
60
|
+
|
61
|
+
def deep_parse(data, structure)
|
62
|
+
magic = data[0..3]
|
63
|
+
template = (magic == "SDPX") ? Structs::TEMPLATE_BE : Structs::TEMPLATE_LE
|
64
|
+
|
65
|
+
result = data.unpack(template).map do |e|
|
66
|
+
case e
|
67
|
+
when String
|
68
|
+
clean = unpad(e)
|
69
|
+
clean.empty? ? nil : clean
|
70
|
+
when Integer
|
71
|
+
(e == BLANK_2 || e == BLANK_4) ? nil : e
|
72
|
+
when Float
|
73
|
+
e.nan? ? nil : e
|
74
|
+
end
|
75
|
+
end
|
76
|
+
result
|
77
|
+
end
|
78
|
+
|
79
|
+
def inform(result)
|
80
|
+
Structs::TEMPLATE_KEYS.zip(result).map{|k, v| "#{k}:#{v}" unless v.nil? }.compact.join("\n")
|
81
|
+
end
|
82
|
+
|
83
|
+
def wrap(result)
|
84
|
+
self.class.nestify(Structs::TEMPLATE_KEYS, result)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.nestify(keys, values)
|
88
|
+
auto_hash = H.new do |h,k|
|
89
|
+
h[k] = H.new(&h.default_proc)
|
90
|
+
end
|
91
|
+
|
92
|
+
keys.each_with_index do |path, idx |
|
93
|
+
value = values[idx]
|
94
|
+
|
95
|
+
sub, elems = auto_hash, path.split('.')
|
96
|
+
while elems.any?
|
97
|
+
dir = elems.shift
|
98
|
+
dir = dir.to_i if dir =~ /^(\d+)$/
|
99
|
+
elems.any? ? (sub = sub[dir]) : (sub[dir] = value)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
auto_hash
|
104
|
+
end
|
105
|
+
|
106
|
+
def unpad(string) # :nodoc:
|
107
|
+
string.gsub(0xFF.chr, '').gsub(0xFF.chr, '')
|
108
|
+
end
|
109
|
+
|
110
|
+
TIME_FIELDS = 7 # :nodoc:
|
111
|
+
|
112
|
+
def uint_to_tc(timestamp) # :nodoc:
|
113
|
+
shift = 4 * TIME_FIELDS;
|
114
|
+
tc_elements = (0..TIME_FIELDS).map do
|
115
|
+
part = ((timestamp >> shift) & 0x0F)
|
116
|
+
shift -= 4
|
117
|
+
part
|
118
|
+
end.join.scan(/(\d{2})/).flatten.map{|e| e.to_i}
|
119
|
+
|
120
|
+
Timecode.at(*tc_elements)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Generates an RDoc description of the DPX structs from the structs.rb file
|
2
|
+
class Formatter #:nodoc:
|
3
|
+
attr_accessor :io, :attr_template, :struct_template
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@io = STDOUT
|
7
|
+
@attr_template = "%s%s (%s)"
|
8
|
+
@struct_template = "%s%s (which is a hash with the following elements):"
|
9
|
+
@array_template = "%s%s (array , %d members):"
|
10
|
+
@padding = ' '
|
11
|
+
end
|
12
|
+
|
13
|
+
def explain_struct(struct, padding = '') #:nodoc:
|
14
|
+
struct.each do | e |
|
15
|
+
key, cast, len = e
|
16
|
+
if cast.is_a?(Depix::Structs::Struct)
|
17
|
+
@io.puts( @struct_template % [padding, key, len])
|
18
|
+
explain_struct(cast, padding + @padding)
|
19
|
+
elsif cast.is_a?(Array) # Repeats
|
20
|
+
@io.puts( @array_template % [padding, key, cast.size])
|
21
|
+
inner_struct = cast[0]
|
22
|
+
ikey, icast, ilen = inner_struct
|
23
|
+
if icast.is_a?(Depix::Structs::Struct)
|
24
|
+
explain_struct(icast, padding + @padding)
|
25
|
+
else
|
26
|
+
@io.puts( @attr_template % [padding, '', icast, ilen])
|
27
|
+
end
|
28
|
+
else
|
29
|
+
@io.puts( @attr_template % [padding, key, cast, len])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class RdocExplainer < Formatter #:nodoc:
|
36
|
+
TPL = <<eof
|
37
|
+
= DPX header structure description
|
38
|
+
|
39
|
+
DPX metadata gets returned as a hash containing other nested hashes. You can address hash keys by symbol, string
|
40
|
+
and method name
|
41
|
+
|
42
|
+
meta.file.magic # same as meta[:file][:magic]
|
43
|
+
|
44
|
+
== Metadata structure
|
45
|
+
|
46
|
+
%s
|
47
|
+
eof
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
super
|
51
|
+
@attr_template = "%s* <tt>%s</tt> %s"
|
52
|
+
@struct_template = "%s* <tt>%s</tt> hash of"
|
53
|
+
@array_template = "%s* <tt>%s</tt> (array , %d members):"
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_rdoc_for(struct)
|
58
|
+
@io = StringIO.new
|
59
|
+
explain_struct(Depix::Structs::DPX_INFO)
|
60
|
+
TPL % @io.string
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
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
|
+
#
|
8
|
+
# It's very fragile - in the world of C, everything is fixed length. If Tolstoy wanted to write
|
9
|
+
# "War and Peace" in C he would need to know the number of letters ahead. It has good and bad
|
10
|
+
# qualities - the good ones being computers go faster like that. The rest are bad parts.
|
11
|
+
module Structs
|
12
|
+
|
13
|
+
COLORIMETRIC = {
|
14
|
+
:UserDefined => 0,
|
15
|
+
:PrintingDensity => 1,
|
16
|
+
:Linear => 2,
|
17
|
+
:Logarithmic => 3,
|
18
|
+
:UnspecifiedVideo => 4,
|
19
|
+
:SMTPE_274M => 5,
|
20
|
+
:ITU_R709 => 6,
|
21
|
+
:ITU_R601_625L => 7,
|
22
|
+
:ITU_R601_525L => 8,
|
23
|
+
:NTSCCompositeVideo => 9,
|
24
|
+
:PALCompositeVideo => 10,
|
25
|
+
:ZDepthLinear => 11,
|
26
|
+
:DepthHomogeneous => 12
|
27
|
+
}
|
28
|
+
|
29
|
+
COMPONENT_TYPE = {
|
30
|
+
:Undefined => 0,
|
31
|
+
:Red => 1,
|
32
|
+
:Green => 2,
|
33
|
+
:Blue => 3,
|
34
|
+
:Alpha => 4,
|
35
|
+
:Luma => 6,
|
36
|
+
:ColorDifferenceCbCr => 7,
|
37
|
+
:Depth => 8,
|
38
|
+
:CompositeVideo => 9,
|
39
|
+
:RGB => 50,
|
40
|
+
:RGBA => 51,
|
41
|
+
:ABGR => 52,
|
42
|
+
:CbYCrY422 => 100,
|
43
|
+
:CbYACrYA4224 => 101,
|
44
|
+
:CbYCr444 => 102,
|
45
|
+
:CbYCrA4444 => 103,
|
46
|
+
:UserDef2Element => 150,
|
47
|
+
:UserDef3Element => 151,
|
48
|
+
:UserDef4Element => 152,
|
49
|
+
:UserDef5Element => 153,
|
50
|
+
:UserDef6Element => 154,
|
51
|
+
:UserDef7Element => 155,
|
52
|
+
:UserDef8Element => 156,
|
53
|
+
}
|
54
|
+
|
55
|
+
#:stopdoc:
|
56
|
+
|
57
|
+
# To avoid fucking up with sizes afterwards
|
58
|
+
UINT, FLOAT, USHORT, UCHAR = 4, 4, 2, 1
|
59
|
+
|
60
|
+
def self.struct_size(struct_const) #:nodoc:
|
61
|
+
struct_const.inject(0){| s, e | s + e[2]}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Used to distinguish structs from repeated values
|
65
|
+
class Struct < Array; end
|
66
|
+
|
67
|
+
|
68
|
+
FILE_INFO = Struct[
|
69
|
+
[:magic, String, 4],
|
70
|
+
[:image_offset, Integer, UINT],
|
71
|
+
|
72
|
+
[:version, String, 8],
|
73
|
+
|
74
|
+
[:file_size, Integer, UINT],
|
75
|
+
[:ditto_key, Integer, UINT],
|
76
|
+
[:generic_size, Integer, UINT],
|
77
|
+
[:industry_size, Integer, UINT],
|
78
|
+
[:user_size, Integer, UINT],
|
79
|
+
|
80
|
+
[:filename, String, 100],
|
81
|
+
[:timestamp, String, 24],
|
82
|
+
[:creator, String, 100],
|
83
|
+
[:project, String, 200],
|
84
|
+
[:copyright, String, 200],
|
85
|
+
|
86
|
+
[:encrypt_key, Integer, UINT],
|
87
|
+
[:reserve, String, 104],
|
88
|
+
]
|
89
|
+
|
90
|
+
FILM_INFO = Struct[
|
91
|
+
[:id, String, 2],
|
92
|
+
[:type, String, 2],
|
93
|
+
[:offset, String, 2],
|
94
|
+
[:prefix, String, 6],
|
95
|
+
[:count, String, 4],
|
96
|
+
[:format, String, 32],
|
97
|
+
|
98
|
+
[:frame_position, Integer, UINT],
|
99
|
+
[:sequence_extent, Integer, UINT],
|
100
|
+
[:held_count, Integer, UINT],
|
101
|
+
|
102
|
+
[:frame_rate, Float, FLOAT],
|
103
|
+
[:shutter_angle, Float, FLOAT],
|
104
|
+
|
105
|
+
[:frame_id, String, 32],
|
106
|
+
[:slate, String, 100],
|
107
|
+
[:reserve, String, 56],
|
108
|
+
]
|
109
|
+
|
110
|
+
|
111
|
+
IMAGE_ELEMENT = Struct[
|
112
|
+
[:data_sign, Integer, UINT],
|
113
|
+
[:low_data, Integer, UINT],
|
114
|
+
[:low_quantity, Float, FLOAT],
|
115
|
+
[:high_data, Integer, UINT],
|
116
|
+
[:high_quantity, Float, FLOAT],
|
117
|
+
|
118
|
+
# TODO: Autoreplace with enum values. Note: with these we will likely be addressing the enums
|
119
|
+
[:descriptor, String, UCHAR],
|
120
|
+
[:transfer, String, UCHAR],
|
121
|
+
[:colorimetric, String, UCHAR],
|
122
|
+
[:bit_size, String, UCHAR],
|
123
|
+
|
124
|
+
[:packing, Integer, USHORT],
|
125
|
+
[:encoding, Integer, USHORT],
|
126
|
+
[:data_offset, Integer, UINT],
|
127
|
+
[:end_of_line_padding, Integer, UINT],
|
128
|
+
[:end_of_image_padding, Integer, UINT],
|
129
|
+
[:description, String, 32],
|
130
|
+
]
|
131
|
+
|
132
|
+
IMAGE_ELEMENTS = (0..7).map{|e| [e, IMAGE_ELEMENT, struct_size(IMAGE_ELEMENT)] }
|
133
|
+
|
134
|
+
IMAGE_INFO = Struct[
|
135
|
+
[:orientation, Integer, USHORT],
|
136
|
+
[:number_elements, Integer, USHORT],
|
137
|
+
|
138
|
+
[:pixels_per_line, Integer, UINT],
|
139
|
+
[:lines_per_element, Integer, UINT],
|
140
|
+
|
141
|
+
[:image_elements, IMAGE_ELEMENTS, struct_size(IMAGE_ELEMENTS) ],
|
142
|
+
|
143
|
+
[:reserve, String, 52],
|
144
|
+
]
|
145
|
+
|
146
|
+
BORDER = (0..3).map{|s| [s, Integer, USHORT] }
|
147
|
+
|
148
|
+
ASPECT_RATIO = [
|
149
|
+
[0, Integer, UINT],
|
150
|
+
[1, Integer, UINT],
|
151
|
+
]
|
152
|
+
|
153
|
+
ORIENTATION_INFO = Struct[
|
154
|
+
|
155
|
+
[:x_offset, Integer, UINT],
|
156
|
+
[:y_offset, Integer, UINT],
|
157
|
+
|
158
|
+
[:x_center, Float, FLOAT],
|
159
|
+
[:y_center, Float, FLOAT],
|
160
|
+
|
161
|
+
[:x_size, Integer, UINT],
|
162
|
+
[:y_size, Integer, UINT],
|
163
|
+
|
164
|
+
[:filename, String, 100],
|
165
|
+
[:timestamp, String, 24],
|
166
|
+
[:device, String, 32],
|
167
|
+
[:serial, String, 32],
|
168
|
+
|
169
|
+
[:border, BORDER, struct_size(BORDER)],
|
170
|
+
[:aspect_ratio, ASPECT_RATIO, struct_size(ASPECT_RATIO)],
|
171
|
+
|
172
|
+
[:reserve, String, 28],
|
173
|
+
]
|
174
|
+
|
175
|
+
TELEVISION_INFO = Struct[
|
176
|
+
[:time_code, Integer, UINT],
|
177
|
+
[:user_bits, Integer, UINT],
|
178
|
+
|
179
|
+
[:interlace, String, UCHAR],
|
180
|
+
[:field_number, String, UCHAR],
|
181
|
+
[:video_signal, String, UCHAR],
|
182
|
+
[:padding, String, UCHAR],
|
183
|
+
|
184
|
+
[:horizontal_sample_rate, Float, FLOAT],
|
185
|
+
[:vertical_sample_rate, Float, FLOAT],
|
186
|
+
[:frame_rate, Float, FLOAT],
|
187
|
+
[:time_offset, Float, FLOAT],
|
188
|
+
[:gamma, Float, FLOAT],
|
189
|
+
[:black_level, Float, FLOAT],
|
190
|
+
[:black_gain, Float, FLOAT],
|
191
|
+
[:break_point, Float, FLOAT],
|
192
|
+
[:white_level, Float, FLOAT],
|
193
|
+
[:integration_times, Float, FLOAT],
|
194
|
+
[:reserve, String, 76],
|
195
|
+
]
|
196
|
+
|
197
|
+
USER_INFO = Struct[
|
198
|
+
[:id, String, 32],
|
199
|
+
[:user_data, Integer, UINT],
|
200
|
+
]
|
201
|
+
|
202
|
+
DPX_INFO = Struct[
|
203
|
+
[:file, FILE_INFO, struct_size(FILE_INFO)],
|
204
|
+
[:image, IMAGE_INFO, struct_size(IMAGE_INFO)],
|
205
|
+
[:orientation, ORIENTATION_INFO, struct_size(ORIENTATION_INFO)],
|
206
|
+
[:film, FILM_INFO, struct_size(FILM_INFO)],
|
207
|
+
[:television, TELEVISION_INFO, struct_size(TELEVISION_INFO)],
|
208
|
+
[:user, USER_INFO, struct_size(USER_INFO)],
|
209
|
+
]
|
210
|
+
|
211
|
+
# Converts the nexted structs to one template that can be fed to Ruby pack/unpack. This yields
|
212
|
+
# some impressive performance improvements (about 1.4 times faster) over reading fields bytewise
|
213
|
+
def self.struct_to_template(struct, big_endian)
|
214
|
+
keys, template = [], ''
|
215
|
+
struct.each do | elem |
|
216
|
+
key, cast, size = elem
|
217
|
+
pattern = case true
|
218
|
+
when cast.is_a?(Struct) # Nested structs
|
219
|
+
inner_keys, inner_template = struct_to_template(cast, big_endian)
|
220
|
+
# Use a dot as a divider. We will detect it later on and merge into nested hashes
|
221
|
+
keys += inner_keys.map{|k| [key, k].join('.') }
|
222
|
+
inner_template
|
223
|
+
when cast.is_a?(Array) # Repeat values
|
224
|
+
inner_keys, inner_template = struct_to_template(cast, big_endian)
|
225
|
+
# Use a dot as a divider. We will detect it later on and merge into nested hashes
|
226
|
+
keys += inner_keys.map{|k| [key, k].join('.') }
|
227
|
+
inner_template
|
228
|
+
when cast == Integer || cast == Timecode
|
229
|
+
keys << key.to_s
|
230
|
+
integer_template(size, big_endian)
|
231
|
+
when cast == String
|
232
|
+
keys << key.to_s
|
233
|
+
"A#{size}"
|
234
|
+
when cast == Float
|
235
|
+
keys << key.to_s
|
236
|
+
big_endian ? "g" : "f"
|
237
|
+
end
|
238
|
+
|
239
|
+
template << pattern
|
240
|
+
end
|
241
|
+
[keys, template]
|
242
|
+
end
|
243
|
+
|
244
|
+
def self.integer_template(size, big_endian) #:nodoc:
|
245
|
+
if size == 2
|
246
|
+
big_endian ? "n" : "v"
|
247
|
+
elsif size == 4
|
248
|
+
big_endian ? "N" : "V"
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# Shortcuts used to speed up parsing
|
253
|
+
TEMPLATE_KEYS, TEMPLATE_BE = struct_to_template(DPX_INFO, true)
|
254
|
+
TEMPLATE_LE = struct_to_template(DPX_INFO, false)
|
255
|
+
TEMPLATE_LENGTH = struct_size(DPX_INFO)
|
256
|
+
|
257
|
+
#:startdoc:
|
258
|
+
end
|
259
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/test/test_depix.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/depix'
|
2
|
+
require 'test/unit'
|
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 ReaderTest < Test::Unit::TestCase
|
60
|
+
def test_nestify
|
61
|
+
k, v = ["foo", "bar"], [1, 2]
|
62
|
+
assert_equal( {"foo"=>1, "bar"=> 2}, Depix::Reader.nestify(k,v))
|
63
|
+
|
64
|
+
k, v = ["foo", "bar.baz"], [1, 2]
|
65
|
+
assert_equal( {"foo"=>1, "bar"=> {"baz"=> 2}}, Depix::Reader.nestify(k,v))
|
66
|
+
|
67
|
+
k, v = ["foo", "bar.baz", "bar.bam"], [1, 2, 3]
|
68
|
+
assert_equal( {"foo"=>1, "bar"=> {"baz"=> 2, "bam" => 3}}, Depix::Reader.nestify(k,v))
|
69
|
+
|
70
|
+
k, v = ["foo", "bar.baz.boo", "bar.baz.doo"], [1, 2, 3]
|
71
|
+
assert_equal( {"foo"=>1, "bar"=> {"baz"=> {"boo" => 2, "doo" => 3}}}, Depix::Reader.nestify(k,v))
|
72
|
+
|
73
|
+
k, v = ["foo", "bar.0", "bar.1"], [1, 2, 3]
|
74
|
+
assert_equal( {"foo"=>1, "bar"=>{0=>2, 1=>3}}, Depix::Reader.nestify(k,v))
|
75
|
+
|
76
|
+
k, v = ["foo", "bar.0.baz", "bar.0.dam"], [1, 2, 3]
|
77
|
+
assert_equal( {"foo"=>1, "bar"=>{0=>{"dam"=>3, "baz"=>2}}}, Depix::Reader.nestify(k,v))
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_parse
|
81
|
+
file = 'samples/E012_P001_L000002_lin.0001.dpx'
|
82
|
+
parsed = Depix::Reader.from_file(file)
|
83
|
+
assert_equal 'SDPX', parsed.file.magic
|
84
|
+
assert_equal 320, image.pixels_per_line
|
85
|
+
assert_equal 240, image.lines_per_element
|
86
|
+
end
|
87
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: depix
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Julik Tarkhanov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-12-19 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.8.2
|
24
|
+
version:
|
25
|
+
description: Read DPX file metadata
|
26
|
+
email:
|
27
|
+
- me@julik.nl
|
28
|
+
executables:
|
29
|
+
- depix-describe
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files:
|
33
|
+
- History.txt
|
34
|
+
- Manifest.txt
|
35
|
+
- README.txt
|
36
|
+
- DPX_HEADER_STRUCTURE.txt
|
37
|
+
files:
|
38
|
+
- History.txt
|
39
|
+
- Manifest.txt
|
40
|
+
- README.txt
|
41
|
+
- DPX_HEADER_STRUCTURE.txt
|
42
|
+
- Rakefile
|
43
|
+
- bin/depix-describe
|
44
|
+
- lib/depix.rb
|
45
|
+
- lib/depix/struct_explainer.rb
|
46
|
+
- lib/depix/structs.rb
|
47
|
+
- test/test_depix.rb
|
48
|
+
- test/samples/E012_P001_L000002_lin.0001.dpx
|
49
|
+
- test/samples/E012_P001_L000002_lin.0002.dpx
|
50
|
+
- test/samples/E012_P001_L000002_log.0001.dpx
|
51
|
+
- test/samples/E012_P001_L000002_log.0002.dpx
|
52
|
+
has_rdoc: true
|
53
|
+
homepage: http://rubyforge.org/julik/depix
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options:
|
56
|
+
- --main
|
57
|
+
- README.txt
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: "0"
|
71
|
+
version:
|
72
|
+
requirements: []
|
73
|
+
|
74
|
+
rubyforge_project: wiretap
|
75
|
+
rubygems_version: 1.3.1
|
76
|
+
signing_key:
|
77
|
+
specification_version: 2
|
78
|
+
summary: Read DPX file metadata
|
79
|
+
test_files:
|
80
|
+
- test/test_depix.rb
|