exiftool 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 97cc49e13b847ed687dca2430f6e3974d1327998
4
+ data.tar.gz: 7a6c7e780ad4962983b491c517d2f4a8a890264b
5
+ SHA512:
6
+ metadata.gz: 614fee303365b7d799ebf3d29109502194ed89f35685f4dfabe45a729fd184a23e3d657bc4c91270e5c9ffdda7873f613956c4fa511bfa067f52996500f93f28
7
+ data.tar.gz: 97ad66d4cab65ea08313ae3b24222852ab9477113deb276eecb88dbda568d59833ddcd164485a5d68857cbca6393729443ddd792d9924d136785c4d1873136f9
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ script: bundle exec rake
3
+ before_install:
4
+ - sudo apt-get install libimage-exiftool-perl
5
+ rvm:
6
+ - 1.9.3
7
+ - 2.0.0
8
+ - rbx-19mode
9
+ - jruby-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in exiftoolr.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # Ruby wrapper for ExifTool
2
+
3
+ [![Build Status](https://secure.travis-ci.org/mceachen/exiftool.png?branch=master)](http://travis-ci.org/mceachen/exiftool)
4
+ [![Gem Version](https://badge.fury.io/rb/exiftool.png)](http://rubygems.org/gems/exiftool)
5
+ [![Code Climate](https://codeclimate.com/github/mceachen/exiftool.png)](https://codeclimate.com/github/mceachen/exiftool)
6
+
7
+ This gem is the simplest thing that could possibly work that
8
+ reads the output of [exiftool](http://www.sno.phy.queensu.ca/~phil/exiftool)
9
+ and renders it into a ruby hash, with correctly typed values and symbolized keys.
10
+
11
+ Rubies 1.9 and later are supported.
12
+
13
+ ## Features
14
+
15
+ * GPS latitude and longitude values are parsed as signed floats,
16
+ where north and east are positive, and west and south are negative.
17
+ * Values like shutter speed and exposure time are rendered as Rationals,
18
+ which lets the caller show them as fractions (1/250) or as comparable numeric instances.
19
+ * String values like "interop" and "serial number" are kept as strings
20
+ (which preserves zero prefixes)
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.
25
+ * No method_missing madness
26
+
27
+ ## Usage
28
+
29
+ ```ruby
30
+ require 'Exiftool'
31
+ e = Exiftool.new("path/to/iPhone 4S.jpg")
32
+ e.to_hash
33
+ # => {:make => "Apple", :gps_longitude => -122.47566667, …
34
+ e.to_display_hash
35
+ # => {"Make" => "Apple", "GPS Longitude" => -122.47566667, …
36
+ ```
37
+
38
+ ### Multiple file support
39
+
40
+ This gem supports Exiftool's multiget, which lets you fetch metadata for many files at once.
41
+
42
+ This can be dramatically more efficient (like, 60x faster) than spinning up the ```exiftool```
43
+ process for each file.
44
+
45
+ Supply an array to the Exiftool initializer, then use ```.result_for```:
46
+
47
+ ```ruby
48
+ require 'Exiftool'
49
+ e = Exiftool.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
+ * ```Exiftool::NoSuchFile``` is raised if the provided filename doesn't exist.
63
+ * ```Exiftool::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
+ Exiftool.new("Gemfile").errors?
68
+ #=> true
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 'exiftool'
78
+
79
+ and then run ```bundle```.
80
+
81
+ ## Change history
82
+
83
+ ### 0.2.0
84
+
85
+ * Renamed from exiftoolr to exiftool
86
+
87
+ ### 0.1.0
88
+
89
+ * Better timestamp parsing—now both sub-second and timezone offsets are handled correctly
90
+ * Switched to minitest-spec
91
+ * Ruby 1.8.7 is no longer supported, hence the minor version uptick.
92
+
93
+ ### 0.0.9
94
+
95
+ * Explicitly added MIT licensing to the gemspec.
96
+
97
+ ### 0.0.8
98
+
99
+ * Extracted methods in parsing to make the code complexity lower. FOUR DOT OH GPA
100
+
101
+ ### 0.0.7
102
+
103
+ * Added warning values for EXIF headers that are corrupt
104
+ * Made initialize gracefully accept an empty array, or an array of Pathname instances
105
+ * Added support for ruby 1.9.3 and exiftool v8.15 (Ubuntu Natty) and v8.85 (current stable version)
106
+
107
+ ### 0.0.5
108
+
109
+ Fixed homepage URL in gemspec
110
+
111
+ ### 0.0.4
112
+
113
+ Added support for multiple file fetching (which is *much* faster for large directories)
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require "bundler/gem_tasks"
2
+
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
+
17
+ task :default => :test
data/exiftool.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'exiftool/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'exiftool'
8
+ spec.version = Exiftool::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'
15
+
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"]
20
+
21
+ spec.requirements << 'ExifTool (see http://www.sno.phy.queensu.ca/~phil/exiftool/)'
22
+
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']
30
+ end
data/lib/exiftool.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'json'
2
+ require 'shellwords'
3
+ require 'exiftool/result'
4
+
5
+ class Exiftool
6
+ class NoSuchFile < StandardError ; end
7
+ class NotAFile < StandardError ; end
8
+ class ExiftoolNotInstalled < StandardError ; end
9
+
10
+ def self.exiftool_installed?
11
+ exiftool_version > 0
12
+ end
13
+
14
+ def self.exiftool_version
15
+ @@exiftool_version ||= `exiftool -ver 2> /dev/null`.to_f
16
+ end
17
+
18
+ def self.expand_path(filename)
19
+ raise(NoSuchFile, filename) unless File.exist?(filename)
20
+ raise(NotAFile, filename) unless File.file?(filename)
21
+ File.expand_path(filename)
22
+ end
23
+
24
+ def initialize(filenames, exiftool_opts = '')
25
+ @file2result = {}
26
+ filenames = [filenames] if filenames.is_a?(String)
27
+ unless filenames.empty?
28
+ escaped_filenames = filenames.collect do |f|
29
+ Shellwords.escape(self.class.expand_path(f.to_s))
30
+ end.join(" ")
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
+ json = `#{cmd}`
35
+ raise ExiftoolNotInstalled if json == ""
36
+ JSON.parse(json).each do |raw|
37
+ result = Result.new(raw)
38
+ @file2result[result.source_file] = result
39
+ end
40
+ end
41
+ end
42
+
43
+ def result_for(filename)
44
+ @file2result[self.class.expand_path(filename)]
45
+ end
46
+
47
+ def files_with_results
48
+ @file2result.values.collect { |r| r.source_file unless r.errors? }.compact
49
+ end
50
+
51
+ def to_hash
52
+ first.to_hash
53
+ end
54
+
55
+ def to_display_hash
56
+ first.to_display_hash
57
+ end
58
+
59
+ def symbol_display_hash
60
+ first.symbol_display_hash
61
+ end
62
+
63
+ def errors?
64
+ @file2result.values.any? { |ea| ea.errors? }
65
+ end
66
+
67
+ private
68
+
69
+ def first
70
+ raise InvalidArgument, 'use #result_for when multiple filenames are used' if @file2result.size > 1
71
+ @file2result.values.first
72
+ end
73
+ end
@@ -0,0 +1,62 @@
1
+ require 'time'
2
+ require 'rational'
3
+
4
+ class Exiftool
5
+ class Parser
6
+
7
+ WORD_BOUNDARY_RES = [/([A-Z\d]+)([A-Z][a-z])/, /([a-z\d])([A-Z])/]
8
+ FRACTION_RE = /^(\d+)\/(\d+)$/
9
+
10
+ attr_reader :key, :display_key, :sym_key, :raw_value
11
+
12
+ def initialize(key, raw_value)
13
+ @key = key
14
+ @display_key = WORD_BOUNDARY_RES.inject(key) { |k, regex| k.gsub(regex, '\1 \2') }
15
+ @sym_key = display_key.downcase.gsub(' ', '_').to_sym
16
+ @raw_value = raw_value
17
+ end
18
+
19
+ def value
20
+ for_lat_long ||
21
+ for_date ||
22
+ for_fraction ||
23
+ raw_value
24
+ rescue StandardError => e
25
+ "Warning: Parsing '#{raw_value}' for attribute '#{key}' raised #{e.message}"
26
+ end
27
+
28
+ private
29
+
30
+ def for_lat_long
31
+ if sym_key == :gps_latitude || sym_key == :gps_longitude
32
+ value, direction = raw_value.split(" ")
33
+ if value =~ /\A\d+\.?\d*\z/
34
+ value.to_f * (['S', 'W'].include?(direction) ? -1 : 1)
35
+ end
36
+ end
37
+ end
38
+
39
+ def for_date
40
+ if raw_value.is_a?(String) && display_key =~ /\bdate\b/i
41
+ try_parse { Time.strptime(raw_value, '%Y:%m:%d %H:%M:%S%z') } ||
42
+ try_parse { Time.strptime(raw_value, '%Y:%m:%d %H:%M:%S') } ||
43
+ try_parse { Time.strptime(raw_value, '%Y:%m:%d %H:%M:%S.%L%z') } ||
44
+ try_parse { Time.strptime(raw_value, '%Y:%m:%d %H:%M:%S.%L') } ||
45
+ try_parse { Time.parse(raw_value) }
46
+ end
47
+ end
48
+
49
+ def try_parse
50
+ yield
51
+ rescue ArgumentError
52
+ nil
53
+ end
54
+
55
+ def for_fraction
56
+ if raw_value.is_a?(String)
57
+ scan = raw_value.scan(FRACTION_RE).first
58
+ v = Rational(*scan.map { |ea| ea.to_i }) unless scan.nil?
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,32 @@
1
+ require 'exiftool/parser'
2
+
3
+ class Exiftool
4
+ class Result
5
+ attr_reader :to_hash, :to_display_hash, :symbol_display_hash
6
+
7
+ def initialize(raw_hash)
8
+ @raw_hash = raw_hash
9
+ @to_hash = {}
10
+ @to_display_hash = {}
11
+ @symbol_display_hash = {}
12
+ @raw_hash.each do |key, raw_value|
13
+ p = Parser.new(key, raw_value)
14
+ @to_hash[p.sym_key] = p.value
15
+ @to_display_hash[p.display_key] = p.value
16
+ @symbol_display_hash[p.sym_key] = p.display_key
17
+ end
18
+ end
19
+
20
+ def [](key)
21
+ @to_hash[key]
22
+ end
23
+
24
+ def source_file
25
+ self[:source_file]
26
+ end
27
+
28
+ def errors?
29
+ self[:error] == 'Unknown file type' || self[:warning] == 'Unsupported file type'
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ class Exiftool
2
+ VERSION = Gem::Version.new('0.2.0')
3
+ end
Binary file
@@ -0,0 +1,225 @@
1
+ ---
2
+ :source_file: /Users/mrm/Dropbox/code/exiftoolr/test/Canon 20D.jpg
3
+ :exif_tool_version: 9.31
4
+ :file_name: Canon 20D.jpg
5
+ :directory: /Users/mrm/Dropbox/code/exiftoolr/test
6
+ :file_size: 2.9 MB
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)
14
+ :make: Canon
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
23
+ denominator: 250
24
+ numerator: 1
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
33
+ denominator: 250
34
+ numerator: 1
35
+ :aperture_value: 9.0
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
53
+ :canon_exposure_mode: Program AE
54
+ :lens_type: Unknown (-1)
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)
62
+ :focus_continuous: Single
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
75
+ denominator: 251
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
117
+ :valid_af_points: 9
118
+ :canon_image_width: 3504
119
+ :canon_image_height: 2336
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
128
+ :serial_number_format: Format 2
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
156
+ :sensor_top_border: 19
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
164
+ :color_temp_as_shot: 4662
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
173
+ :wb_rggb_levels_tungsten: 1410 1023 1023 2408
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
194
+ :custom_rendered: Normal
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
204
+ :color_components: 3
205
+ :y_cb_cr_sub_sampling: YCbCr4:2:2 (2 1)
206
+ :aperture: 9.0
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
214
+ denominator: 250
215
+ numerator: 1
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