1# Copyright 2014 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 auto and manual captures are similar with same scene.""" 15 16 17import logging 18import math 19import os.path 20from mobly import test_runner 21import numpy as np 22 23import its_base_test 24import camera_properties_utils 25import capture_request_utils 26import image_processing_utils 27import its_session_utils 28 29_AWB_AUTO_ATOL = 0.10 30_AWB_AUTO_RTOL = 0.25 31_AWB_MANUAL_ATOL = 0.05 32_NAME = os.path.splitext(os.path.basename(__file__))[0] 33_TONEMAP_GAMMA = sum([[t/63.0, math.pow(t/63.0, 1/2.2)] for t in range(64)], []) 34 35 36def extract_awb_gains_and_xform(cap, cap_name, log_path): 37 """Extract the AWB transform and gains, save image, and log info. 38 39 Args: 40 cap: camera capture 41 cap_name: text string to identify cap type 42 log_path: location to save images 43 44 Returns: 45 awb_gains, awb_xform 46 """ 47 img = image_processing_utils.convert_capture_to_rgb_image(cap) 48 name_with_log_path = os.path.join(log_path, _NAME) 49 image_processing_utils.write_image( 50 img, f'{name_with_log_path}_{cap_name}.jpg') 51 awb_gains = cap['metadata']['android.colorCorrection.gains'] 52 awb_xform = capture_request_utils.rational_to_float( 53 cap['metadata']['android.colorCorrection.transform']) 54 logging.debug('%s gains: %s', cap_name, str(awb_gains)) 55 logging.debug('%s transform: %s', cap_name, str(awb_xform)) 56 return awb_gains, awb_xform 57 58 59class AutoVsManualTest(its_base_test.ItsBaseTest): 60 """Capture auto and manual shots that should look the same. 61 62 Manual shots taken with just manual WB, and also with manual WB+tonemap. 63 64 In all cases, the general color/look of the shots should be the same, 65 however there can be variations in brightness/contrast due to different 66 'auto' ISP blocks that may be disabled in the manual flows. 67 """ 68 69 def test_auto_vs_manual(self): 70 logging.debug('Starting %s', _NAME) 71 with its_session_utils.ItsSession( 72 device_id=self.dut.serial, 73 camera_id=self.camera_id, 74 hidden_physical_id=self.hidden_physical_id) as cam: 75 props = cam.get_camera_properties() 76 props = cam.override_with_hidden_physical_camera_props(props) 77 mono_camera = camera_properties_utils.mono_camera(props) 78 log_path = self.log_path 79 80 # check SKIP conditions 81 camera_properties_utils.skip_unless( 82 camera_properties_utils.read_3a(props) and 83 camera_properties_utils.per_frame_control(props)) 84 85 # Load chart for scene 86 its_session_utils.load_scene( 87 cam, props, self.scene, self.tablet, self.chart_distance) 88 89 # Converge 3A and get the estimates 90 largest_yuv = capture_request_utils.get_largest_yuv_format(props) 91 match_ar = (largest_yuv['width'], largest_yuv['height']) 92 fmt = capture_request_utils.get_near_vga_yuv_format( 93 props, match_ar=match_ar) 94 s, e, awb_gains, awb_xform, fd = cam.do_3a(get_results=True, 95 mono_camera=mono_camera) 96 awb_xform_rat = capture_request_utils.float_to_rational(awb_xform) 97 logging.debug('AE sensitivity: %d, exposure: %dms', s, e/1000000.0) 98 logging.debug('AWB gains: %s', str(awb_gains)) 99 logging.debug('AWB transform: %s', str(awb_xform)) 100 logging.debug('AF distance: %.3f', fd) 101 102 # Auto capture 103 req = capture_request_utils.auto_capture_request() 104 cap_auto = cam.do_capture(req, fmt) 105 awb_gains_a, awb_xform_a = extract_awb_gains_and_xform( 106 cap_auto, 'auto', log_path) 107 108 # Manual capture 1: WB 109 req = capture_request_utils.manual_capture_request(s, e, fd) 110 req['android.colorCorrection.transform'] = awb_xform_rat 111 req['android.colorCorrection.gains'] = awb_gains 112 cap_man1 = cam.do_capture(req, fmt) 113 awb_gains_m1, awb_xform_m1 = extract_awb_gains_and_xform( 114 cap_man1, 'manual_wb', log_path) 115 116 # Manual capture 2: WB + tonemap 117 req['android.tonemap.mode'] = 0 118 req['android.tonemap.curve'] = {'red': _TONEMAP_GAMMA, 119 'green': _TONEMAP_GAMMA, 120 'blue': _TONEMAP_GAMMA} 121 cap_man2 = cam.do_capture(req, fmt) 122 awb_gains_m2, awb_xform_m2 = extract_awb_gains_and_xform( 123 cap_man2, 'manual_wb_tm', log_path) 124 125 # Check AWB gains & transform in manual results match values from do_3a 126 for g, x in [(awb_gains_m1, awb_xform_m1), (awb_gains_m2, awb_xform_m2)]: 127 if not np.allclose(awb_xform, x, atol=_AWB_MANUAL_ATOL, rtol=0): 128 raise AssertionError( 129 f'awb_xform 3A: {awb_xform}, ' 130 f'manual: {x}, ATOL={_AWB_MANUAL_ATOL}') 131 if not np.allclose(awb_gains, g, atol=_AWB_MANUAL_ATOL, rtol=0): 132 raise AssertionError( 133 f'awb_gains 3A: {awb_gains}, ' 134 f'manual: {g}, ATOL={_AWB_MANUAL_ATOL}') 135 136 # Check AWB gains & transform in auto results match values from do_3a 137 if not np.allclose(awb_xform_a, awb_xform, atol=_AWB_AUTO_ATOL, 138 rtol=_AWB_AUTO_RTOL): 139 raise AssertionError(f'awb_xform 3A: {awb_xform}, auto: {awb_xform_a}, ' 140 f'RTOL={_AWB_AUTO_RTOL}, ATOL={_AWB_AUTO_ATOL}') 141 if not np.allclose(awb_gains_a, awb_gains, atol=_AWB_AUTO_ATOL, 142 rtol=_AWB_AUTO_RTOL): 143 raise AssertionError(f'awb_gains 3A: {awb_gains}, auto: {awb_gains_a}, ' 144 f'RTOL={_AWB_AUTO_RTOL}, ATOL={_AWB_AUTO_ATOL}') 145 146if __name__ == '__main__': 147 test_runner.main() 148 149