Class: PhotoUtils::Tools::Compare
- Inherits:
-
PhotoUtils::Tool
- Object
- PhotoUtils::Tool
- PhotoUtils::Tools::Compare
- Defined in:
- lib/photo_utils/tools/compare.rb
Instance Method Summary collapse
- #run ⇒ Object
-
#validate(scene, camera, lens, subject_distance_delta, angle_of_view_delta) ⇒ Object
FIXME: Ugh.
Methods inherited from PhotoUtils::Tool
#description, #initialize, #name, #usage
Constructor Details
This class inherits a constructor from PhotoUtils::Tool
Instance Method Details
#run ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
# File 'lib/photo_utils/tools/compare.rb', line 12 def run # given: # an image file with EXIF data # extract: # aperture/speed/sensitivity/bias # format (eg, APS-C) # lens focal length (in that format) # post-process exposure adjustment (from XMP data) # estimate width/height # desired minimum shutter # calculate # Ev100 # subject distance # depth of field # height or width (not given) # focal length required in 35mm # aperture required for equivalent depth of field # (warn if shutter changes) # # scenes: # stage performance at medium / far # portrait at close / medium #FIXME: Ugh $min_sensitivity = Sensitivity.new(100) $max_sensitivity = Sensitivity.new(1600) $max_angle_of_view_delta = Angle.new(5) $max_subject_distance_delta = 6.feet base = Path.new('/Users/johnl/Pictures/Lightroom Burned Exports') shots = %q{ # file width DoF description 3787022130_ce334b0c20_o.jpg 56" 3' OCF poem typist: W/A closeup 3894979751_8ef3683c78_o.jpg 54" 3' OCF poem typist: medium 3711708327_917d493f1a_o.jpg 23" 1' OCF man with hat: torso 58970238_dfe52a2c77_o.jpg 96" 3' backlit dancer: medium from medium 3043470502_9b3406f4dd_o.jpg 81" 3' winged stripper 3084813136_180bda6d84_o.jpg 75" 3' stripper moving away: close from close IMG_2664.jpg 233" 10' yarddogs dancers: wide from far IMG_2672.jpg 95" 10' yarddogs horns: medium from far IMG_2864.jpg 49" 2' sideshow bug eating: close from near IMG_2790.jpg 63" 3' sideshow ass: medium from near 060512.031.jpg 233" 6' Japan: man on street at night 060512.051.jpg 150' 50' Japan: Tokyo cityscape 060512.139.jpg 15' 10' Japan: man in street 060514.058.jpg 4' 1' Japan: bookstore 060515.081.jpg 7' 4' Japan: plants at corner 060519.001.jpg 14" 3" Japan: eggs 060520.012.jpg 20' 10' Japan: haystack 060524.023.jpg 9' 3' Japan: plants & rust 060527.051.jpg 8' 3' Japan: racoon & bicycle 060528.003.jpg 18" 6" Japan: tiny buddhas }.split(/\n/).map { |line| line.gsub!(/^\s+|\s+$/, '') line.sub!(/#.*/, '') if line.empty? nil else file, width, dof, type = line.split(/\s+/, 4) dof = Length.new(dof) width = Length.new(width) HashStruct.new( type: type, file: file, subject_width: width, desired_dof: dof) end }.compact # FIXME: Ugh def validate(scene, camera, lens, subject_distance_delta, angle_of_view_delta) # # validate subject distance delta # if subject_distance_delta > $max_subject_distance_delta raise "subject distance delta too different (#{subject_distance_delta.to_s(:imperial)} > #{$max_subject_distance_delta.to_s(:imperial)})" end # # validate aperture # if scene.aperture > lens.min_aperture || scene.aperture < lens.max_aperture raise "aperture out of range (#{scene.aperture} != #{lens.max_aperture} .. #{lens.min_aperture})" end # # validate angle of view # if angle_of_view_delta > $max_angle_of_view_delta raise "angle of view too different (#{angle_of_view_delta} > #{$max_angle_of_view_delta})" end # if scene.angle_of_view - scene.angle_of_view > 5 # raise "angle of view too wide (#{scene2.angle_of_view} > #{scene.angle_of_view})" # next # end # # validate shutter # if scene.time > camera.max_shutter raise "shutter too slow (#{scene.time} < #{camera.max_shutter})" end end successes = {} shots.each do |shot| img = MiniExiftool.new(base + shot.file, numerical: true, timestamps: DateTime) scene = Scene.new model = img['Model'] scene.format = Format[model] or raise "Can't determine frame for model #{model.inspect} (#{shot.file})" scene.description = "#{shot[:type]} [#{shot[:seq]}]" scene.aperture = img['Aperture'] if img['ISO'].kind_of?(Numeric) scene.sensitivity = img['ISO'] else # "0 800" scene.sensitivity = img['ISO'].split(' ').last.to_f end scene.time = img['ExposureTime'] scene.focal_length = img['FocalLength'] exp_comp = img['ExposureCompensation'].to_f if exp_comp != 0 scene.sensitivity = Sensitivity.new_from_v(scene.sensitivity.to_v + exp_comp) end # d = w * f / s scene.subject_distance = Length.new(shot.subject_width * (scene.focal_length / scene.format.width)) puts puts "--- #{scene.description}" puts scene.print_exposure scene.print_depth_of_field puts # now compute equivalent scene for each camera cameras.each do |camera| scene2 = Scene.new scene2.format = camera.format or raise "Unknown format: #{camera.format.inspect}" scene2.aperture = scene.aperture scene2.brightness = scene.brightness # scene2.sensitivity = 400 # find the lens that would best fit found = false # NOTE: #uniq doesn't work well with delegate classes, so we cast the focal length to a float first focal_lengths = camera.lenses.collect { |lens| lens.focal_length.to_f }.sort.reverse.uniq focal_lengths.each do |focal_length| lenses = camera.lenses.select { |lens| lens.focal_length == focal_length }.sort_by { |lens| lens.max_aperture }.reverse lenses.each do |lens| scene2.focal_length = lens.focal_length # keeping subject width the same, compute new distance from given focal length # o i # --- = --- # d f # # f = focal length # d = subject distance # o = subject dimension # i = frame dimension scene2.subject_distance = 1 / ((scene2.frame.width / scene2.focal_length) / shot.subject_width) # # calculate depth of field # near_limit = scene2.subject_distance - (shot.desired_dof / 2) far_limit = scene2.subject_distance + (shot.desired_dof / 2) scene2.aperture = scene2.aperture_for_depth_of_field(near_limit, far_limit) # # adjust aperture # # ;;a = scene2.aperture # round aperture to closest 1/2 stop scene2.aperture = Aperture.new_from_v((scene2.aperture.to_v / 0.5).round * 0.5) # ;;puts "[1] #{a} => #{scene2.aperture}" # clamp to maximum aperture scene2.aperture = [scene2.aperture, lens.max_aperture].max # ;;puts "[2] #{a} => #{scene2.aperture}" # # calculate sensitivity # # start with minimum shutter time scene2.time = camera.max_shutter # round up to the next ISO value scene2.sensitivity = Sensitivity.new_from_v(scene2.sensitivity.to_v.ceil) scene2.sensitivity = [scene2.sensitivity, $max_sensitivity].min scene2.sensitivity = [scene2.sensitivity, $min_sensitivity].max # # force recalculation of shutter # scene2.time = nil # # compute subject distance difference # subject_distance_delta = scene.subject_distance - scene2.subject_distance # # compute angle of view difference # angle_of_view_delta = Angle.new(scene.angle_of_view - scene2.angle_of_view) # # validate # begin validate(scene2, camera, lens, subject_distance_delta, angle_of_view_delta) rescue => e failure = e end puts " %-15.15s | %-19.19s | %10s @ %5s @ %8s | dist: %6s (%s%6s) | dof: %6s (-%5s .. +%5s) | %s" % [ camera.name, lens.name, scene2.aperture, scene2.time, scene2.sensitivity, scene2.subject_distance.to_s(:imperial), subject_distance_delta < 0 ? '-' : '+', subject_distance_delta.abs.to_s(:imperial), scene2.total_depth_of_field.to_s(:imperial), scene2.near_distance_from_subject.to_s(:imperial), scene2.far_distance_from_subject.to_s(:imperial), failure || 'GOOD' ] if [:verbose] || !failure successes[camera.name] ||= {} if !failure successes[camera.name][lens.name] ||= 0 successes[camera.name][lens.name] += 1 found = true break end end break if found end unless found successes[camera.name]['FAILED'] ||= 0 successes[camera.name]['FAILED'] += 1 end end end ;;pp successes # ;;successes.sort_by { |k,v| v }.each { |k,v| puts "%2d: %s" % [v, k] } end |
#validate(scene, camera, lens, subject_distance_delta, angle_of_view_delta) ⇒ Object
FIXME: Ugh
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/photo_utils/tools/compare.rb', line 89 def validate(scene, camera, lens, subject_distance_delta, angle_of_view_delta) # # validate subject distance delta # if subject_distance_delta > $max_subject_distance_delta raise "subject distance delta too different (#{subject_distance_delta.to_s(:imperial)} > #{$max_subject_distance_delta.to_s(:imperial)})" end # # validate aperture # if scene.aperture > lens.min_aperture || scene.aperture < lens.max_aperture raise "aperture out of range (#{scene.aperture} != #{lens.max_aperture} .. #{lens.min_aperture})" end # # validate angle of view # if angle_of_view_delta > $max_angle_of_view_delta raise "angle of view too different (#{angle_of_view_delta} > #{$max_angle_of_view_delta})" end # if scene.angle_of_view - scene.angle_of_view > 5 # raise "angle of view too wide (#{scene2.angle_of_view} > #{scene.angle_of_view})" # next # end # # validate shutter # if scene.time > camera.max_shutter raise "shutter too slow (#{scene.time} < #{camera.max_shutter})" end end |