exiftoolr 0.0.4
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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.md +75 -0
- data/Rakefile +5 -0
- data/exiftoolr.gemspec +26 -0
- data/lib/exiftoolr.rb +64 -0
- data/lib/exiftoolr/result.rb +47 -0
- data/lib/exiftoolr/version.rb +3 -0
- data/test/Canon 20D.jpg +0 -0
- data/test/Canon 20D.jpg.yaml +225 -0
- data/test/Droid X.jpg +0 -0
- data/test/Droid X.jpg.yaml +87 -0
- data/test/faces.jpg +0 -0
- data/test/faces.jpg.yaml +327 -0
- data/test/iPhone 4S.jpg +0 -0
- data/test/iPhone 4S.jpg.yaml +80 -0
- data/test/test_exiftoolr.rb +62 -0
- metadata +131 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# Ruby wrapper for ExifTool
|
2
|
+
|
3
|
+
This gem is the simplest thing that could possibly work that
|
4
|
+
reads the output of [exiftool](http://www.sno.phy.queensu.ca/~phil/exiftool)
|
5
|
+
and renders it into a ruby hash, with correctly typed values and symbolized keys.
|
6
|
+
|
7
|
+
## Want constitutes "correct"?
|
8
|
+
|
9
|
+
* GPS latitude and longitude are rendered as signed floats,
|
10
|
+
where north and east are positive, and west and south are negative.
|
11
|
+
* Values like shutter speed and exposure time are rendered as Rationals,
|
12
|
+
which lets the caller show them as fractions (1/250) or as comparable.
|
13
|
+
* String values like "interop" and "serial number" are kept as strings
|
14
|
+
(which preserves zero prefixes)
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
You'll want to [install ExifTool](http://www.sno.phy.queensu.ca/~phil/exiftool/install.html), then
|
19
|
+
|
20
|
+
```
|
21
|
+
gem install exiftoolr
|
22
|
+
```
|
23
|
+
|
24
|
+
or add to your Gemfile:
|
25
|
+
|
26
|
+
```
|
27
|
+
gem 'exiftoolr'
|
28
|
+
```
|
29
|
+
|
30
|
+
and run ```bundle```.
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
require 'exiftoolr'
|
36
|
+
e = Exiftoolr.new("path/to/iPhone 4S.jpg")
|
37
|
+
e.to_hash
|
38
|
+
# => {:make => "Apple", :gps_longitude => -122.47566667, …
|
39
|
+
e.to_display_hash
|
40
|
+
# => {"Make" => "Apple", "GPS Longitude" => -122.47566667, …
|
41
|
+
```
|
42
|
+
|
43
|
+
### Multiple file support
|
44
|
+
|
45
|
+
Supply an array to the Exiftoolr initializer, then use ```.result_for```:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
require 'exiftoolr'
|
49
|
+
e = Exiftoolr.new(Dir["**/*.jpg"])
|
50
|
+
result = e.result_for("path/to/iPhone 4S.jpg")
|
51
|
+
result.to_hash
|
52
|
+
# => {:make => "Apple", :gps_longitude => -122.47566667, …
|
53
|
+
result[:gps_longitude]
|
54
|
+
# => -122.47566667
|
55
|
+
|
56
|
+
e.files_with_results
|
57
|
+
# => ["path/to/iPhone 4S.jpg", "path/to/Droid X.jpg", …
|
58
|
+
```
|
59
|
+
|
60
|
+
### When things go wrong
|
61
|
+
|
62
|
+
* ```Exiftoolr::NoSuchFile``` is raised if the provided filename doesn't exist.
|
63
|
+
* ```Exiftoolr::ExiftoolNotInstalled``` is raised if ```exiftool``` isn't in your ```PATH```.
|
64
|
+
* If ExifTool has a problem reading EXIF data, no exception is raised, but ```#errors?``` will return true:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
Exiftoolr.new("Gemfile").errors?
|
68
|
+
#=> true
|
69
|
+
```
|
70
|
+
|
71
|
+
## Change history
|
72
|
+
|
73
|
+
### 0.0.4
|
74
|
+
|
75
|
+
Added support for multiple file fetching (which is *much* faster for large directories)
|
data/Rakefile
ADDED
data/exiftoolr.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "exiftoolr/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "exiftoolr"
|
7
|
+
s.version = Exiftoolr::VERSION
|
8
|
+
s.authors = ["Matthew McEachen"]
|
9
|
+
s.email = ["matthew-github@mceachen.org"]
|
10
|
+
s.homepage = "https://github.com/mceachen/exif_tooler"
|
11
|
+
s.summary = %q{Multiget ExifTool wrapper for ruby}
|
12
|
+
s.description = %q{Multiget ExifTool wrapper for ruby}
|
13
|
+
|
14
|
+
s.rubyforge_project = "exiftoolr"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.requirements << "ExifTool (see http://www.sno.phy.queensu.ca/~phil/exiftool/)"
|
22
|
+
|
23
|
+
s.add_dependency "json"
|
24
|
+
s.add_development_dependency "rake"
|
25
|
+
s.add_development_dependency "rspec"
|
26
|
+
end
|
data/lib/exiftoolr.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require "exiftoolr/version"
|
2
|
+
require "exiftoolr/result"
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
require 'shellwords'
|
6
|
+
|
7
|
+
class Exiftoolr
|
8
|
+
class NoSuchFile < StandardError; end
|
9
|
+
class NotAFile < StandardError; end
|
10
|
+
class ExiftoolNotInstalled < StandardError; end
|
11
|
+
|
12
|
+
def self.exiftool_installed?
|
13
|
+
`exiftool -ver 2> /dev/null`.to_f > 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.expand_path(filename)
|
17
|
+
raise NoSuchFile, filename unless File.exist?(filename)
|
18
|
+
raise NotAFile, filename unless File.file?(filename)
|
19
|
+
File.expand_path(filename)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(filenames, exiftool_opts = "")
|
23
|
+
escaped_filenames = filenames.to_a.collect do |f|
|
24
|
+
Shellwords.escape(self.class.expand_path(f))
|
25
|
+
end.join(" ")
|
26
|
+
json = `exiftool #{exiftool_opts} -j −coordFormat "%.8f" -dateFormat "%Y-%m-%d %H:%M:%S" #{escaped_filenames} 2> /dev/null`
|
27
|
+
raise ExiftoolNotInstalled if json == ""
|
28
|
+
@file2result = { }
|
29
|
+
JSON.parse(json).each do |raw|
|
30
|
+
@file2result[r.source_file] = Result.new(raw)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def result_for(filename)
|
35
|
+
@file2result[self.class.expand_path(filename)]
|
36
|
+
end
|
37
|
+
|
38
|
+
def files_with_results
|
39
|
+
@file2result.values.collect{|r|r.source_file unless r.errors?}.compact
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_hash
|
43
|
+
first.to_hash
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_display_hash
|
47
|
+
first.to_display_hash
|
48
|
+
end
|
49
|
+
|
50
|
+
def symbol_display_hash
|
51
|
+
first.symbol_display_hash
|
52
|
+
end
|
53
|
+
|
54
|
+
def errors?
|
55
|
+
@file2result.values.any? { |ea| ea.errors? }
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def first
|
61
|
+
raise InvalidArgument, "use #result_for when multiple filenames are used" if @file2result.size > 1
|
62
|
+
@file2result.values.first
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'rational'
|
3
|
+
|
4
|
+
class Exiftoolr
|
5
|
+
class Result
|
6
|
+
attr_reader :to_hash, :to_display_hash, :symbol_display_hash
|
7
|
+
|
8
|
+
WORD_BOUNDARY_RES = [/([A-Z\d]+)([A-Z][a-z])/, /([a-z\d])([A-Z])/]
|
9
|
+
FRACTION_RE = /\d+\/\d+/
|
10
|
+
|
11
|
+
def initialize(raw_hash)
|
12
|
+
@raw_hash = raw_hash
|
13
|
+
@to_hash = { }
|
14
|
+
@to_display_hash = { }
|
15
|
+
@symbol_display_hash = { }
|
16
|
+
|
17
|
+
@raw_hash.each do |k, v|
|
18
|
+
display_key = WORD_BOUNDARY_RES.inject(k) { |key, regex| key.gsub(regex, '\1 \2') }
|
19
|
+
sym_key = display_key.downcase.gsub(' ', '_').to_sym
|
20
|
+
if sym_key == :gps_latitude || sym_key == :gps_longitude
|
21
|
+
value, direction = v.split(" ")
|
22
|
+
v = value.to_f
|
23
|
+
v *= -1 if direction == 'S' || direction == 'W'
|
24
|
+
elsif display_key =~ /\bdate\b/i
|
25
|
+
v = Time.parse(v)
|
26
|
+
elsif v =~ FRACTION_RE
|
27
|
+
v = Rational(*v.split('/').collect { |ea| ea.to_i })
|
28
|
+
end
|
29
|
+
@to_hash[sym_key] = v
|
30
|
+
@to_display_hash[display_key] = v
|
31
|
+
@symbol_display_hash[sym_key] = display_key
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def [](key)
|
36
|
+
@to_hash[key]
|
37
|
+
end
|
38
|
+
|
39
|
+
def source_file
|
40
|
+
self[:source_file]
|
41
|
+
end
|
42
|
+
|
43
|
+
def errors?
|
44
|
+
self[:error] == "Unknown file type" || self[:warning] == "Unsupported file type"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/test/Canon 20D.jpg
ADDED
Binary file
|
@@ -0,0 +1,225 @@
|
|
1
|
+
---
|
2
|
+
:color_temp_custom2: 4792
|
3
|
+
:sensor_left_border: 84
|
4
|
+
:flash_activity: 0
|
5
|
+
:measured_rggb: 674 1024 1024 670
|
6
|
+
:photo_effect: "Off"
|
7
|
+
:digital_zoom: None
|
8
|
+
:af_points_in_focus: 4
|
9
|
+
:color_temp_shade: 7000
|
10
|
+
:y_cb_cr_sub_sampling: YCbCr4:2:2 (2 1)
|
11
|
+
:metering_mode: Evaluative
|
12
|
+
:image_size: 3504x2336
|
13
|
+
:canon_model_id: EOS 20D
|
14
|
+
:bracket_mode: "Off"
|
15
|
+
:camera_type: EOS Mid-range
|
16
|
+
:exposure_mode: Auto
|
17
|
+
:tone_curve: Standard
|
18
|
+
:af_area_x_positions: 8 -555 571 -931 8 947 -555 571 8
|
19
|
+
:color_tone: Normal
|
20
|
+
:af_image_width: 3504
|
21
|
+
:max_aperture: 5.6
|
22
|
+
:aeb_bracket_value: 0
|
23
|
+
:shutter_curtain_sync: 1st-curtain sync
|
24
|
+
:aeb_sequence_auto_cancel: 0,-,+/Enabled
|
25
|
+
:scale_factor35efl: 1.6
|
26
|
+
:lens_af_stop_button: AF stop
|
27
|
+
:add_original_decision_data: "Off"
|
28
|
+
:thumbnail_image: (Binary data 11259 bytes)
|
29
|
+
:flash_sync_speed_av: Auto
|
30
|
+
:black_mask_right_border: 0
|
31
|
+
:base_iso: 200
|
32
|
+
:image_width: 3504
|
33
|
+
:focal_plane_x_resolution: 2886.363636
|
34
|
+
:exposure_program: Program AE
|
35
|
+
:safety_shift_in_av_or_tv: Disable
|
36
|
+
:measured_ev: 13.25
|
37
|
+
:continuous_drive: Single
|
38
|
+
:red_balance: 1.943842
|
39
|
+
:file_name: Canon 20D.jpg
|
40
|
+
:white_balance_blue: 0
|
41
|
+
:lens: 17.0 - 85.0 mm
|
42
|
+
:digital_gain: 0
|
43
|
+
:macro_mode: Normal
|
44
|
+
:wb_rggb_levels_flash: 2187 1023 1023 1333
|
45
|
+
:menu_button_display_position: Previous (top if power off)
|
46
|
+
:sensor_height: 2360
|
47
|
+
:file_size: 2.9 MB
|
48
|
+
:black_mask_top_border: 0
|
49
|
+
:ettlii: Evaluative
|
50
|
+
:af_area_width: 78
|
51
|
+
:f_number: 9.0
|
52
|
+
:directory: test
|
53
|
+
:black_mask_bottom_border: 0
|
54
|
+
:focal_plane_y_size: 15.37 mm
|
55
|
+
:toning_effect: None
|
56
|
+
:af_area_y_positions: 504 270 270 4 4 4 -262 -262 -496
|
57
|
+
:focal_plane_y_resolution: 2885.805763
|
58
|
+
:target_aperture: 9
|
59
|
+
:focus_mode: One-shot AF
|
60
|
+
:mirror_lockup: Disable
|
61
|
+
:color_temp_flash: 6309
|
62
|
+
:make: Canon
|
63
|
+
:set_function_when_shooting: Image replay
|
64
|
+
:wb_bracket_value_gm: 0
|
65
|
+
:exposure_time: !ruby/object:Rational
|
66
|
+
denominator: 250
|
67
|
+
numerator: 1
|
68
|
+
:thumbnail_offset: 9728
|
69
|
+
:canon_flash_mode: "Off"
|
70
|
+
:serial_number: 0330113864
|
71
|
+
:fov: 15.4 deg
|
72
|
+
:thumbnail_image_valid_area: 0 159 7 112
|
73
|
+
:af_point_selection_method: Normal
|
74
|
+
:color_space: sRGB
|
75
|
+
:original_decision_data_offset: 0
|
76
|
+
:lens_id: Unknown 17-85mm
|
77
|
+
:shutter_speed: !ruby/object:Rational
|
78
|
+
denominator: 250
|
79
|
+
numerator: 1
|
80
|
+
:light_value: 13.3
|
81
|
+
:superimposed_display: "On"
|
82
|
+
:exposure_compensation: 0
|
83
|
+
:orientation: Horizontal (normal)
|
84
|
+
:components_configuration: Y, Cb, Cr, -
|
85
|
+
:"shutter-ae_lock": AF/AE lock
|
86
|
+
:focal_length: 85.0 mm
|
87
|
+
:focal_length35efl: "85.0 mm (35 mm equivalent: 132.8 mm)"
|
88
|
+
:bracket_shot_number: 0
|
89
|
+
:model: Canon EOS 20D
|
90
|
+
:aperture_value: 9.0
|
91
|
+
:flash_guide_number: 0
|
92
|
+
:long_exposure_noise_reduction2: "Off"
|
93
|
+
:focal_plane_x_size: 23.04 mm
|
94
|
+
:sequence_number: 0
|
95
|
+
:long_exposure_noise_reduction: "Off"
|
96
|
+
:white_balance: Auto
|
97
|
+
:file_type: JPEG
|
98
|
+
:canon_exposure_mode: Program AE
|
99
|
+
:interop_version: "0100"
|
100
|
+
:canon_image_width: 3504
|
101
|
+
:color_temp_fluorescent: 3952
|
102
|
+
:canon_firmware_version: Firmware 1.0.2
|
103
|
+
:sharpness: "+1"
|
104
|
+
:color_temp_auto: 4662
|
105
|
+
:af_assist_beam: Emits
|
106
|
+
:iso: 200
|
107
|
+
:black_mask_left_border: 0
|
108
|
+
:color_temp_custom1: 6163
|
109
|
+
:focal_plane_resolution_unit: inches
|
110
|
+
:wb_rggb_levels_custom1: 2095 1023 1023 1316
|
111
|
+
:flash_bits: (none)
|
112
|
+
:sensor_right_border: 3587
|
113
|
+
:bracket_value: 0
|
114
|
+
:canon_image_type: Canon EOS 20D
|
115
|
+
:exif_version: "0221"
|
116
|
+
:exif_image_width: 3504
|
117
|
+
:encoding_process: Baseline DCT, Huffman coding
|
118
|
+
:contrast: "+1"
|
119
|
+
:file_modify_date: 2012-02-19 21:49:48 -08:00
|
120
|
+
:white_balance_red: 0
|
121
|
+
:auto_rotate: None
|
122
|
+
:scene_capture_type: Standard
|
123
|
+
:compression: JPEG (old-style)
|
124
|
+
:sensor_red_level: 0
|
125
|
+
:control_mode: Camera Local Control
|
126
|
+
:sensor_width: 3596
|
127
|
+
:shooting_mode: Program AE
|
128
|
+
:lens_type: Unknown (-1)
|
129
|
+
:sensor_blue_level: 0
|
130
|
+
:file_permissions: rw-r--r--
|
131
|
+
:num_af_points: 9
|
132
|
+
:focus_continuous: Single
|
133
|
+
:color_temp_tungsten: 3196
|
134
|
+
:thumbnail_length: 11259
|
135
|
+
:target_exposure_time: !ruby/object:Rational
|
136
|
+
denominator: 251
|
137
|
+
numerator: 1
|
138
|
+
:valid_af_points: 9
|
139
|
+
:long_focal: 85 mm
|
140
|
+
:wb_rggb_levels_daylight: 1995 1023 1023 1509
|
141
|
+
:interop_index: R98 - DCF basic file (sRGB)
|
142
|
+
:canon_image_height: 2336
|
143
|
+
:create_date: 2004-09-19 12:25:20 -07:00
|
144
|
+
:wb_bracket_mode: "Off"
|
145
|
+
:focal_units: 1/mm
|
146
|
+
:serial_number_format: Format 2
|
147
|
+
:short_focal: 17 mm
|
148
|
+
:sensor_top_border: 19
|
149
|
+
:measured_ev2: 13.5
|
150
|
+
:image_height: 2336
|
151
|
+
:color_temp_as_shot: 4662
|
152
|
+
:flash: Off, Did not fire
|
153
|
+
:iso_expansion: "On"
|
154
|
+
:record_mode: JPEG
|
155
|
+
:flashpix_version: "0100"
|
156
|
+
:wb_rggb_levels_tungsten: 1410 1023 1023 2408
|
157
|
+
:blue_balance: 1.638424
|
158
|
+
:sensor_bottom_border: 2354
|
159
|
+
:circle_of_confusion: 0.019 mm
|
160
|
+
:exif_byte_order: Little-endian (Intel, II)
|
161
|
+
:custom_rendered: Normal
|
162
|
+
:drive_mode: Single-frame Shooting
|
163
|
+
:color_components: 3
|
164
|
+
:x_resolution: 72
|
165
|
+
:exif_image_height: 2336
|
166
|
+
:aperture: 9.0
|
167
|
+
:easy_mode: Manual
|
168
|
+
:nd_filter: n/a
|
169
|
+
:exposure_level_increments: !ruby/object:Rational
|
170
|
+
denominator: 3
|
171
|
+
numerator: 1
|
172
|
+
:auto_exposure_bracketing: "Off"
|
173
|
+
:af_area_height: 78
|
174
|
+
:flash_firing: Fires
|
175
|
+
:wb_rggb_levels_custom2: 1912 1023 1023 1627
|
176
|
+
:self_timer2: 0
|
177
|
+
:lens35efl: "17.0 - 85.0 mm (35 mm equivalent: 26.6 - 132.8 mm)"
|
178
|
+
:mime_type: image/jpeg
|
179
|
+
:wb_rggb_levels_shade: 2292 1023 1023 1267
|
180
|
+
:canon_image_size: Large
|
181
|
+
:hyperfocal_distance: 41.74 m
|
182
|
+
:wb_rggb_levels_auto: 1973 1023 1023 1663
|
183
|
+
:zoom_source_width: 0
|
184
|
+
:auto_iso: 100
|
185
|
+
:vrd_offset: 0
|
186
|
+
:wb_shift_gm: 0
|
187
|
+
:wb_rggb_levels_fluorescent: 1797 1023 1023 2058
|
188
|
+
:color_temperature: 4800
|
189
|
+
:wb_rggb_levels: 1973 1015 1015 1663
|
190
|
+
:wb_shift_ab: 0
|
191
|
+
:user_comment: ""
|
192
|
+
:zoom_target_width: 0
|
193
|
+
:quality: Fine
|
194
|
+
:color_temp_cloudy: 6000
|
195
|
+
:af_image_height: 2336
|
196
|
+
:manual_flash_output: n/a
|
197
|
+
:flash_exposure_comp: 0
|
198
|
+
:wb_rggb_levels_cloudy: 2160 1023 1023 1373
|
199
|
+
:source_file: test/Canon 20D.jpg
|
200
|
+
:color_temp_daylight: 5200
|
201
|
+
:sharpness_frequency: n/a
|
202
|
+
:slow_shutter: None
|
203
|
+
:modify_date: 2004-09-19 12:25:20 -07:00
|
204
|
+
:bulb_duration: 0
|
205
|
+
:bits_per_sample: 8
|
206
|
+
:focus_range: Not Known
|
207
|
+
:saturation: "+1"
|
208
|
+
:focal_type: Zoom
|
209
|
+
:wb_rggb_levels_as_shot: 1973 1015 1015 1663
|
210
|
+
:min_aperture: 32
|
211
|
+
:exif_tool_version: 8.59
|
212
|
+
:y_resolution: 72
|
213
|
+
:date_time_original: 2004-09-19 12:25:20 -07:00
|
214
|
+
:shutter_speed_value: !ruby/object:Rational
|
215
|
+
denominator: 250
|
216
|
+
numerator: 1
|
217
|
+
:filter_effect: None
|
218
|
+
:resolution_unit: inches
|
219
|
+
:y_cb_cr_positioning: Co-sited
|
220
|
+
:picture_style: None
|
221
|
+
:file_number: 793-9390
|
222
|
+
:self_timer: "Off"
|
223
|
+
:wb_bracket_value_ab: 0
|
224
|
+
:optical_zoom_code: n/a
|
225
|
+
:owner_name: unknown
|
data/test/Droid X.jpg
ADDED
Binary file
|
@@ -0,0 +1,87 @@
|
|
1
|
+
---
|
2
|
+
:exposure_compensation: 0
|
3
|
+
:model: DROIDX 6d460001fff80000015d7d330e01c011
|
4
|
+
:color_space: sRGB
|
5
|
+
:components_configuration: Cb, Y, Cr, Y
|
6
|
+
:exif_byte_order: Little-endian (Intel, II)
|
7
|
+
:sensing_method: One-chip color area
|
8
|
+
:create_date: 2010-12-08 01:15:37 -08:00
|
9
|
+
:bits_per_sample: 8
|
10
|
+
:date_time_original: 2010-12-08 01:15:37 -08:00
|
11
|
+
:source_file: test/Droid X.jpg
|
12
|
+
:thumbnail_length: 32586
|
13
|
+
:exif_image_width: 3264
|
14
|
+
:scene_type: Directly photographed
|
15
|
+
:contrast: Normal
|
16
|
+
:exposure_program: Program AE
|
17
|
+
:compression: JPEG (old-style)
|
18
|
+
:image_size: 3264x1840
|
19
|
+
:max_aperture_value: 2.8
|
20
|
+
:mime_type: image/jpeg
|
21
|
+
:x_resolution: 300
|
22
|
+
:interop_version: "0100"
|
23
|
+
:orientation: Rotate 90 CW
|
24
|
+
:software: 2.2
|
25
|
+
:thumbnail_offset: 2150
|
26
|
+
:shutter_speed: !ruby/object:Rational
|
27
|
+
denominator: 15
|
28
|
+
numerator: 1
|
29
|
+
:directory: test
|
30
|
+
:flashpix_version: "0100"
|
31
|
+
:y_cb_cr_positioning: Centered
|
32
|
+
:flash: Auto, Did not fire
|
33
|
+
:f_number: 2.8
|
34
|
+
:saturation: Normal
|
35
|
+
:file_modify_date: 2012-02-19 21:49:48 -08:00
|
36
|
+
:y_resolution: 300
|
37
|
+
:encoding_process: Baseline DCT, Huffman coding
|
38
|
+
:subject_distance: 0 m
|
39
|
+
:thumbnail_image: (Binary data 32586 bytes)
|
40
|
+
:white_balance: Auto
|
41
|
+
:metering_mode: Center-weighted average
|
42
|
+
:exposure_mode: Auto
|
43
|
+
:compressed_bits_per_pixel: 4
|
44
|
+
:circle_of_confusion: 0.004 mm
|
45
|
+
:color_components: 3
|
46
|
+
:image_height: 1840
|
47
|
+
:exif_tool_version: 8.59
|
48
|
+
:subject_distance_range: Unknown
|
49
|
+
:exposure_time: !ruby/object:Rational
|
50
|
+
denominator: 15
|
51
|
+
numerator: 1
|
52
|
+
:resolution_unit: inches
|
53
|
+
:scene_capture_type: Standard
|
54
|
+
:file_name: Droid X.jpg
|
55
|
+
:image_unique_id: ""
|
56
|
+
:hyperfocal_distance: 2.24 m
|
57
|
+
:focal_length35efl: "5.0 mm (35 mm equivalent: 38.0 mm)"
|
58
|
+
:light_source: Standard Light A
|
59
|
+
:y_cb_cr_sub_sampling: YCbCr4:2:0 (2 2)
|
60
|
+
:aperture: 2.8
|
61
|
+
:interop_index: R98 - DCF basic file (sRGB)
|
62
|
+
:fov: 50.7 deg
|
63
|
+
:brightness_value: -0.8203125
|
64
|
+
:file_permissions: rwxr-xr-x
|
65
|
+
:exif_version: "0220"
|
66
|
+
:digital_zoom_ratio: 0
|
67
|
+
:sharpness: Normal
|
68
|
+
:custom_rendered: Custom
|
69
|
+
:aperture_value: 2.8
|
70
|
+
:exif_image_height: 1840
|
71
|
+
:focal_length: 5.0 mm
|
72
|
+
:gain_control: High gain up
|
73
|
+
:light_value: 4.2
|
74
|
+
:modify_date: 2010-12-08 01:15:37 -08:00
|
75
|
+
:shutter_speed_value: !ruby/object:Rational
|
76
|
+
denominator: 15
|
77
|
+
numerator: 1
|
78
|
+
:make: Motorola
|
79
|
+
:exposure_index: 637
|
80
|
+
:file_size: 2023 kB
|
81
|
+
:focal_length_in35mm_format: 38 mm
|
82
|
+
:image_width: 3264
|
83
|
+
:user_comment: ""
|
84
|
+
:file_type: JPEG
|
85
|
+
:scale_factor35efl: 7.6
|
86
|
+
:iso: 637
|
87
|
+
:file_source: Digital Camera
|
data/test/faces.jpg
ADDED
Binary file
|
data/test/faces.jpg.yaml
ADDED
@@ -0,0 +1,327 @@
|
|
1
|
+
---
|
2
|
+
:black_mask_bottom_border: 0
|
3
|
+
:red_trc: (Binary data 14 bytes)
|
4
|
+
:wb_rggb_levels_custom2: 1734 1023 1023 1901
|
5
|
+
:serial_number: 0720413239
|
6
|
+
:fov: 68.3 deg
|
7
|
+
:self_timer2: 0
|
8
|
+
:file_size: 3.8 MB
|
9
|
+
:wb_rggb_levels: 1822 1015 1015 1709
|
10
|
+
:wb_shift_ab: 0
|
11
|
+
:lens35efl: "17.0 - 40.0 mm (35 mm equivalent: 26.6 - 62.5 mm)"
|
12
|
+
:thumbnail_image_valid_area: 0 159 7 112
|
13
|
+
:"profile_description_ml-zh-cn": !binary |
|
14
|
+
55u45py6IFJHQiDmj4/ov7Dmlofku7Y=
|
15
|
+
|
16
|
+
:current_iptc_digest: b443520a10119da99c2550175e6d0efb
|
17
|
+
:model: Canon EOS 20D
|
18
|
+
:af_point_selection_method: Normal
|
19
|
+
:aperture_value: 4.0
|
20
|
+
:coded_character_set: UTF8
|
21
|
+
:user_comment: ""
|
22
|
+
:color_space: sRGB
|
23
|
+
:focus_range: Not Known
|
24
|
+
:"profile_description_ml-ja-jp": !binary |
|
25
|
+
44Kr44Oh44OpIFJHQiDjg5fjg63jg5XjgqHjgqTjg6s=
|
26
|
+
|
27
|
+
:zoom_target_width: 0
|
28
|
+
:device_model: ""
|
29
|
+
:flash_guide_number: 0
|
30
|
+
:focal_plane_x_size: 23.04 mm
|
31
|
+
:saturation: Normal
|
32
|
+
:quality: Fine
|
33
|
+
:"profile_description_ml-it-it": Profilo RGB Fotocamera
|
34
|
+
:focal_type: Zoom
|
35
|
+
:iso: 400
|
36
|
+
:sequence_number: 0
|
37
|
+
:profile_file_signature: acsp
|
38
|
+
:wb_rggb_levels_as_shot: 1822 1015 1015 1709
|
39
|
+
:black_mask_left_border: 0
|
40
|
+
:long_exposure_noise_reduction2: "Off"
|
41
|
+
:color_temp_custom2: 4301
|
42
|
+
:blue_trc: (Binary data 14 bytes)
|
43
|
+
:color_temp_custom1: 2694
|
44
|
+
:compression: JPEG (old-style)
|
45
|
+
:exif_tool_version: 8.59
|
46
|
+
:focal_plane_resolution_unit: inches
|
47
|
+
:sensor_left_border: 84
|
48
|
+
:sensor_red_level: 0
|
49
|
+
:wb_rggb_levels_custom1: 1096 1023 1023 2793
|
50
|
+
:"profile_description_ml-ko-kr": !binary |
|
51
|
+
7Lm066mU6528IFJHQiDtlITroZztjIzsnbw=
|
52
|
+
|
53
|
+
:photo_effect: "Off"
|
54
|
+
:af_area_x_positions: 8 -555 571 -931 8 947 -555 571 8
|
55
|
+
:profile_version: 2.2.0
|
56
|
+
:control_mode: Camera Local Control
|
57
|
+
:flash_activity: 0
|
58
|
+
:connection_space_illuminant: 0.9642 1 0.82491
|
59
|
+
:sensor_width: 3596
|
60
|
+
:color_tone: Normal
|
61
|
+
:measured_rggb: 641 1024 1024 660
|
62
|
+
:create_date: 2005-11-26 14:20:34 -08:00
|
63
|
+
:shooting_mode: Depth-of-field AE
|
64
|
+
:application_record_version: 4
|
65
|
+
:af_image_width: 3504
|
66
|
+
:wb_bracket_mode: "Off"
|
67
|
+
:lens_type: Unknown (-1)
|
68
|
+
:focal_units: 1/mm
|
69
|
+
:make: Canon
|
70
|
+
:safety_shift_in_av_or_tv: Enable
|
71
|
+
:aeb_bracket_value: 0
|
72
|
+
:profile_id: 0
|
73
|
+
:software: Picasa
|
74
|
+
:serial_number_format: Format 2
|
75
|
+
:measured_ev: 14.12
|
76
|
+
:shutter_curtain_sync: 1st-curtain sync
|
77
|
+
:white_balance_blue: 0
|
78
|
+
:exif_byte_order: Little-endian (Intel, II)
|
79
|
+
:short_focal: 17 mm
|
80
|
+
:device_attributes: Reflective, Glossy, Positive, Color
|
81
|
+
:region_area_y:
|
82
|
+
- 0.248716
|
83
|
+
- 0.486301
|
84
|
+
- 0.243365
|
85
|
+
- 0.360873
|
86
|
+
- 0.466182
|
87
|
+
:custom_rendered: Normal
|
88
|
+
:sensor_top_border: 19
|
89
|
+
:focal_plane_y_size: 15.37 mm
|
90
|
+
:continuous_drive: Single
|
91
|
+
:profile_cmm_type: appl
|
92
|
+
:drive_mode: Single-frame Shooting
|
93
|
+
:toning_effect: None
|
94
|
+
:red_balance: 1.795074
|
95
|
+
:af_area_y_positions: 504 270 270 4 4 4 -262 -262 -496
|
96
|
+
:mime_type: image/jpeg
|
97
|
+
:color_components: 3
|
98
|
+
:region_area_x:
|
99
|
+
- 0.549658
|
100
|
+
- 0.858162
|
101
|
+
- 0.730736
|
102
|
+
- 0.453196
|
103
|
+
- 0.118579
|
104
|
+
:wb_rggb_levels_shade: 2215 1023 1023 1341
|
105
|
+
:x_resolution: 72
|
106
|
+
:cmm_flags: Not Embedded, Independent
|
107
|
+
:focus_mode: One-shot AF
|
108
|
+
:"profile_description_ml-fi-fi": Kameran RGB-profiili
|
109
|
+
:blue_matrix_column: 0.15662 0.08336 0.71953
|
110
|
+
:canon_image_size: Large
|
111
|
+
:original_decision_data_offset: 3968887
|
112
|
+
:focal_plane_y_resolution: 3959.322034
|
113
|
+
:hyperfocal_distance: 3.76 m
|
114
|
+
:lens_id: Unknown 17-40mm
|
115
|
+
:target_aperture: 4
|
116
|
+
:color_temp_cloudy: 6000
|
117
|
+
:wb_rggb_levels_auto: 1822 1023 1023 1709
|
118
|
+
:envelope_record_version: 4
|
119
|
+
:shutter_speed: !ruby/object:Rational
|
120
|
+
denominator: 4000
|
121
|
+
numerator: 1
|
122
|
+
:color_space_data: "RGB "
|
123
|
+
:af_image_height: 2336
|
124
|
+
:zoom_source_width: 0
|
125
|
+
:exposure_compensation: 0
|
126
|
+
:long_exposure_noise_reduction: "On"
|
127
|
+
:light_value: 14.0
|
128
|
+
:region_applied_to_dimensions_unit: pixel
|
129
|
+
:manual_flash_output: n/a
|
130
|
+
:y_resolution: 72
|
131
|
+
:white_balance: Auto
|
132
|
+
:flash_exposure_comp: !ruby/object:Rational
|
133
|
+
denominator: 3
|
134
|
+
numerator: 2
|
135
|
+
:superimposed_display: "On"
|
136
|
+
:date_time_original: 2005-11-26 14:20:34 -08:00
|
137
|
+
:wb_rggb_levels_cloudy: 2087 1023 1023 1459
|
138
|
+
:region_applied_to_dimensions_h: 2336
|
139
|
+
:interop_version: "0100"
|
140
|
+
:flash_bits: (none)
|
141
|
+
:shutter_speed_value: !ruby/object:Rational
|
142
|
+
denominator: 4000
|
143
|
+
numerator: 1
|
144
|
+
:source_file: test/faces.jpg
|
145
|
+
:file_type: JPEG
|
146
|
+
:file_name: faces.jpg
|
147
|
+
:iptc_digest: b443520a10119da99c2550175e6d0efb
|
148
|
+
:filter_effect: None
|
149
|
+
:sensor_right_border: 3587
|
150
|
+
:canon_exposure_mode: Depth-of-field AE
|
151
|
+
:"profile_description_ml-es-es": "Perfil RGB para C\xC3\xA1mara"
|
152
|
+
:profile_description_ml: Camera RGB Profile
|
153
|
+
:resolution_unit: inches
|
154
|
+
:region_area_w:
|
155
|
+
- 0.0667808
|
156
|
+
- 0.103881
|
157
|
+
- 0.0687785
|
158
|
+
- 0.0753424
|
159
|
+
- 0.11387
|
160
|
+
:bracket_value: 0
|
161
|
+
:digital_zoom: None
|
162
|
+
:y_cb_cr_positioning: Co-sited
|
163
|
+
:exif_image_width: 3504
|
164
|
+
:af_points_in_focus: 0,1,2,3,4,5,6,7,8
|
165
|
+
:color_temp_shade: 7000
|
166
|
+
:sensor_blue_level: 0
|
167
|
+
:canon_image_type: Canon EOS 20D
|
168
|
+
:region_applied_to_dimensions_w: 3504
|
169
|
+
:num_af_points: 9
|
170
|
+
:exif_version: "0221"
|
171
|
+
:y_cb_cr_sub_sampling: YCbCr4:2:2 (2 1)
|
172
|
+
:color_temp_tungsten: 3203
|
173
|
+
:aeb_sequence_auto_cancel: 0,-,+/Enabled
|
174
|
+
:metering_mode: Center-weighted average
|
175
|
+
:thumbnail_length: 12170
|
176
|
+
:green_matrix_column: 0.35332 0.67441 0.09042
|
177
|
+
:scale_factor35efl: 1.6
|
178
|
+
:image_size: 3504x2336
|
179
|
+
:measured_ev2: 14.125
|
180
|
+
:focus_continuous: Single
|
181
|
+
:primary_platform: Apple Computer Inc.
|
182
|
+
:lens_af_stop_button: ONE SHOT <-> AI SERVO
|
183
|
+
:media_white_point: 0.95047 1 1.0891
|
184
|
+
:image_height: 2336
|
185
|
+
:file_permissions: rw-r--r--
|
186
|
+
:lens: 17.0 - 40.0 mm
|
187
|
+
:add_original_decision_data: "On"
|
188
|
+
:"profile_description_ml-nl-nl": RGB-profiel Camera
|
189
|
+
:profile_class: Input Device Profile
|
190
|
+
:flash: Off, Did not fire
|
191
|
+
:digital_gain: 0
|
192
|
+
:thumbnail_image: (Binary data 12170 bytes)
|
193
|
+
:exif_image_height: 2336
|
194
|
+
:iso_expansion: "Off"
|
195
|
+
:red_matrix_column: 0.45427 0.24263 0.01482
|
196
|
+
:macro_mode: Normal
|
197
|
+
:aperture: 4.0
|
198
|
+
:color_temp_as_shot: 4599
|
199
|
+
:rendering_intent: Perceptual
|
200
|
+
:directory: test
|
201
|
+
:wb_rggb_levels_flash: 2112 1023 1023 1414
|
202
|
+
:mirror_lockup: Disable
|
203
|
+
:menu_button_display_position: Previous (top if power off)
|
204
|
+
:profile_copyright: Copyright 2003 Apple Computer Inc., all rights reserved.
|
205
|
+
:easy_mode: Manual
|
206
|
+
:auto_exposure_bracketing: "Off"
|
207
|
+
:color_temp_flash: 6303
|
208
|
+
:sensor_height: 2360
|
209
|
+
:auto_iso: 100
|
210
|
+
:nd_filter: n/a
|
211
|
+
:set_function_when_shooting: Change quality
|
212
|
+
:green_trc: (Binary data 14 bytes)
|
213
|
+
:vrd_offset: 0
|
214
|
+
:exposure_level_increments: !ruby/object:Rational
|
215
|
+
denominator: 3
|
216
|
+
numerator: 1
|
217
|
+
:wb_bracket_value_gm: 0
|
218
|
+
:region_area_h:
|
219
|
+
- 0.119007
|
220
|
+
- 0.1875
|
221
|
+
- 0.12286
|
222
|
+
- 0.101884
|
223
|
+
- 0.189212
|
224
|
+
:wb_rggb_levels_fluorescent: 1743 1023 1023 2205
|
225
|
+
:orientation: Horizontal (normal)
|
226
|
+
:exposure_time: !ruby/object:Rational
|
227
|
+
denominator: 4000
|
228
|
+
numerator: 1
|
229
|
+
:components_configuration: Y, Cb, Cr, -
|
230
|
+
:color_temp_daylight: 5200
|
231
|
+
:region_area_unit:
|
232
|
+
- normalized
|
233
|
+
- normalized
|
234
|
+
- normalized
|
235
|
+
- normalized
|
236
|
+
- normalized
|
237
|
+
:color_temperature: 4300
|
238
|
+
:thumbnail_offset: 4036
|
239
|
+
:sharpness_frequency: n/a
|
240
|
+
:wb_shift_gm: 0
|
241
|
+
:"profile_description_ml-da-dk": RGB-beskrivelse til Kamera
|
242
|
+
:region_name:
|
243
|
+
- Matthew McEachen
|
244
|
+
- Karen McEachen
|
245
|
+
- Ruth McEachen
|
246
|
+
- James McEachen
|
247
|
+
- Jamie McEachen
|
248
|
+
:focal_length: 17.0 mm
|
249
|
+
:device_manufacturer: appl
|
250
|
+
:slow_shutter: None
|
251
|
+
:canon_image_width: 3504
|
252
|
+
:focal_length35efl: "17.0 mm (35 mm equivalent: 26.6 mm)"
|
253
|
+
:warning: Invalid original decision data
|
254
|
+
:bulb_duration: 0
|
255
|
+
:color_temp_fluorescent: 3961
|
256
|
+
:bracket_shot_number: 0
|
257
|
+
:"profile_description_ml-pt-br": "Perfil RGB de C\xC3\xA2mera"
|
258
|
+
:picture_style: None
|
259
|
+
:bits_per_sample: 8
|
260
|
+
:region_type:
|
261
|
+
- Face
|
262
|
+
- Face
|
263
|
+
- Face
|
264
|
+
- Face
|
265
|
+
- Face
|
266
|
+
:canon_firmware_version: Firmware 2.0.3
|
267
|
+
:sharpness: 0
|
268
|
+
:file_number: 326-2647
|
269
|
+
:modify_date: 2012-02-15 22:58:07 -08:00
|
270
|
+
:"profile_description_ml-zh-tw": !binary |
|
271
|
+
5pW45L2N55u45qmfIFJHQiDoibLlvanmj4/ov7A=
|
272
|
+
|
273
|
+
:self_timer: "Off"
|
274
|
+
:encoding_process: Baseline DCT, Huffman coding
|
275
|
+
:color_temp_auto: 4599
|
276
|
+
:profile_creator: appl
|
277
|
+
:af_assist_beam: Emits
|
278
|
+
:optical_zoom_code: n/a
|
279
|
+
:contrast: Normal
|
280
|
+
:white_balance_red: 0
|
281
|
+
:owner_name: unknown
|
282
|
+
:xmp_toolkit: XMP Core 5.1.2
|
283
|
+
:canon_model_id: EOS 20D
|
284
|
+
:wb_bracket_value_ab: 0
|
285
|
+
:target_exposure_time: !ruby/object:Rational
|
286
|
+
denominator: 4008
|
287
|
+
numerator: 1
|
288
|
+
:auto_rotate: None
|
289
|
+
:bracket_mode: "Off"
|
290
|
+
:valid_af_points: 9
|
291
|
+
:scene_capture_type: Standard
|
292
|
+
:exposure_mode: Auto
|
293
|
+
:tone_curve: Standard
|
294
|
+
:chromatic_adaptation: 1.04788 0.02292 -0.0502 0.02957 0.99049 -0.01706 -0.00923 0.01508 0.75165
|
295
|
+
:long_focal: 40 mm
|
296
|
+
:file_modify_date: 2012-02-19 21:49:48 -08:00
|
297
|
+
:flash_sync_speed_av: Auto
|
298
|
+
:"profile_description_ml-sv-se": "RGB-profil f\xC3\xB6r Kamera"
|
299
|
+
:profile_connection_space: "XYZ "
|
300
|
+
:wb_rggb_levels_daylight: 1933 1023 1023 1607
|
301
|
+
:black_mask_right_border: 0
|
302
|
+
:camera_type: EOS Mid-range
|
303
|
+
:"profile_description_ml-de-de": "RGB-Profil f\xC3\xBCr Kameras"
|
304
|
+
:record_mode: JPEG
|
305
|
+
:interop_index: R98 - DCF basic file (sRGB)
|
306
|
+
:profile_date_time: 2003-07-01 00:00:00 -07:00
|
307
|
+
:base_iso: 400
|
308
|
+
:focal_plane_x_resolution: 3959.322034
|
309
|
+
:flashpix_version: "0100"
|
310
|
+
:canon_image_height: 2336
|
311
|
+
:"profile_description_ml-no-no": RGB-kameraprofil
|
312
|
+
:profile_description: Camera RGB Profile
|
313
|
+
:wb_rggb_levels_tungsten: 1382 1023 1023 2574
|
314
|
+
:black_mask_top_border: 0
|
315
|
+
:exposure_program: Not Defined
|
316
|
+
:"profile_description_ml-fr-fu": "Profil RVB de l\xE2\x80\x99appareil-photo"
|
317
|
+
:blue_balance: 1.683744
|
318
|
+
:ettlii: Evaluative
|
319
|
+
:image_width: 3504
|
320
|
+
:"shutter-ae_lock": AF/AE lock
|
321
|
+
:af_area_height: 78
|
322
|
+
:sensor_bottom_border: 2354
|
323
|
+
:af_area_width: 78
|
324
|
+
:flash_firing: Fires
|
325
|
+
:circle_of_confusion: 0.019 mm
|
326
|
+
:canon_flash_mode: "Off"
|
327
|
+
:f_number: 4.0
|
data/test/iPhone 4S.jpg
ADDED
Binary file
|
@@ -0,0 +1,80 @@
|
|
1
|
+
---
|
2
|
+
:gps_latitude_ref: North
|
3
|
+
:model: iPhone 4S
|
4
|
+
:color_space: sRGB
|
5
|
+
:components_configuration: Y, Cb, Cr, -
|
6
|
+
:exif_byte_order: Big-endian (Motorola, MM)
|
7
|
+
:sensing_method: One-chip color area
|
8
|
+
:create_date: 2011-11-23 09:39:52 -08:00
|
9
|
+
:bits_per_sample: 8
|
10
|
+
:date_time_original: 2011-11-23 09:39:52 -08:00
|
11
|
+
:source_file: test/iPhone 4S.jpg
|
12
|
+
:gps_img_direction: 121.9838275
|
13
|
+
:thumbnail_length: 5987
|
14
|
+
:exif_image_width: 3264
|
15
|
+
:exposure_program: Program AE
|
16
|
+
:compression: JPEG (old-style)
|
17
|
+
:image_size: 3264x2448
|
18
|
+
:mime_type: image/jpeg
|
19
|
+
:gps_longitude: -122.47566667
|
20
|
+
:x_resolution: 72
|
21
|
+
:orientation: Horizontal (normal)
|
22
|
+
:software: 5.0.1
|
23
|
+
:thumbnail_offset: 914
|
24
|
+
:gps_altitude_ref: Above Sea Level
|
25
|
+
:shutter_speed: !ruby/object:Rational
|
26
|
+
denominator: 6135
|
27
|
+
numerator: 1
|
28
|
+
:directory: test
|
29
|
+
:flashpix_version: "0100"
|
30
|
+
:y_cb_cr_positioning: Centered
|
31
|
+
:flash: Auto, Did not fire
|
32
|
+
:f_number: 2.4
|
33
|
+
:gps_altitude: 14.2 m Above Sea Level
|
34
|
+
:file_modify_date: 2012-02-19 21:49:48 -08:00
|
35
|
+
:y_resolution: 72
|
36
|
+
:encoding_process: Baseline DCT, Huffman coding
|
37
|
+
:thumbnail_image: (Binary data 5987 bytes)
|
38
|
+
:exposure_mode: Auto
|
39
|
+
:white_balance: Auto
|
40
|
+
:circle_of_confusion: 0.004 mm
|
41
|
+
:metering_mode: Multi-segment
|
42
|
+
:gps_latitude: 37.50233333
|
43
|
+
:color_components: 3
|
44
|
+
:image_height: 2448
|
45
|
+
:exif_tool_version: 8.59
|
46
|
+
:exposure_time: !ruby/object:Rational
|
47
|
+
denominator: 6135
|
48
|
+
numerator: 1
|
49
|
+
:gps_longitude_ref: West
|
50
|
+
:resolution_unit: inches
|
51
|
+
:scene_capture_type: Standard
|
52
|
+
:gps_position: 37.50233333 N, 122.47566667 W
|
53
|
+
:file_name: iPhone 4S.jpg
|
54
|
+
:hyperfocal_distance: 2.08 m
|
55
|
+
:focal_length35efl: "4.3 mm (35 mm equivalent: 35.0 mm)"
|
56
|
+
:y_cb_cr_sub_sampling: YCbCr4:2:0 (2 2)
|
57
|
+
:aperture: 2.4
|
58
|
+
:fov: 54.4 deg
|
59
|
+
:brightness_value: 11.27442702
|
60
|
+
:file_permissions: rw-r--r--
|
61
|
+
:gps_img_direction_ref: True North
|
62
|
+
:exif_version: "0221"
|
63
|
+
:sharpness: Normal
|
64
|
+
:gps_time_stamp: "19:03:32"
|
65
|
+
:aperture_value: 2.4
|
66
|
+
:exif_image_height: 2448
|
67
|
+
:focal_length: 4.3 mm
|
68
|
+
:light_value: 15.8
|
69
|
+
:modify_date: 2011-11-23 09:39:52 -08:00
|
70
|
+
:shutter_speed_value: !ruby/object:Rational
|
71
|
+
denominator: 6135
|
72
|
+
numerator: 1
|
73
|
+
:make: Apple
|
74
|
+
:file_size: 1954 kB
|
75
|
+
:subject_area: 1631 1223 881 881
|
76
|
+
:focal_length_in35mm_format: 35 mm
|
77
|
+
:image_width: 3264
|
78
|
+
:file_type: JPEG
|
79
|
+
:scale_factor35efl: 8.2
|
80
|
+
:iso: 64
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "exiftoolr"
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
class TestExiftoolr < Test::Unit::TestCase
|
6
|
+
|
7
|
+
DUMP_RESULTS = false
|
8
|
+
|
9
|
+
def test_missing
|
10
|
+
assert_raise Exiftoolr::NoSuchFile do
|
11
|
+
Exiftoolr.new("no/such/file")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_directory
|
16
|
+
assert_raise Exiftoolr::NotAFile do
|
17
|
+
Exiftoolr.new("lib")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_invalid_exif
|
22
|
+
assert Exiftoolr.new("Gemfile").errors?
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_matches
|
26
|
+
Dir["**/*.jpg"].each do |filename|
|
27
|
+
e = Exiftoolr.new(filename)
|
28
|
+
validate_result(e, filename)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate_result(result, filename)
|
33
|
+
yaml_file = "#{filename}.yaml"
|
34
|
+
if File.exist?(yaml_file)
|
35
|
+
assert !result.errors?
|
36
|
+
else
|
37
|
+
assert result.errors?
|
38
|
+
return
|
39
|
+
end
|
40
|
+
exif = result.to_hash
|
41
|
+
File.open(yaml_file, 'w') { |out| YAML.dump(exif, out) } if DUMP_RESULTS
|
42
|
+
e = File.open(yaml_file) { |f| YAML::load(f) }
|
43
|
+
exif.keys.each do |k|
|
44
|
+
next if [:file_modify_date, :directory, :source_file].include? k
|
45
|
+
assert_equal e[k], exif[k], "Key '#{k}' was incorrect for #{filename}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_multi_matches
|
50
|
+
filenames = Dir["**/*.jpg"].to_a
|
51
|
+
e = Exiftoolr.new(filenames)
|
52
|
+
filenames.each { |f| validate_result(e.result_for(f), f) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_error_filtering
|
56
|
+
filenames = Dir["**/*.*"].to_a
|
57
|
+
e = Exiftoolr.new(filenames)
|
58
|
+
expected_files = Dir["**/*.jpg"].to_a.collect{|f|File.expand_path(f)}.sort
|
59
|
+
assert_equal expected_files, e.files_with_results.sort
|
60
|
+
filenames.each { |f| validate_result(e.result_for(f), f) }
|
61
|
+
end
|
62
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: exiftoolr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Matthew McEachen
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-02-23 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
prerelease: false
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: 3
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
name: json
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
hash: 3
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
45
|
+
type: :development
|
46
|
+
name: rake
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
prerelease: false
|
50
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
type: :development
|
60
|
+
name: rspec
|
61
|
+
version_requirements: *id003
|
62
|
+
description: Multiget ExifTool wrapper for ruby
|
63
|
+
email:
|
64
|
+
- matthew-github@mceachen.org
|
65
|
+
executables: []
|
66
|
+
|
67
|
+
extensions: []
|
68
|
+
|
69
|
+
extra_rdoc_files: []
|
70
|
+
|
71
|
+
files:
|
72
|
+
- .gitignore
|
73
|
+
- Gemfile
|
74
|
+
- README.md
|
75
|
+
- Rakefile
|
76
|
+
- exiftoolr.gemspec
|
77
|
+
- lib/exiftoolr.rb
|
78
|
+
- lib/exiftoolr/result.rb
|
79
|
+
- lib/exiftoolr/version.rb
|
80
|
+
- test/Canon 20D.jpg
|
81
|
+
- test/Canon 20D.jpg.yaml
|
82
|
+
- test/Droid X.jpg
|
83
|
+
- test/Droid X.jpg.yaml
|
84
|
+
- test/faces.jpg
|
85
|
+
- test/faces.jpg.yaml
|
86
|
+
- test/iPhone 4S.jpg
|
87
|
+
- test/iPhone 4S.jpg.yaml
|
88
|
+
- test/test_exiftoolr.rb
|
89
|
+
homepage: https://github.com/mceachen/exif_tooler
|
90
|
+
licenses: []
|
91
|
+
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
hash: 3
|
103
|
+
segments:
|
104
|
+
- 0
|
105
|
+
version: "0"
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
hash: 3
|
112
|
+
segments:
|
113
|
+
- 0
|
114
|
+
version: "0"
|
115
|
+
requirements:
|
116
|
+
- ExifTool (see http://www.sno.phy.queensu.ca/~phil/exiftool/)
|
117
|
+
rubyforge_project: exiftoolr
|
118
|
+
rubygems_version: 1.8.17
|
119
|
+
signing_key:
|
120
|
+
specification_version: 3
|
121
|
+
summary: Multiget ExifTool wrapper for ruby
|
122
|
+
test_files:
|
123
|
+
- test/Canon 20D.jpg
|
124
|
+
- test/Canon 20D.jpg.yaml
|
125
|
+
- test/Droid X.jpg
|
126
|
+
- test/Droid X.jpg.yaml
|
127
|
+
- test/faces.jpg
|
128
|
+
- test/faces.jpg.yaml
|
129
|
+
- test/iPhone 4S.jpg
|
130
|
+
- test/iPhone 4S.jpg.yaml
|
131
|
+
- test/test_exiftoolr.rb
|