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 os.path 16 17 import its.caps 18 import its.device 19 import its.image 20 import its.objects 21 import matplotlib 22 from matplotlib import pylab 23 import numpy as np 24 25 NAME = os.path.basename(__file__).split('.')[0] 26 LOCKED = 3 27 LUMA_LOCKED_TOL = 0.05 28 THRESH_CONVERGE_FOR_EV = 8 # AE must converge within this num 29 YUV_FULL_SCALE = 255.0 30 YUV_SATURATION_MIN = 253.0 31 YUV_SATURATION_TOL = 1.0 32 33 34 def main(): 35 """Tests that EV compensation is applied.""" 36 37 with its.device.ItsSession() as cam: 38 props = cam.get_camera_properties() 39 its.caps.skip_unless(its.caps.ev_compensation(props) and 40 its.caps.ae_lock(props)) 41 42 debug = its.caps.debug_mode() 43 largest_yuv = its.objects.get_largest_yuv_format(props) 44 if debug: 45 fmt = largest_yuv 46 else: 47 match_ar = (largest_yuv['width'], largest_yuv['height']) 48 fmt = its.objects.get_smallest_yuv_format(props, match_ar=match_ar) 49 50 ev_per_step = its.objects.rational_to_float( 51 props['android.control.aeCompensationStep']) 52 steps_per_ev = int(1.0 / ev_per_step) 53 evs = range(-2 * steps_per_ev, 2 * steps_per_ev + 1, steps_per_ev) 54 lumas = [] 55 reds = [] 56 greens = [] 57 blues = [] 58 59 # Converge 3A, and lock AE once converged. skip AF trigger as 60 # dark/bright scene could make AF convergence fail and this test 61 # doesn't care the image sharpness. 62 cam.do_3a(ev_comp=0, lock_ae=True, do_af=False) 63 64 for ev in evs: 65 # Capture a single shot with the same EV comp and locked AE. 66 req = its.objects.auto_capture_request() 67 req['android.control.aeExposureCompensation'] = ev 68 req['android.control.aeLock'] = True 69 caps = cam.do_capture([req]*THRESH_CONVERGE_FOR_EV, fmt) 70 luma_locked = [] 71 for i, cap in enumerate(caps): 72 if cap['metadata']['android.control.aeState'] == LOCKED: 73 y = its.image.convert_capture_to_planes(cap)[0] 74 tile = its.image.get_image_patch(y, 0.45, 0.45, 0.1, 0.1) 75 luma = its.image.compute_image_means(tile)[0] 76 luma_locked.append(luma) 77 if i == THRESH_CONVERGE_FOR_EV-1: 78 lumas.append(luma) 79 rgb = its.image.convert_capture_to_rgb_image(cap) 80 rgb_tile = its.image.get_image_patch(rgb, 81 0.45, 0.45, 82 0.1, 0.1) 83 rgb_means = its.image.compute_image_means(rgb_tile) 84 reds.append(rgb_means[0]) 85 greens.append(rgb_means[1]) 86 blues.append(rgb_means[2]) 87 print 'lumas in AE locked captures: ', luma_locked 88 assert np.isclose(min(luma_locked), max(luma_locked), 89 rtol=LUMA_LOCKED_TOL) 90 assert caps[THRESH_CONVERGE_FOR_EV-1]['metadata']['android.control.aeState'] == LOCKED 91 92 pylab.plot(evs, lumas, '-ro') 93 pylab.xlabel('EV Compensation') 94 pylab.ylabel('Mean Luma (Normalized)') 95 matplotlib.pyplot.savefig('%s_plot_means.png' % (NAME)) 96 97 # Trim extra saturated images 98 while lumas and lumas[-1] >= YUV_SATURATION_MIN/YUV_FULL_SCALE: 99 if (np.isclose(reds[-1], greens[-1], 100 YUV_SATURATION_TOL/YUV_FULL_SCALE) and 101 np.isclose(blues[-1], greens[-1], 102 YUV_SATURATION_TOL/YUV_FULL_SCALE)): 103 lumas.pop(-1) 104 reds.pop(-1) 105 greens.pop(-1) 106 blues.pop(-1) 107 print 'Removed saturated image.' 108 else: 109 break 110 # Only allow positive EVs to give saturated image 111 assert len(lumas) > 2 112 luma_diffs = np.diff(lumas) 113 min_luma_diffs = min(luma_diffs) 114 print 'Min of the luma value difference between adjacent ev comp: ', 115 print min_luma_diffs 116 # All luma brightness should be increasing with increasing ev comp. 117 assert min_luma_diffs > 0 118 119 if __name__ == '__main__': 120 main() 121