• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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