1 # Copyright 2018 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 22 import matplotlib 23 from matplotlib import pylab 24 import numpy as np 25 26 AE_STATE_CONVERGED = 2 27 CONTROL_AE_STATE_FLASH_REQUIRED = 4 28 NAME = os.path.basename(__file__).split('.')[0] 29 NUM_CAPTURE = 30 30 VALID_STABLE_LUMA_MIN = 0.1 31 VALID_STABLE_LUMA_MAX = 0.9 32 33 34 def is_awb_af_stable(prev_cap, cap): 35 awb_gains_0 = prev_cap['metadata']['android.colorCorrection.gains'] 36 awb_gains_1 = cap['metadata']['android.colorCorrection.gains'] 37 ccm_0 = prev_cap['metadata']['android.colorCorrection.transform'] 38 ccm_1 = cap['metadata']['android.colorCorrection.transform'] 39 focus_distance_0 = prev_cap['metadata']['android.lens.focusDistance'] 40 focus_distance_1 = cap['metadata']['android.lens.focusDistance'] 41 42 return (np.allclose(awb_gains_0, awb_gains_1, rtol=0.01) and 43 ccm_0 == ccm_1 and 44 np.isclose(focus_distance_0, focus_distance_1, rtol=0.01)) 45 46 47 def main(): 48 """Tests PER_FRAME_CONTROL properties for auto capture requests. 49 50 If debug is required, MANUAL_POSTPROCESSING capability is implied 51 since its.caps.read_3a is valid for test. Debug can performed with 52 a defined tonemap curve: 53 req['android.tonemap.mode'] = 0 54 gamma = sum([[i/63.0,math.pow(i/63.0,1/2.2)] for i in xrange(64)],[]) 55 req['android.tonemap.curve'] = { 56 'red': gamma, 'green': gamma, 'blue': gamma} 57 """ 58 59 with its.device.ItsSession() as cam: 60 props = cam.get_camera_properties() 61 its.caps.skip_unless(its.caps.per_frame_control(props) and 62 its.caps.read_3a(props)) 63 64 debug = its.caps.debug_mode() 65 largest_yuv = its.objects.get_largest_yuv_format(props) 66 if debug: 67 fmt = largest_yuv 68 else: 69 match_ar = (largest_yuv['width'], largest_yuv['height']) 70 fmt = its.objects.get_smallest_yuv_format(props, match_ar=match_ar) 71 72 req = its.objects.auto_capture_request() 73 caps = cam.do_capture([req]*NUM_CAPTURE, fmt) 74 75 total_gains = [] 76 lumas = [] 77 ae_states = [] 78 for i, cap in enumerate(caps): 79 print '=========== frame %d ==========' % i 80 y = its.image.convert_capture_to_planes(cap)[0] 81 tile = its.image.get_image_patch(y, 0.45, 0.45, 0.1, 0.1) 82 luma = its.image.compute_image_means(tile)[0] 83 84 ae_state = cap['metadata']['android.control.aeState'] 85 iso = cap['metadata']['android.sensor.sensitivity'] 86 isp_gain = cap['metadata']['android.control.postRawSensitivityBoost'] 87 exp_time = cap['metadata']['android.sensor.exposureTime'] 88 total_gain = iso*isp_gain/100.0*exp_time/1000000.0 89 awb_state = cap['metadata']['android.control.awbState'] 90 awb_gains = cap['metadata']['android.colorCorrection.gains'] 91 ccm = cap['metadata']['android.colorCorrection.transform'] 92 focus_distance = cap['metadata']['android.lens.focusDistance'] 93 94 # Convert CCM from rational to float, as numpy arrays. 95 awb_ccm = np.array(its.objects.rational_to_float(ccm)).reshape(3, 3) 96 97 print 'AE: %d ISO: %d ISP_sen: %d exp(ms): %d tot_gain: %f' % ( 98 ae_state, iso, isp_gain, exp_time, total_gain), 99 print 'luma: %f' % luma 100 print 'fd: %f' % focus_distance 101 print 'AWB: %d, AWB gains: %s\n AWB matrix: %s' % ( 102 awb_state, str(awb_gains), str(awb_ccm)) 103 print 'Tonemap curve:', cap['metadata']['android.tonemap.curve'] 104 105 lumas.append(luma) 106 total_gains.append(total_gain) 107 ae_states.append(ae_state) 108 img = its.image.convert_capture_to_rgb_image(cap) 109 its.image.write_image(img, '%s_frame_%d.jpg'% (NAME, i)) 110 111 norm_gains = [x / max(total_gains) * max(lumas) for x in total_gains] 112 pylab.plot(range(len(lumas)), lumas, '-g.', 113 label='Center patch brightness') 114 pylab.plot(range(len(norm_gains)), norm_gains, '-r.', 115 label='Metadata AE setting product') 116 pylab.title(NAME) 117 pylab.xlabel('frame index') 118 pylab.legend() 119 matplotlib.pyplot.savefig('%s_plot.png' % (NAME)) 120 121 for i in range(1, len(caps)): 122 if is_awb_af_stable(caps[i-1], caps[i]): 123 prev_total_gain = total_gains[i-1] 124 total_gain = total_gains[i] 125 delta_gain = total_gain - prev_total_gain 126 prev_luma = lumas[i-1] 127 luma = lumas[i] 128 delta_luma = luma - prev_luma 129 # luma and total_gain should change in same direction 130 msg = 'Frame %d to frame %d:' % (i-1, i) 131 msg += ' metadata gain %f->%f (%s), luma %f->%f (%s)' % ( 132 prev_total_gain, total_gain, 133 'increasing' if delta_gain > 0.0 else 'decreasing', 134 prev_luma, luma, 135 'increasing' if delta_luma > 0.0 else 'decreasing') 136 assert delta_gain * delta_luma >= 0.0, msg 137 else: 138 print 'Frame %d->%d AWB/AF changed' % (i-1, i) 139 140 for i in range(len(lumas)): 141 luma = lumas[i] 142 ae_state = ae_states[i] 143 if (ae_state == AE_STATE_CONVERGED or 144 ae_state == CONTROL_AE_STATE_FLASH_REQUIRED): 145 msg = 'Frame %d AE converged luma %f. valid range: (%f, %f)' % ( 146 i, luma, VALID_STABLE_LUMA_MIN, VALID_STABLE_LUMA_MAX) 147 assert VALID_STABLE_LUMA_MIN < luma < VALID_STABLE_LUMA_MAX, msg 148 149 if __name__ == '__main__': 150 main() 151