# Copyright 2014 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 auto and manual captures are similar with same scene.""" import logging import math import os.path from mobly import test_runner import numpy as np import its_base_test import camera_properties_utils import capture_request_utils import image_processing_utils import its_session_utils _AWB_AUTO_ATOL = 0.10 _AWB_AUTO_RTOL = 0.25 _AWB_MANUAL_ATOL = 0.05 _NAME = os.path.splitext(os.path.basename(__file__))[0] _TONEMAP_GAMMA = sum([[t/63.0, math.pow(t/63.0, 1/2.2)] for t in range(64)], []) def extract_awb_gains_and_xform(cap, cap_name, log_path): """Extract the AWB transform and gains, save image, and log info. Args: cap: camera capture cap_name: text string to identify cap type log_path: location to save images Returns: awb_gains, awb_xform """ img = image_processing_utils.convert_capture_to_rgb_image(cap) name_with_log_path = os.path.join(log_path, _NAME) image_processing_utils.write_image( img, f'{name_with_log_path}_{cap_name}.jpg') awb_gains = cap['metadata']['android.colorCorrection.gains'] awb_xform = capture_request_utils.rational_to_float( cap['metadata']['android.colorCorrection.transform']) logging.debug('%s gains: %s', cap_name, str(awb_gains)) logging.debug('%s transform: %s', cap_name, str(awb_xform)) return awb_gains, awb_xform class AutoVsManualTest(its_base_test.ItsBaseTest): """Capture auto and manual shots that should look the same. Manual shots taken with just manual WB, and also with manual WB+tonemap. In all cases, the general color/look of the shots should be the same, however there can be variations in brightness/contrast due to different 'auto' ISP blocks that may be disabled in the manual flows. """ def test_auto_vs_manual(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) mono_camera = camera_properties_utils.mono_camera(props) log_path = self.log_path # check SKIP conditions camera_properties_utils.skip_unless( camera_properties_utils.read_3a(props) and camera_properties_utils.per_frame_control(props)) # Load chart for scene its_session_utils.load_scene( cam, props, self.scene, self.tablet, self.chart_distance) # Converge 3A and get the estimates largest_yuv = capture_request_utils.get_largest_yuv_format(props) match_ar = (largest_yuv['width'], largest_yuv['height']) fmt = capture_request_utils.get_near_vga_yuv_format( props, match_ar=match_ar) s, e, awb_gains, awb_xform, fd = cam.do_3a(get_results=True, mono_camera=mono_camera) awb_xform_rat = capture_request_utils.float_to_rational(awb_xform) logging.debug('AE sensitivity: %d, exposure: %dms', s, e/1000000.0) logging.debug('AWB gains: %s', str(awb_gains)) logging.debug('AWB transform: %s', str(awb_xform)) logging.debug('AF distance: %.3f', fd) # Auto capture req = capture_request_utils.auto_capture_request() cap_auto = cam.do_capture(req, fmt) awb_gains_a, awb_xform_a = extract_awb_gains_and_xform( cap_auto, 'auto', log_path) # Manual capture 1: WB req = capture_request_utils.manual_capture_request(s, e, fd) req['android.colorCorrection.transform'] = awb_xform_rat req['android.colorCorrection.gains'] = awb_gains cap_man1 = cam.do_capture(req, fmt) awb_gains_m1, awb_xform_m1 = extract_awb_gains_and_xform( cap_man1, 'manual_wb', log_path) # Manual capture 2: WB + tonemap req['android.tonemap.mode'] = 0 req['android.tonemap.curve'] = {'red': _TONEMAP_GAMMA, 'green': _TONEMAP_GAMMA, 'blue': _TONEMAP_GAMMA} cap_man2 = cam.do_capture(req, fmt) awb_gains_m2, awb_xform_m2 = extract_awb_gains_and_xform( cap_man2, 'manual_wb_tm', log_path) # Check AWB gains & transform in manual results match values from do_3a for g, x in [(awb_gains_m1, awb_xform_m1), (awb_gains_m2, awb_xform_m2)]: if not np.allclose(awb_xform, x, atol=_AWB_MANUAL_ATOL, rtol=0): raise AssertionError( f'awb_xform 3A: {awb_xform}, ' f'manual: {x}, ATOL={_AWB_MANUAL_ATOL}') if not np.allclose(awb_gains, g, atol=_AWB_MANUAL_ATOL, rtol=0): raise AssertionError( f'awb_gains 3A: {awb_gains}, ' f'manual: {g}, ATOL={_AWB_MANUAL_ATOL}') # Check AWB gains & transform in auto results match values from do_3a if not np.allclose(awb_xform_a, awb_xform, atol=_AWB_AUTO_ATOL, rtol=_AWB_AUTO_RTOL): raise AssertionError(f'awb_xform 3A: {awb_xform}, auto: {awb_xform_a}, ' f'RTOL={_AWB_AUTO_RTOL}, ATOL={_AWB_AUTO_ATOL}') if not np.allclose(awb_gains_a, awb_gains, atol=_AWB_AUTO_ATOL, rtol=_AWB_AUTO_RTOL): raise AssertionError(f'awb_gains 3A: {awb_gains}, auto: {awb_gains_a}, ' f'RTOL={_AWB_AUTO_RTOL}, ATOL={_AWB_AUTO_ATOL}') if __name__ == '__main__': test_runner.main()