exiftoolr 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|