1# Copyright 2015 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 15import os 16 17import its.caps 18import its.device 19import its.image 20import its.objects 21 22import matplotlib 23from matplotlib import pylab 24import numpy 25 26NAME = os.path.basename(__file__).split('.')[0] 27NUM_FRAMES = 4 # number of frames for temporal info to settle 28NUM_SHADING_MODE_SWITCH_LOOPS = 3 29SHADING_MODES = ['OFF', 'FAST', 'HQ'] 30THRESHOLD_DIFF_RATIO = 0.15 31 32 33def main(): 34 """Test that the android.shading.mode param is applied. 35 36 Switching shading modes and checks that the lens shading maps are 37 modified as expected. 38 """ 39 40 with its.device.ItsSession() as cam: 41 props = cam.get_camera_properties() 42 43 its.caps.skip_unless(its.caps.per_frame_control(props) and 44 its.caps.lsc_map(props) and 45 its.caps.lsc_off(props)) 46 47 mono_camera = its.caps.mono_camera(props) 48 49 # lsc_off devices should always support OFF(0), FAST(1), and HQ(2) 50 assert(props.has_key('android.shading.availableModes') and 51 set(props['android.shading.availableModes']) == set([0, 1, 2])) 52 53 # Test 1: Switching shading modes several times and verify: 54 # 1. Lens shading maps with mode OFF are all 1.0 55 # 2. Lens shading maps with mode FAST are similar after switching 56 # shading modes. 57 # 3. Lens shading maps with mode HIGH_QUALITY are similar after 58 # switching shading modes. 59 cam.do_3a(mono_camera=mono_camera) 60 61 # Use smallest yuv size matching the aspect ratio of largest yuv size to 62 # reduce some USB bandwidth overhead since we are only looking at output 63 # metadata in this test. 64 largest_yuv_fmt = its.objects.get_largest_yuv_format(props) 65 largest_yuv_size = (largest_yuv_fmt['width'], largest_yuv_fmt['height']) 66 cap_fmt = its.objects.get_smallest_yuv_format(props, largest_yuv_size) 67 68 # Get the reference lens shading maps for OFF, FAST, and HIGH_QUALITY 69 # in different sessions. 70 # reference_maps[mode] 71 num_shading_modes = len(SHADING_MODES) 72 reference_maps = [[] for mode in range(num_shading_modes)] 73 num_map_gains = 0 74 for mode in range(1, num_shading_modes): 75 req = its.objects.auto_capture_request() 76 req['android.statistics.lensShadingMapMode'] = 1 77 req['android.shading.mode'] = mode 78 cap_res = cam.do_capture([req]*NUM_FRAMES, cap_fmt)[NUM_FRAMES-1]['metadata'] 79 lsc_map = cap_res['android.statistics.lensShadingCorrectionMap'] 80 assert(lsc_map.has_key('width') and 81 lsc_map.has_key('height') and 82 lsc_map['width'] is not None and 83 lsc_map['height'] is not None) 84 if mode == 1: 85 num_map_gains = lsc_map['width'] * lsc_map['height'] * 4 86 reference_maps[0] = [1.0] * num_map_gains 87 reference_maps[mode] = lsc_map['map'] 88 89 # Get the lens shading maps while switching modes in one session. 90 reqs = [] 91 for i in range(NUM_SHADING_MODE_SWITCH_LOOPS): 92 for mode in range(num_shading_modes): 93 for _ in range(NUM_FRAMES): 94 req = its.objects.auto_capture_request() 95 req['android.statistics.lensShadingMapMode'] = 1 96 req['android.shading.mode'] = mode 97 reqs.append(req) 98 99 caps = cam.do_capture(reqs, cap_fmt) 100 101 # shading_maps[mode][loop] 102 shading_maps = [[[] for loop in range(NUM_SHADING_MODE_SWITCH_LOOPS)] 103 for mode in range(num_shading_modes)] 104 105 # Get the shading maps out of capture results 106 for i in range(len(caps)/NUM_FRAMES): 107 shading_maps[i%num_shading_modes][i/NUM_SHADING_MODE_SWITCH_LOOPS] = \ 108 caps[(i+1)*NUM_FRAMES-1]['metadata']['android.statistics.lensShadingCorrectionMap']['map'] 109 110 # Draw the maps 111 for mode in range(num_shading_modes): 112 for i in range(NUM_SHADING_MODE_SWITCH_LOOPS): 113 pylab.clf() 114 pylab.figure(figsize=(5, 5)) 115 pylab.subplot(2, 1, 1) 116 pylab.plot(range(num_map_gains), shading_maps[mode][i], '-r.', 117 label='shading', alpha=0.7) 118 pylab.plot(range(num_map_gains), reference_maps[mode], '-g.', 119 label='ref', alpha=0.7) 120 pylab.xlim([0, num_map_gains]) 121 pylab.ylim([0.9, 4.0]) 122 name = '%s_ls_maps_mode_%d_loop_%d' % (NAME, mode, i) 123 pylab.title(name) 124 pylab.xlabel('Map gains') 125 pylab.ylabel('Lens shading maps') 126 pylab.legend(loc='upper center', numpoints=1, fancybox=True) 127 128 pylab.subplot(2, 1, 2) 129 shading_ref_ratio = numpy.divide( 130 shading_maps[mode][i], reference_maps[mode]) 131 pylab.plot(range(num_map_gains), shading_ref_ratio, '-b.', 132 clip_on=False) 133 pylab.xlim([0, num_map_gains]) 134 pylab.ylim([1.0-THRESHOLD_DIFF_RATIO, 1.0+THRESHOLD_DIFF_RATIO]) 135 pylab.title('Shading/reference Maps Ratio vs Gain') 136 pylab.xlabel('Map gains') 137 pylab.ylabel('Shading/ref maps ratio') 138 139 pylab.tight_layout() 140 matplotlib.pyplot.savefig('%s.png' % name) 141 142 for mode in range(num_shading_modes): 143 if mode == 0: 144 print 'Verifying lens shading maps with mode %s are all 1.0' % ( 145 SHADING_MODES[mode]) 146 else: 147 print 'Verifying lens shading maps with mode %s are similar' % ( 148 SHADING_MODES[mode]) 149 for i in range(NUM_SHADING_MODE_SWITCH_LOOPS): 150 e_msg = 'FAIL mode: %s, loop: %d, THRESH: %.2f' % ( 151 SHADING_MODES[mode], i, THRESHOLD_DIFF_RATIO) 152 assert (numpy.allclose(shading_maps[mode][i], 153 reference_maps[mode], 154 rtol=THRESHOLD_DIFF_RATIO)), e_msg 155 156if __name__ == '__main__': 157 main() 158