exiftoolr 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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