1 # Copyright 2014 The Android Open Source Project 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 import its.image 16 import its.device 17 import its.caps 18 import its.objects 19 import os.path 20 from matplotlib import pylab 21 import matplotlib 22 import matplotlib.pyplot 23 import numpy 24 25 #AE must converge within this number of auto requests for EV 26 THREASH_CONVERGE_FOR_EV = 8 27 28 def main(): 29 """Tests that EV compensation is applied. 30 """ 31 LOCKED = 3 32 33 NAME = os.path.basename(__file__).split(".")[0] 34 35 MAX_LUMA_DELTA_THRESH = 0.05 36 37 with its.device.ItsSession() as cam: 38 props = cam.get_camera_properties() 39 its.caps.skip_unless(its.caps.manual_sensor(props) and 40 its.caps.manual_post_proc(props) and 41 its.caps.per_frame_control(props) and 42 its.caps.ev_compensation(props)) 43 44 debug = its.caps.debug_mode() 45 largest_yuv = its.objects.get_largest_yuv_format(props) 46 if debug: 47 fmt = largest_yuv 48 else: 49 match_ar = (largest_yuv['width'], largest_yuv['height']) 50 fmt = its.objects.get_smallest_yuv_format(props, match_ar=match_ar) 51 52 ev_compensation_range = props['android.control.aeCompensationRange'] 53 range_min = ev_compensation_range[0] 54 range_max = ev_compensation_range[1] 55 ev_per_step = its.objects.rational_to_float( 56 props['android.control.aeCompensationStep']) 57 steps_per_ev = int(round(1.0 / ev_per_step)) 58 ev_steps = range(range_min, range_max + 1, steps_per_ev) 59 imid = len(ev_steps) / 2 60 ev_shifts = [pow(2, step * ev_per_step) for step in ev_steps] 61 lumas = [] 62 63 # Converge 3A, and lock AE once converged. skip AF trigger as 64 # dark/bright scene could make AF convergence fail and this test 65 # doesn't care the image sharpness. 66 cam.do_3a(ev_comp=0, lock_ae=True, do_af=False) 67 68 for ev in ev_steps: 69 70 # Capture a single shot with the same EV comp and locked AE. 71 req = its.objects.auto_capture_request() 72 req['android.control.aeExposureCompensation'] = ev 73 req["android.control.aeLock"] = True 74 # Use linear tone curve to avoid brightness being impacted 75 # by tone curves. 76 req["android.tonemap.mode"] = 0 77 req["android.tonemap.curveRed"] = [0.0,0.0, 1.0,1.0] 78 req["android.tonemap.curveGreen"] = [0.0,0.0, 1.0,1.0] 79 req["android.tonemap.curveBlue"] = [0.0,0.0, 1.0,1.0] 80 caps = cam.do_capture([req]*THREASH_CONVERGE_FOR_EV, fmt) 81 82 for cap in caps: 83 if (cap['metadata']['android.control.aeState'] == LOCKED): 84 y = its.image.convert_capture_to_planes(cap)[0] 85 tile = its.image.get_image_patch(y, 0.45,0.45,0.1,0.1) 86 lumas.append(its.image.compute_image_means(tile)[0]) 87 break 88 assert(cap['metadata']['android.control.aeState'] == LOCKED) 89 90 print "ev_step_size_in_stops", ev_per_step 91 shift_mid = ev_shifts[imid] 92 luma_normal = lumas[imid] / shift_mid 93 expected_lumas = [min(1.0, luma_normal * ev_shift) for ev_shift in ev_shifts] 94 95 pylab.plot(ev_steps, lumas, 'r') 96 pylab.plot(ev_steps, expected_lumas, 'b') 97 matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME)) 98 99 luma_diffs = [expected_lumas[i] - lumas[i] for i in range(len(ev_steps))] 100 max_diff = max(abs(i) for i in luma_diffs) 101 avg_diff = abs(numpy.array(luma_diffs)).mean() 102 print "Max delta between modeled and measured lumas:", max_diff 103 print "Avg delta between modeled and measured lumas:", avg_diff 104 assert(max_diff < MAX_LUMA_DELTA_THRESH) 105 106 if __name__ == '__main__': 107 main() 108