exiftoolr 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9c4230e0116eee40f519e4259f6c8469c4756bf7
4
- data.tar.gz: 3ab21055dae2d4f3b4bc5839c0d096da8beeb323
3
+ metadata.gz: ecd656c3093b0eb6f3f94cc402bf5ee14a9b13ba
4
+ data.tar.gz: a8129d314f15fe908720f07393581b251ae95bfa
5
5
  SHA512:
6
- metadata.gz: 23a797844c0196d46919811515202ad52dcf68647a07ec8f3ee9e3cd50fda1ff47269e3308eff5d2cb568341a7c3dfe6dc116c91827e5b612877c91dae0e0b9e
7
- data.tar.gz: 2e3966f75669e3ade2bc0458f83dbcb202f939fb2d314f58cf9ba0c2c437fea691770cd81296668b8cca9a2f8393efe6b298641706a5a6c77c47d2c0ebf143a7
6
+ metadata.gz: d8bcb6cc7a2dce7917f64c00e616f2493662562ff11ecfbc1bc49665311fd372c6ce057db7e343d45e7cd09e815cdf1ea3dd1f0bed395e086e57c17bbb62c685
7
+ data.tar.gz: 051740e233bd226e7293e4dd332bd22f1de4cc785f670bd3f43b324481d958c75f7ae515199bb8e654f58bd7c002ac3e8a2487b46dbc8f32068909b5d892d9e4
@@ -1,8 +1,9 @@
1
1
  language: ruby
2
2
  script: bundle exec rake
3
3
  before_install:
4
- - sudo apt-get update
5
- - sudo apt-get install libimage-exiftool-perl
4
+ - sudo apt-get install libimage-exiftool-perl
6
5
  rvm:
7
6
  - 1.9.3
8
7
  - 2.0.0
8
+ - rbx-19mode
9
+ - jruby-19mode
data/README.md CHANGED
@@ -8,7 +8,9 @@ This gem is the simplest thing that could possibly work that
8
8
  reads the output of [exiftool](http://www.sno.phy.queensu.ca/~phil/exiftool)
9
9
  and renders it into a ruby hash, with correctly typed values and symbolized keys.
10
10
 
11
- ## What constitutes "correct"?
11
+ Rubies 1.9 and later are supported.
12
+
13
+ ## What constitutes "correct values"?
12
14
 
13
15
  * GPS latitude and longitude are rendered as signed floats,
14
16
  where north and east are positive, and west and south are negative.
@@ -16,22 +18,10 @@ and renders it into a ruby hash, with correctly typed values and symbolized keys
16
18
  which lets the caller show them as fractions (1/250) or as comparable numeric instances.
17
19
  * String values like "interop" and "serial number" are kept as strings
18
20
  (which preserves zero prefixes)
19
-
20
- ## Installation
21
-
22
- You'll want to [install ExifTool](http://www.sno.phy.queensu.ca/~phil/exiftool/install.html), then
23
-
24
- ```
25
- gem install exiftoolr
26
- ```
27
-
28
- or add to your Gemfile:
29
-
30
- ```
31
- gem 'exiftoolr'
32
- ```
33
-
34
- and run ```bundle```.
21
+ * Timestamps are attempted to be interpreted with correct timezones and sub-second resolution, if
22
+ the header contains that data.
23
+ Please note that EXIF headers don't always include a timezone offset, so we just adopt the system
24
+ timezone, which may, of course, be wrong.
35
25
 
36
26
  ## Usage
37
27
 
@@ -46,6 +36,11 @@ e.to_display_hash
46
36
 
47
37
  ### Multiple file support
48
38
 
39
+ This gem supports Exiftool's multiget, which lets you fetch metadata for many files at once.
40
+
41
+ This can be dramatically more efficient (like, 60x faster) than spinning up the ```exiftool```
42
+ process for each file.
43
+
49
44
  Supply an array to the Exiftoolr initializer, then use ```.result_for```:
50
45
 
51
46
  ```ruby
@@ -72,8 +67,25 @@ Exiftoolr.new("Gemfile").errors?
72
67
  #=> true
73
68
  ```
74
69
 
70
+
71
+ ## Installation
72
+
73
+ First [install ExifTool](http://www.sno.phy.queensu.ca/~phil/exiftool/install.html).
74
+
75
+ Then, add this your Gemfile:
76
+
77
+ gem 'exiftoolr'
78
+
79
+ and then run ```bundle```.
80
+
75
81
  ## Change history
76
82
 
83
+ ### 0.1.0
84
+
85
+ * Better timestamp parsing—now both sub-second and timezone offsets are handled correctly
86
+ * Switched to minitest-spec
87
+ * Ruby 1.8.7 is no longer supported, hence the minor version uptick.
88
+
77
89
  ### 0.0.9
78
90
 
79
91
  * Explicitly added MIT licensing to the gemspec.
data/Rakefile CHANGED
@@ -1,5 +1,17 @@
1
1
  require "bundler/gem_tasks"
2
- require "rake/testtask"
3
2
 
4
- Rake::TestTask.new
3
+ require 'yard'
4
+ YARD::Rake::YardocTask.new do |t|
5
+ t.files = ['lib/**/*.rb', 'README.md']
6
+ end
7
+
8
+ require 'rake/testtask'
9
+
10
+ Rake::TestTask.new do |t|
11
+ t.libs.push "lib"
12
+ t.libs.push "test"
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = true
15
+ end
16
+
5
17
  task :default => :test
@@ -1,26 +1,30 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "exiftoolr/version"
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'exiftoolr/version'
4
5
 
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/exiftoolr"
11
- s.summary = %q{Multiget ExifTool wrapper for ruby}
12
- s.description = %q{Multiget ExifTool wrapper for ruby}
13
- s.license = 'MIT'
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'exiftoolr'
8
+ spec.version = Exiftoolr::VERSION
9
+ spec.authors = ['Matthew McEachen']
10
+ spec.email = %w(matthew-github@mceachen.org)
11
+ spec.homepage = 'https://github.com/mceachen/exiftoolr'
12
+ spec.summary = %q{Multiget ExifTool wrapper for ruby}
13
+ spec.description = %q{Multiget ExifTool wrapper for ruby}
14
+ spec.license = 'MIT'
14
15
 
15
- s.rubyforge_project = "exiftoolr"
16
+ spec.files = `git ls-files`.split("\n")
17
+ spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
16
20
 
17
- s.files = `git ls-files`.split("\n")
18
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
- s.require_paths = ["lib"]
21
+ spec.requirements << 'ExifTool (see http://www.sno.phy.queensu.ca/~phil/exiftool/)'
21
22
 
22
- s.requirements << "ExifTool (see http://www.sno.phy.queensu.ca/~phil/exiftool/)"
23
-
24
- s.add_dependency "json"
25
- s.add_development_dependency "rake"
23
+ spec.add_dependency 'json'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'bundler'
26
+ spec.add_development_dependency 'yard'
27
+ spec.add_development_dependency 'minitest'
28
+ spec.add_development_dependency 'minitest-great_expectations'
29
+ spec.add_development_dependency 'minitest-reporters' unless ENV['CI']
26
30
  end
@@ -1,8 +1,6 @@
1
- require "exiftoolr/version"
2
- require "exiftoolr/result"
3
-
4
1
  require 'json'
5
2
  require 'shellwords'
3
+ require 'exiftoolr/result'
6
4
 
7
5
  class Exiftoolr
8
6
  class NoSuchFile < StandardError ; end
@@ -23,14 +21,16 @@ class Exiftoolr
23
21
  File.expand_path(filename)
24
22
  end
25
23
 
26
- def initialize(filenames, exiftool_opts = "")
24
+ def initialize(filenames, exiftool_opts = '')
27
25
  @file2result = {}
28
26
  filenames = [filenames] if filenames.is_a?(String)
29
27
  unless filenames.empty?
30
28
  escaped_filenames = filenames.collect do |f|
31
29
  Shellwords.escape(self.class.expand_path(f.to_s))
32
30
  end.join(" ")
33
- cmd = "exiftool #{exiftool_opts} -j -coordFormat \"%.8f\" -dateFormat \"%Y-%m-%d %H:%M:%S\" #{escaped_filenames} 2> /dev/null"
31
+ # I'd like to use -dateformat, but it doesn't support timezone offsets properly,
32
+ # nor sub-second timestamps.
33
+ cmd = "exiftool #{exiftool_opts} -j -coordFormat \"%.8f\" #{escaped_filenames} 2> /dev/null"
34
34
  json = `#{cmd}`
35
35
  raise ExiftoolNotInstalled if json == ""
36
36
  JSON.parse(json).each do |raw|
@@ -67,7 +67,7 @@ class Exiftoolr
67
67
  private
68
68
 
69
69
  def first
70
- raise InvalidArgument, "use #result_for when multiple filenames are used" if @file2result.size > 1
70
+ raise InvalidArgument, 'use #result_for when multiple filenames are used' if @file2result.size > 1
71
71
  @file2result.values.first
72
72
  end
73
73
  end
@@ -0,0 +1,63 @@
1
+ require 'time'
2
+ require 'date'
3
+ require 'rational'
4
+
5
+ class Exiftoolr
6
+ class Parser
7
+
8
+ WORD_BOUNDARY_RES = [/([A-Z\d]+)([A-Z][a-z])/, /([a-z\d])([A-Z])/]
9
+ FRACTION_RE = /^(\d+)\/(\d+)$/
10
+
11
+ attr_reader :key, :display_key, :sym_key, :raw_value
12
+
13
+ def initialize(key, raw_value)
14
+ @key = key
15
+ @display_key = WORD_BOUNDARY_RES.inject(key) { |k, regex| k.gsub(regex, '\1 \2') }
16
+ @sym_key = display_key.downcase.gsub(' ', '_').to_sym
17
+ @raw_value = raw_value
18
+ end
19
+
20
+ def value
21
+ for_lat_long ||
22
+ for_date ||
23
+ for_fraction ||
24
+ raw_value
25
+ rescue StandardError => e
26
+ "Warning: Parsing '#{raw_value}' for attribute '#{key}' raised #{e.message}"
27
+ end
28
+
29
+ private
30
+
31
+ def for_lat_long
32
+ if sym_key == :gps_latitude || sym_key == :gps_longitude
33
+ value, direction = raw_value.split(" ")
34
+ if value =~ /\A\d+\.?\d*\z/
35
+ value.to_f * (['S', 'W'].include?(direction) ? -1 : 1)
36
+ end
37
+ end
38
+ end
39
+
40
+ def for_date
41
+ if raw_value.is_a?(String) && display_key =~ /\bdate\b/i
42
+ try_parse { Time.strptime(raw_value, '%Y:%m:%d %H:%M:%S%z') } ||
43
+ try_parse { Time.strptime(raw_value, '%Y:%m:%d %H:%M:%S') } ||
44
+ try_parse { Time.strptime(raw_value, '%Y:%m:%d %H:%M:%S.%L%z') } ||
45
+ try_parse { Time.strptime(raw_value, '%Y:%m:%d %H:%M:%S.%L') } ||
46
+ try_parse { Time.parse(raw_value) }
47
+ end
48
+ end
49
+
50
+ def try_parse
51
+ yield
52
+ rescue ArgumentError
53
+ nil
54
+ end
55
+
56
+ def for_fraction
57
+ if raw_value.is_a?(String)
58
+ scan = raw_value.scan(FRACTION_RE).first
59
+ v = Rational(*scan.map { |ea| ea.to_i }) unless scan.nil?
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,5 +1,4 @@
1
- require 'time'
2
- require 'rational'
1
+ require 'exiftoolr/parser'
3
2
 
4
3
  class Exiftoolr
5
4
  class Result
@@ -27,51 +26,7 @@ class Exiftoolr
27
26
  end
28
27
 
29
28
  def errors?
30
- self[:error] == "Unknown file type" || self[:warning] == "Unsupported file type"
31
- end
32
- end
33
-
34
- class Parser
35
- WORD_BOUNDARY_RES = [/([A-Z\d]+)([A-Z][a-z])/, /([a-z\d])([A-Z])/]
36
- FRACTION_RE = /^(\d+)\/(\d+)$/
37
-
38
- attr_reader :display_key, :sym_key, :raw_value
39
-
40
- def initialize(key, raw_value)
41
- @display_key = WORD_BOUNDARY_RES.inject(key) { |k, regex| k.gsub(regex, '\1 \2') }
42
- @sym_key = display_key.downcase.gsub(' ', '_').to_sym
43
- @raw_value = raw_value
44
- end
45
-
46
- def value
47
- for_lat_long ||
48
- for_date ||
49
- for_fraction ||
50
- raw_value
51
- rescue StandardError => e
52
- v = "Warning: Parsing '#{raw_value}' for attribute '#{k}' raised #{e.message}"
53
- end
54
-
55
- private
56
-
57
- def for_lat_long
58
- if sym_key == :gps_latitude || sym_key == :gps_longitude
59
- value, direction = raw_value.split(" ")
60
- value.to_f * (['S', 'W'].include?(direction) ? -1 : 1)
61
- end
62
- end
63
-
64
- def for_date
65
- if raw_value.is_a?(String) && display_key =~ /\bdate\b/i
66
- Time.parse(raw_value)
67
- end
68
- end
69
-
70
- def for_fraction
71
- if raw_value.is_a?(String)
72
- scan = raw_value.scan(FRACTION_RE).first
73
- v = Rational(*scan.collect { |ea| ea.to_i }) unless scan.nil?
74
- end
29
+ self[:error] == 'Unknown file type' || self[:warning] == 'Unsupported file type'
75
30
  end
76
31
  end
77
32
  end
@@ -1,3 +1,3 @@
1
1
  class Exiftoolr
2
- VERSION = Gem::Version.new("0.0.9")
2
+ VERSION = Gem::Version.new('0.1.0')
3
3
  end
@@ -1,223 +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
1
+ ---
2
+ :source_file: /Users/mrm/Dropbox/code/exiftoolr/test/Canon 20D.jpg
3
+ :exif_tool_version: 9.31
39
4
  :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
5
+ :directory: /Users/mrm/Dropbox/code/exiftoolr/test
47
6
  :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
7
+ :file_modify_date: 2012-02-23 05:22:17.000000000 +00:00
8
+ :file_access_date: 2013-07-14 20:25:41.000000000 +00:00
9
+ :file_inode_change_date: 2013-02-16 19:21:59.000000000 +00:00
10
+ :file_permissions: rw-r--r--
11
+ :file_type: JPEG
12
+ :mime_type: image/jpeg
13
+ :exif_byte_order: Little-endian (Intel, II)
62
14
  :make: Canon
63
- :set_function_when_shooting: Image replay
64
- :wb_bracket_value_gm: 0
65
- :exposure_time: !ruby/object:Rational
15
+ :model: Canon EOS 20D
16
+ :orientation: Horizontal (normal)
17
+ :x_resolution: 72
18
+ :y_resolution: 72
19
+ :resolution_unit: inches
20
+ :modify_date: 2004-09-19 12:25:20.000000000 +00:00
21
+ :y_cb_cr_positioning: Co-sited
22
+ :exposure_time: !ruby/object:Rational
66
23
  denominator: 250
67
24
  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
25
+ :f_number: 9.0
26
+ :exposure_program: Program AE
27
+ :iso: 200
28
+ :exif_version: '0221'
29
+ :date_time_original: 2004-09-19 12:25:20.000000000 +00:00
30
+ :create_date: 2004-09-19 12:25:20.000000000 +00:00
31
+ :components_configuration: Y, Cb, Cr, -
32
+ :shutter_speed_value: !ruby/object:Rational
78
33
  denominator: 250
79
34
  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
35
  :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
36
+ :flash: Off, Did not fire
37
+ :focal_length: 85.0 mm
38
+ :macro_mode: Normal
39
+ :self_timer: 'Off'
40
+ :quality: Fine
41
+ :canon_flash_mode: 'Off'
42
+ :continuous_drive: Single
43
+ :focus_mode: One-shot AF
44
+ :record_mode: JPEG
45
+ :canon_image_size: Large
46
+ :easy_mode: Manual
47
+ :digital_zoom: None
48
+ :contrast: '+1'
49
+ :saturation: '+1'
50
+ :sharpness: '+1'
51
+ :metering_mode: Evaluative
52
+ :focus_range: Not Known
98
53
  :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
54
  :lens_type: Unknown (-1)
129
- :sensor_blue_level: 0
130
- :file_permissions: rw-r--r--
131
- :num_af_points: 9
55
+ :max_focal_length: 85 mm
56
+ :min_focal_length: 17 mm
57
+ :focal_units: 1/mm
58
+ :max_aperture: 5.6
59
+ :min_aperture: 32
60
+ :flash_activity: 0
61
+ :flash_bits: (none)
132
62
  :focus_continuous: Single
133
- :color_temp_tungsten: 3196
134
- :thumbnail_length: 11259
135
- :target_exposure_time: !ruby/object:Rational
63
+ :zoom_source_width: 0
64
+ :zoom_target_width: 0
65
+ :photo_effect: 'Off'
66
+ :manual_flash_output: n/a
67
+ :focal_type: Zoom
68
+ :focal_plane_x_size: 23.04 mm
69
+ :focal_plane_y_size: 15.37 mm
70
+ :auto_iso: 100
71
+ :base_iso: 200
72
+ :measured_ev: 13.25
73
+ :target_aperture: 9
74
+ :target_exposure_time: !ruby/object:Rational
136
75
  denominator: 251
137
76
  numerator: 1
77
+ :exposure_compensation: 0
78
+ :white_balance: Auto
79
+ :slow_shutter: None
80
+ :sequence_number: 0
81
+ :optical_zoom_code: n/a
82
+ :flash_guide_number: 0
83
+ :flash_exposure_comp: 0
84
+ :auto_exposure_bracketing: 'Off'
85
+ :aeb_bracket_value: 0
86
+ :control_mode: Camera Local Control
87
+ :measured_ev2: 13.5
88
+ :bulb_duration: 0
89
+ :camera_type: EOS Mid-range
90
+ :auto_rotate: None
91
+ :nd_filter: n/a
92
+ :self_timer2: 0
93
+ :canon_image_type: Canon EOS 20D
94
+ :canon_firmware_version: Firmware 1.0.2
95
+ :owner_name: unknown
96
+ :serial_number: 0330113864
97
+ :set_function_when_shooting: Image replay
98
+ :long_exposure_noise_reduction: 'Off'
99
+ :flash_sync_speed_av: Auto
100
+ :shutter-ae_lock: AF/AE lock
101
+ :af_assist_beam: Emits
102
+ :exposure_level_increments: 1/3 Stop
103
+ :flash_firing: Fires
104
+ :iso_expansion: 'On'
105
+ :aeb_sequence_auto_cancel: 0,-,+/Enabled
106
+ :superimposed_display: 'On'
107
+ :menu_button_display_position: Previous (top if power off)
108
+ :mirror_lockup: Disable
109
+ :af_point_selection_method: Normal
110
+ :ettlii: Evaluative
111
+ :shutter_curtain_sync: 1st-curtain sync
112
+ :safety_shift_in_av_or_tv: Disable
113
+ :lens_af_stop_button: AF stop
114
+ :add_original_decision_data: 'Off'
115
+ :canon_model_id: EOS 20D
116
+ :num_af_points: 9
138
117
  :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)
118
+ :canon_image_width: 3504
142
119
  :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
120
+ :af_image_width: 3504
121
+ :af_image_height: 2336
122
+ :af_area_width: 78
123
+ :af_area_height: 78
124
+ :af_area_x_positions: 8 -555 571 -931 8 947 -555 571 8
125
+ :af_area_y_positions: 504 270 270 4 4 4 -262 -262 -496
126
+ :af_points_in_focus: 4
127
+ :thumbnail_image_valid_area: 0 159 7 112
146
128
  :serial_number_format: Format 2
147
- :short_focal: 17 mm
129
+ :original_decision_data_offset: 0
130
+ :file_number: 793-9390
131
+ :bracket_mode: 'Off'
132
+ :bracket_value: 0
133
+ :bracket_shot_number: 0
134
+ :long_exposure_noise_reduction2: 'Off'
135
+ :wb_bracket_mode: 'Off'
136
+ :wb_bracket_value_ab: 0
137
+ :wb_bracket_value_gm: 0
138
+ :filter_effect: None
139
+ :toning_effect: None
140
+ :tone_curve: Standard
141
+ :sharpness_frequency: n/a
142
+ :sensor_red_level: 0
143
+ :sensor_blue_level: 0
144
+ :white_balance_red: 0
145
+ :white_balance_blue: 0
146
+ :color_temperature: 4800
147
+ :picture_style: None
148
+ :digital_gain: 0
149
+ :wb_shift_ab: 0
150
+ :wb_shift_gm: 0
151
+ :measured_rggb: 674 1024 1024 670
152
+ :vrd_offset: 0
153
+ :sensor_width: 3596
154
+ :sensor_height: 2360
155
+ :sensor_left_border: 84
148
156
  :sensor_top_border: 19
149
- :measured_ev2: 13.5
150
- :image_height: 2336
157
+ :sensor_right_border: 3587
158
+ :sensor_bottom_border: 2354
159
+ :black_mask_left_border: 0
160
+ :black_mask_top_border: 0
161
+ :black_mask_right_border: 0
162
+ :black_mask_bottom_border: 0
163
+ :wb_rggb_levels_as_shot: 1973 1015 1015 1663
151
164
  :color_temp_as_shot: 4662
152
- :flash: Off, Did not fire
153
- :iso_expansion: "On"
154
- :record_mode: JPEG
155
- :flashpix_version: "0100"
165
+ :wb_rggb_levels_auto: 1973 1023 1023 1663
166
+ :color_temp_auto: 4662
167
+ :wb_rggb_levels_daylight: 1995 1023 1023 1509
168
+ :color_temp_daylight: 5200
169
+ :wb_rggb_levels_shade: 2292 1023 1023 1267
170
+ :color_temp_shade: 7000
171
+ :wb_rggb_levels_cloudy: 2160 1023 1023 1373
172
+ :color_temp_cloudy: 6000
156
173
  :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)
174
+ :color_temp_tungsten: 3196
175
+ :wb_rggb_levels_fluorescent: 1797 1023 1023 2058
176
+ :color_temp_fluorescent: 3952
177
+ :wb_rggb_levels_flash: 2187 1023 1023 1333
178
+ :color_temp_flash: 6309
179
+ :wb_rggb_levels_custom1: 2095 1023 1023 1316
180
+ :color_temp_custom1: 6163
181
+ :wb_rggb_levels_custom2: 1912 1023 1023 1627
182
+ :color_temp_custom2: 4792
183
+ :color_tone: Normal
184
+ :user_comment: ''
185
+ :flashpix_version: '0100'
186
+ :color_space: sRGB
187
+ :exif_image_width: 3504
188
+ :exif_image_height: 2336
189
+ :interop_index: R98 - DCF basic file (sRGB)
190
+ :interop_version: '0100'
191
+ :focal_plane_x_resolution: 2886.363636
192
+ :focal_plane_y_resolution: 2885.805763
193
+ :focal_plane_resolution_unit: inches
161
194
  :custom_rendered: Normal
162
- :drive_mode: Single-frame Shooting
195
+ :exposure_mode: Auto
196
+ :scene_capture_type: Standard
197
+ :compression: JPEG (old-style)
198
+ :thumbnail_offset: 9728
199
+ :thumbnail_length: 11259
200
+ :image_width: 3504
201
+ :image_height: 2336
202
+ :encoding_process: Baseline DCT, Huffman coding
203
+ :bits_per_sample: 8
163
204
  :color_components: 3
164
- :x_resolution: 72
165
- :exif_image_height: 2336
205
+ :y_cb_cr_sub_sampling: YCbCr4:2:2 (2 1)
166
206
  :aperture: 9.0
167
- :easy_mode: Manual
168
- :nd_filter: n/a
169
- :exposure_level_increments: "1/3 Stop"
170
- :auto_exposure_bracketing: "Off"
171
- :af_area_height: 78
172
- :flash_firing: Fires
173
- :wb_rggb_levels_custom2: 1912 1023 1023 1627
174
- :self_timer2: 0
175
- :lens35efl: "17.0 - 85.0 mm (35 mm equivalent: 26.6 - 132.8 mm)"
176
- :mime_type: image/jpeg
177
- :wb_rggb_levels_shade: 2292 1023 1023 1267
178
- :canon_image_size: Large
179
- :hyperfocal_distance: 41.74 m
180
- :wb_rggb_levels_auto: 1973 1023 1023 1663
181
- :zoom_source_width: 0
182
- :auto_iso: 100
183
- :vrd_offset: 0
184
- :wb_shift_gm: 0
185
- :wb_rggb_levels_fluorescent: 1797 1023 1023 2058
186
- :color_temperature: 4800
187
- :wb_rggb_levels: 1973 1015 1015 1663
188
- :wb_shift_ab: 0
189
- :user_comment: ""
190
- :zoom_target_width: 0
191
- :quality: Fine
192
- :color_temp_cloudy: 6000
193
- :af_image_height: 2336
194
- :manual_flash_output: n/a
195
- :flash_exposure_comp: 0
196
- :wb_rggb_levels_cloudy: 2160 1023 1023 1373
197
- :source_file: test/Canon 20D.jpg
198
- :color_temp_daylight: 5200
199
- :sharpness_frequency: n/a
200
- :slow_shutter: None
201
- :modify_date: 2004-09-19 12:25:20 -07:00
202
- :bulb_duration: 0
203
- :bits_per_sample: 8
204
- :focus_range: Not Known
205
- :saturation: "+1"
206
- :focal_type: Zoom
207
- :wb_rggb_levels_as_shot: 1973 1015 1015 1663
208
- :min_aperture: 32
209
- :exif_tool_version: 8.59
210
- :y_resolution: 72
211
- :date_time_original: 2004-09-19 12:25:20 -07:00
212
- :shutter_speed_value: !ruby/object:Rational
207
+ :drive_mode: Single-frame Shooting
208
+ :image_size: 3504x2336
209
+ :lens: 17.0 - 85.0 mm
210
+ :lens_id: Unknown 17-85mm
211
+ :scale_factor35efl: 1.0
212
+ :shooting_mode: Program AE
213
+ :shutter_speed: !ruby/object:Rational
213
214
  denominator: 250
214
215
  numerator: 1
215
- :filter_effect: None
216
- :resolution_unit: inches
217
- :y_cb_cr_positioning: Co-sited
218
- :picture_style: None
219
- :file_number: 793-9390
220
- :self_timer: "Off"
221
- :wb_bracket_value_ab: 0
222
- :optical_zoom_code: n/a
223
- :owner_name: unknown
216
+ :thumbnail_image: (Binary data 11259 bytes)
217
+ :wb_rggb_levels: 1973 1015 1015 1663
218
+ :blue_balance: 1.638424
219
+ :circle_of_confusion: 0.030 mm
220
+ :fov: 23.7 deg
221
+ :focal_length35efl: '85.0 mm (35 mm equivalent: 85.6 mm)'
222
+ :hyperfocal_distance: 26.91 m
223
+ :lens35efl: '17.0 - 85.0 mm (35 mm equivalent: 17.1 - 85.6 mm)'
224
+ :light_value: 13.3
225
+ :red_balance: 1.943842