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"""Verifies android.shading.mode parameter is applied.""" 15 16 17import logging 18import os.path 19import matplotlib 20from matplotlib import pylab 21from mobly import test_runner 22import numpy as np 23 24import its_base_test 25import camera_properties_utils 26import capture_request_utils 27import its_session_utils 28 29_NAME = os.path.splitext(os.path.basename(__file__))[0] 30_NUM_FRAMES = 4 # number of frames for temporal info to settle 31_NUM_SWITCH_LOOPS = 3 32_SHADING_MODES = {0: 'LSC_OFF', 1: 'LSC_FAST', 2: 'LSC_HQ'} 33_NUM_SHADING_MODES = len(_SHADING_MODES) 34_THRESHOLD_DIFF_RATIO = 0.15 35_VGA_W, _VGA_H = 640, 480 36 37 38def create_plots(shading_maps, reference_maps, num_map_gains, log_path): 39 """Create 2 panel plot from data.""" 40 for mode in range(_NUM_SHADING_MODES): 41 for i in range(_NUM_SWITCH_LOOPS): 42 pylab.clf() 43 pylab.figure(figsize=(5, 5)) 44 pylab.subplot(2, 1, 1) 45 pylab.plot(range(num_map_gains), shading_maps[mode][i], '-r.', 46 label='shading', alpha=0.7) 47 pylab.plot(range(num_map_gains), reference_maps[mode], '-g.', 48 label='ref', alpha=0.7) 49 pylab.xlim([0, num_map_gains]) 50 pylab.ylim([0.9, 4.0]) 51 name_suffix = f'ls_maps_mode_{mode}_loop_{i}' 52 pylab.title(f'{_NAME}_{name_suffix}') 53 pylab.xlabel('Map gains') 54 pylab.ylabel('Lens shading maps') 55 pylab.legend(loc='upper center', numpoints=1, fancybox=True) 56 57 pylab.subplot(2, 1, 2) 58 shading_ref_ratio = np.divide( 59 shading_maps[mode][i], reference_maps[mode]) 60 pylab.plot(range(num_map_gains), shading_ref_ratio, '-b.', clip_on=False) 61 pylab.xlim([0, num_map_gains]) 62 pylab.ylim([1.0-_THRESHOLD_DIFF_RATIO, 1.0+_THRESHOLD_DIFF_RATIO]) 63 pylab.title('Shading/reference Maps Ratio vs Gain') 64 pylab.xlabel('Map gains') 65 pylab.ylabel('Shading/reference maps ratio') 66 67 pylab.tight_layout() 68 matplotlib.pyplot.savefig( 69 f'{os.path.join(log_path, _NAME)}_{name_suffix}.png') 70 71 72class ParamShadingModeTest(its_base_test.ItsBaseTest): 73 """Test that the android.shading.mode param is applied. 74 75 Switches shading modes and checks that the lens shading maps are 76 modified as expected. 77 78 Lens shading correction modes are OFF=0, FAST=1, and HQ=2. 79 80 Uses VGA sized captures to reduce some USB bandwidth overhead since we are 81 only looking at output metadata in this test. 82 83 First asserts all modes are supported. Then runs 2 captures. 84 85 cap1: switches shading modes several times and gets reference maps 86 cap2: gets the lens shading maps while switching modes in 1 session 87 88 Creates plots of reference maps and shading maps. 89 90 Asserts proper behavior: 91 1. Lens shading maps with OFF are all 1.0 92 2. Lens shading maps with FAST are similar after switching shading modes 93 3. Lens shading maps with HQ are similar after switching shading modes. 94 """ 95 96 def test_param_shading_mode(self): 97 logging.debug('Starting %s', _NAME) 98 with its_session_utils.ItsSession( 99 device_id=self.dut.serial, 100 camera_id=self.camera_id, 101 hidden_physical_id=self.hidden_physical_id) as cam: 102 props = cam.get_camera_properties() 103 props = cam.override_with_hidden_physical_camera_props(props) 104 camera_properties_utils.skip_unless( 105 camera_properties_utils.per_frame_control(props) and 106 camera_properties_utils.lsc_map(props) and 107 camera_properties_utils.lsc_off(props)) 108 log_path = self.log_path 109 110 # Load chart for scene 111 its_session_utils.load_scene( 112 cam, props, self.scene, self.tablet, self.chart_distance) 113 114 # lsc devices support all modes 115 if set(props.get('android.shading.availableModes')) != set( 116 _SHADING_MODES.keys()): 117 raise KeyError( 118 f"Available modes: {props.get('android.shading.availableModes')}, " 119 f'SHADING_MODEs: {[*_SHADING_MODES]}.') 120 121 # define fmt 122 mono_camera = camera_properties_utils.mono_camera(props) 123 cam.do_3a(mono_camera=mono_camera) 124 cap_fmt = {'format': 'yuv', 'width': _VGA_W, 'height': _VGA_H} 125 logging.debug('Capture format: %s', str(cap_fmt)) 126 127 # cap1 128 reference_maps = [[] for mode in range(_NUM_SHADING_MODES)] 129 num_map_gains = 0 130 for mode in range(1, _NUM_SHADING_MODES): 131 req = capture_request_utils.auto_capture_request() 132 req['android.statistics.lensShadingMapMode'] = 1 133 req['android.shading.mode'] = mode 134 cap_res = cam.do_capture( 135 [req]*_NUM_FRAMES, cap_fmt)[_NUM_FRAMES-1]['metadata'] 136 lsc_map = cap_res['android.statistics.lensShadingCorrectionMap'] 137 if not lsc_map.get('width') or not lsc_map.get('height'): 138 raise KeyError('width or height not in LSC map.') 139 if mode == 1: 140 num_map_gains = lsc_map['width'] * lsc_map['height'] * 4 141 reference_maps[0] = [1.0] * num_map_gains 142 reference_maps[mode] = lsc_map['map'] 143 144 # cap2 145 reqs = [] 146 for i in range(_NUM_SWITCH_LOOPS): 147 for mode in range(_NUM_SHADING_MODES): 148 for _ in range(_NUM_FRAMES): 149 req = capture_request_utils.auto_capture_request() 150 req['android.statistics.lensShadingMapMode'] = 1 151 req['android.shading.mode'] = mode 152 reqs.append(req) 153 caps = cam.do_capture(reqs, cap_fmt) 154 155 # Populate shading maps from cap2 results 156 shading_maps = [[[] for loop in range(_NUM_SWITCH_LOOPS)] 157 for mode in range(_NUM_SHADING_MODES)] 158 for i in range(len(caps)//_NUM_FRAMES): 159 shading_maps[i%_NUM_SHADING_MODES][i//_NUM_SWITCH_LOOPS] = caps[ 160 (i+1)*_NUM_FRAMES-1]['metadata'][ 161 'android.statistics.lensShadingCorrectionMap']['map'] 162 163 # Plot the shading and reference maps 164 create_plots(shading_maps, reference_maps, num_map_gains, log_path) 165 166 # Assert proper behavior 167 for mode in range(_NUM_SHADING_MODES): 168 if mode == 0: 169 logging.debug('Verifying lens shading maps with mode %s are all 1.0', 170 _SHADING_MODES[mode]) 171 else: 172 logging.debug('Verifying lens shading maps with mode %s are similar', 173 _SHADING_MODES[mode]) 174 for i in range(_NUM_SWITCH_LOOPS): 175 if not (np.allclose(shading_maps[mode][i], reference_maps[mode], 176 rtol=_THRESHOLD_DIFF_RATIO)): 177 raise AssertionError(f'FAIL mode: {_SHADING_MODES[mode]}, ' 178 f'loop: {i}, THRESH: {_THRESHOLD_DIFF_RATIO}') 179 180if __name__ == '__main__': 181 test_runner.main() 182