• 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
36
37def create_plots(shading_maps, reference_maps, num_map_gains, log_path):
38  """Create 2 panel plot from data."""
39  for mode in range(_NUM_SHADING_MODES):
40    for i in range(_NUM_SWITCH_LOOPS):
41      pylab.clf()
42      pylab.figure(figsize=(5, 5))
43      pylab.subplot(2, 1, 1)
44      pylab.plot(range(num_map_gains), shading_maps[mode][i], '-r.',
45                 label='shading', alpha=0.7)
46      pylab.plot(range(num_map_gains), reference_maps[mode], '-g.',
47                 label='ref', alpha=0.7)
48      pylab.xlim([0, num_map_gains])
49      pylab.ylim([0.9, 4.0])
50      name_suffix = 'ls_maps_mode_%d_loop_%d' % (mode, i)
51      pylab.title('%s_%s' % (_NAME, name_suffix))
52      pylab.xlabel('Map gains')
53      pylab.ylabel('Lens shading maps')
54      pylab.legend(loc='upper center', numpoints=1, fancybox=True)
55
56      pylab.subplot(2, 1, 2)
57      shading_ref_ratio = np.divide(
58          shading_maps[mode][i], reference_maps[mode])
59      pylab.plot(range(num_map_gains), shading_ref_ratio, '-b.', clip_on=False)
60      pylab.xlim([0, num_map_gains])
61      pylab.ylim([1.0-_THRESHOLD_DIFF_RATIO, 1.0+_THRESHOLD_DIFF_RATIO])
62      pylab.title('Shading/reference Maps Ratio vs Gain')
63      pylab.xlabel('Map gains')
64      pylab.ylabel('Shading/reference maps ratio')
65
66      pylab.tight_layout()
67      matplotlib.pyplot.savefig(
68          f'{os.path.join(log_path, _NAME)}_{name_suffix}.png')
69
70
71class ParamShadingModeTest(its_base_test.ItsBaseTest):
72  """Test that the android.shading.mode param is applied.
73
74  Switches shading modes and checks that the lens shading maps are
75  modified as expected.
76
77  Lens shading correction modes are OFF=0, FAST=1, and HQ=2.
78
79  Uses smallest yuv size matching the aspect ratio of largest yuv size to
80  reduce some USB bandwidth overhead since we are only looking at output
81  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('Available modes: %s, SHADING_MODEs: %s.'
118                       % str(props.get('android.shading.availableModes')),
119                       [*_SHADING_MODES])
120
121      # get smallest matching fmt
122      mono_camera = camera_properties_utils.mono_camera(props)
123      cam.do_3a(mono_camera=mono_camera)
124      largest_yuv_fmt = capture_request_utils.get_largest_yuv_format(props)
125      largest_yuv_size = (largest_yuv_fmt['width'], largest_yuv_fmt['height'])
126      cap_fmt = capture_request_utils.get_smallest_yuv_format(
127          props, match_ar=largest_yuv_size)
128
129      # cap1
130      reference_maps = [[] for mode in range(_NUM_SHADING_MODES)]
131      num_map_gains = 0
132      for mode in range(1, _NUM_SHADING_MODES):
133        req = capture_request_utils.auto_capture_request()
134        req['android.statistics.lensShadingMapMode'] = 1
135        req['android.shading.mode'] = mode
136        cap_res = cam.do_capture(
137            [req]*_NUM_FRAMES, cap_fmt)[_NUM_FRAMES-1]['metadata']
138        lsc_map = cap_res['android.statistics.lensShadingCorrectionMap']
139        if not lsc_map.get('width') or not lsc_map.get('height'):
140          raise KeyError('width or height not in LSC map.')
141        if mode == 1:
142          num_map_gains = lsc_map['width'] * lsc_map['height'] * 4
143          reference_maps[0] = [1.0] * num_map_gains
144        reference_maps[mode] = lsc_map['map']
145
146      # cap2
147      reqs = []
148      for i in range(_NUM_SWITCH_LOOPS):
149        for mode in range(_NUM_SHADING_MODES):
150          for _ in range(_NUM_FRAMES):
151            req = capture_request_utils.auto_capture_request()
152            req['android.statistics.lensShadingMapMode'] = 1
153            req['android.shading.mode'] = mode
154            reqs.append(req)
155      caps = cam.do_capture(reqs, cap_fmt)
156
157      # Populate shading maps from cap2 results
158      shading_maps = [[[] for loop in range(_NUM_SWITCH_LOOPS)]
159                      for mode in range(_NUM_SHADING_MODES)]
160      for i in range(len(caps)//_NUM_FRAMES):
161        shading_maps[i%_NUM_SHADING_MODES][i//_NUM_SWITCH_LOOPS] = caps[
162            (i+1)*_NUM_FRAMES-1]['metadata'][
163                'android.statistics.lensShadingCorrectionMap']['map']
164
165      # Plot the shading and reference maps
166      create_plots(shading_maps, reference_maps, num_map_gains, log_path)
167
168      # Assert proper behavior
169      for mode in range(_NUM_SHADING_MODES):
170        if mode == 0:
171          logging.debug('Verifying lens shading maps with mode %s are all 1.0',
172                        _SHADING_MODES[mode])
173        else:
174          logging.debug('Verifying lens shading maps with mode %s are similar',
175                        _SHADING_MODES[mode])
176        for i in range(_NUM_SWITCH_LOOPS):
177          if not (np.allclose(shading_maps[mode][i], reference_maps[mode],
178                              rtol=_THRESHOLD_DIFF_RATIO)):
179            raise AssertionError(f'FAIL mode: {_SHADING_MODES[mode]}, '
180                                 f'loop: {i}, THRESH: {_THRESHOLD_DIFF_RATIO}')
181
182if __name__ == '__main__':
183  test_runner.main()
184