blob: e3143f40d71de7369faa31e844af5bb48e87ff69 [file] [log] [blame]
# Copyright 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Verifies android.shading.mode parameter is applied."""
import logging
import os.path
import matplotlib
from matplotlib import pylab
from mobly import test_runner
import numpy as np
import its_base_test
import camera_properties_utils
import capture_request_utils
import its_session_utils
_NAME = os.path.splitext(os.path.basename(__file__))[0]
_NUM_FRAMES = 4 # number of frames for temporal info to settle
_NUM_SWITCH_LOOPS = 3
_SHADING_MODES = {0: 'LSC_OFF', 1: 'LSC_FAST', 2: 'LSC_HQ'}
_NUM_SHADING_MODES = len(_SHADING_MODES)
_THRESHOLD_DIFF_RATIO = 0.15
def create_plots(shading_maps, reference_maps, num_map_gains, log_path):
"""Create 2 panel plot from data."""
for mode in range(_NUM_SHADING_MODES):
for i in range(_NUM_SWITCH_LOOPS):
pylab.clf()
pylab.figure(figsize=(5, 5))
pylab.subplot(2, 1, 1)
pylab.plot(range(num_map_gains), shading_maps[mode][i], '-r.',
label='shading', alpha=0.7)
pylab.plot(range(num_map_gains), reference_maps[mode], '-g.',
label='ref', alpha=0.7)
pylab.xlim([0, num_map_gains])
pylab.ylim([0.9, 4.0])
name_suffix = 'ls_maps_mode_%d_loop_%d' % (mode, i)
pylab.title('%s_%s' % (_NAME, name_suffix))
pylab.xlabel('Map gains')
pylab.ylabel('Lens shading maps')
pylab.legend(loc='upper center', numpoints=1, fancybox=True)
pylab.subplot(2, 1, 2)
shading_ref_ratio = np.divide(
shading_maps[mode][i], reference_maps[mode])
pylab.plot(range(num_map_gains), shading_ref_ratio, '-b.', clip_on=False)
pylab.xlim([0, num_map_gains])
pylab.ylim([1.0-_THRESHOLD_DIFF_RATIO, 1.0+_THRESHOLD_DIFF_RATIO])
pylab.title('Shading/reference Maps Ratio vs Gain')
pylab.xlabel('Map gains')
pylab.ylabel('Shading/reference maps ratio')
pylab.tight_layout()
matplotlib.pyplot.savefig(
f'{os.path.join(log_path, _NAME)}_{name_suffix}.png')
class ParamShadingModeTest(its_base_test.ItsBaseTest):
"""Test that the android.shading.mode param is applied.
Switches shading modes and checks that the lens shading maps are
modified as expected.
Lens shading correction modes are OFF=0, FAST=1, and HQ=2.
Uses smallest yuv size matching the aspect ratio of largest yuv size to
reduce some USB bandwidth overhead since we are only looking at output
metadata in this test.
First asserts all modes are supported. Then runs 2 captures.
cap1: switches shading modes several times and gets reference maps
cap2: gets the lens shading maps while switching modes in 1 session
Creates plots of reference maps and shading maps.
Asserts proper behavior:
1. Lens shading maps with OFF are all 1.0
2. Lens shading maps with FAST are similar after switching shading modes
3. Lens shading maps with HQ are similar after switching shading modes.
"""
def test_param_shading_mode(self):
logging.debug('Starting %s', _NAME)
with its_session_utils.ItsSession(
device_id=self.dut.serial,
camera_id=self.camera_id,
hidden_physical_id=self.hidden_physical_id) as cam:
props = cam.get_camera_properties()
props = cam.override_with_hidden_physical_camera_props(props)
camera_properties_utils.skip_unless(
camera_properties_utils.per_frame_control(props) and
camera_properties_utils.lsc_map(props) and
camera_properties_utils.lsc_off(props))
log_path = self.log_path
# Load chart for scene
its_session_utils.load_scene(
cam, props, self.scene, self.tablet, self.chart_distance)
# lsc devices support all modes
if set(props.get('android.shading.availableModes')) != set(
_SHADING_MODES.keys()):
raise KeyError('Available modes: %s, SHADING_MODEs: %s.'
% str(props.get('android.shading.availableModes')),
[*_SHADING_MODES])
# get smallest matching fmt
mono_camera = camera_properties_utils.mono_camera(props)
cam.do_3a(mono_camera=mono_camera)
largest_yuv_fmt = capture_request_utils.get_largest_yuv_format(props)
largest_yuv_size = (largest_yuv_fmt['width'], largest_yuv_fmt['height'])
cap_fmt = capture_request_utils.get_smallest_yuv_format(
props, match_ar=largest_yuv_size)
# cap1
reference_maps = [[] for mode in range(_NUM_SHADING_MODES)]
num_map_gains = 0
for mode in range(1, _NUM_SHADING_MODES):
req = capture_request_utils.auto_capture_request()
req['android.statistics.lensShadingMapMode'] = 1
req['android.shading.mode'] = mode
cap_res = cam.do_capture(
[req]*_NUM_FRAMES, cap_fmt)[_NUM_FRAMES-1]['metadata']
lsc_map = cap_res['android.statistics.lensShadingCorrectionMap']
if not lsc_map.get('width') or not lsc_map.get('height'):
raise KeyError('width or height not in LSC map.')
if mode == 1:
num_map_gains = lsc_map['width'] * lsc_map['height'] * 4
reference_maps[0] = [1.0] * num_map_gains
reference_maps[mode] = lsc_map['map']
# cap2
reqs = []
for i in range(_NUM_SWITCH_LOOPS):
for mode in range(_NUM_SHADING_MODES):
for _ in range(_NUM_FRAMES):
req = capture_request_utils.auto_capture_request()
req['android.statistics.lensShadingMapMode'] = 1
req['android.shading.mode'] = mode
reqs.append(req)
caps = cam.do_capture(reqs, cap_fmt)
# Populate shading maps from cap2 results
shading_maps = [[[] for loop in range(_NUM_SWITCH_LOOPS)]
for mode in range(_NUM_SHADING_MODES)]
for i in range(len(caps)//_NUM_FRAMES):
shading_maps[i%_NUM_SHADING_MODES][i//_NUM_SWITCH_LOOPS] = caps[
(i+1)*_NUM_FRAMES-1]['metadata'][
'android.statistics.lensShadingCorrectionMap']['map']
# Plot the shading and reference maps
create_plots(shading_maps, reference_maps, num_map_gains, log_path)
# Assert proper behavior
for mode in range(_NUM_SHADING_MODES):
if mode == 0:
logging.debug('Verifying lens shading maps with mode %s are all 1.0',
_SHADING_MODES[mode])
else:
logging.debug('Verifying lens shading maps with mode %s are similar',
_SHADING_MODES[mode])
for i in range(_NUM_SWITCH_LOOPS):
if not (np.allclose(shading_maps[mode][i], reference_maps[mode],
rtol=_THRESHOLD_DIFF_RATIO)):
raise AssertionError(f'FAIL mode: {_SHADING_MODES[mode]}, '
f'loop: {i}, THRESH: {_THRESHOLD_DIFF_RATIO}')
if __name__ == '__main__':
test_runner.main()