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 29AWB_AUTO_ATOL = 0.10 30AWB_AUTO_RTOL = 0.25 31AWB_MANUAL_ATOL = 0.05 32NAME = os.path.splitext(os.path.basename(__file__))[0] 33TONEMAP_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 image_processing_utils.write_image(img, '%s_%s.jpg' % ( 49 os.path.join(log_path, NAME), cap_name)) 50 awb_gains = cap['metadata']['android.colorCorrection.gains'] 51 awb_xform = capture_request_utils.rational_to_float( 52 cap['metadata']['android.colorCorrection.transform']) 53 logging.debug('%s gains: %s', cap_name, str(awb_gains)) 54 logging.debug('%s transform: %s', cap_name, str(awb_xform)) 55 return awb_gains, awb_xform 56 57 58class AutoVsManualTest(its_base_test.ItsBaseTest): 59 """Capture auto and manual shots that should look the same. 60 61 Manual shots taken with just manual WB, and also with manual WB+tonemap. 62 63 In all cases, the general color/look of the shots should be the same, 64 however there can be variations in brightness/contrast due to different 65 'auto' ISP blocks that may be disabled in the manual flows. 66 """ 67 68 def test_auto_vs_manual(self): 69 logging.debug('Starting %s', NAME) 70 with its_session_utils.ItsSession( 71 device_id=self.dut.serial, 72 camera_id=self.camera_id, 73 hidden_physical_id=self.hidden_physical_id) as cam: 74 props = cam.get_camera_properties() 75 props = cam.override_with_hidden_physical_camera_props(props) 76 mono_camera = camera_properties_utils.mono_camera(props) 77 log_path = self.log_path 78 79 # check SKIP conditions 80 camera_properties_utils.skip_unless( 81 camera_properties_utils.read_3a(props) and 82 camera_properties_utils.per_frame_control(props)) 83 84 # Load chart for scene 85 its_session_utils.load_scene( 86 cam, props, self.scene, self.tablet, self.chart_distance) 87 88 # Converge 3A and get the estimates 89 largest_yuv = capture_request_utils.get_largest_yuv_format(props) 90 match_ar = (largest_yuv['width'], largest_yuv['height']) 91 fmt = capture_request_utils.get_smallest_yuv_format( 92 props, match_ar=match_ar) 93 s, e, awb_gains, awb_xform, fd = cam.do_3a(get_results=True, 94 mono_camera=mono_camera) 95 awb_xform_rat = capture_request_utils.float_to_rational(awb_xform) 96 logging.debug('AE sensitivity: %d, exposure: %dms', s, e/1000000.0) 97 logging.debug('AWB gains: %s', str(awb_gains)) 98 logging.debug('AWB transform: %s', str(awb_xform)) 99 logging.debug('AF distance: %.3f', fd) 100 101 # Auto capture 102 req = capture_request_utils.auto_capture_request() 103 cap_auto = cam.do_capture(req, fmt) 104 awb_gains_a, awb_xform_a = extract_awb_gains_and_xform( 105 cap_auto, 'auto', log_path) 106 107 # Manual capture 1: WB 108 req = capture_request_utils.manual_capture_request(s, e, fd) 109 req['android.colorCorrection.transform'] = awb_xform_rat 110 req['android.colorCorrection.gains'] = awb_gains 111 cap_man1 = cam.do_capture(req, fmt) 112 awb_gains_m1, awb_xform_m1 = extract_awb_gains_and_xform( 113 cap_man1, 'manual_wb', log_path) 114 115 # Manual capture 2: WB + tonemap 116 req['android.tonemap.mode'] = 0 117 req['android.tonemap.curve'] = {'red': TONEMAP_GAMMA, 118 'green': TONEMAP_GAMMA, 119 'blue': TONEMAP_GAMMA} 120 cap_man2 = cam.do_capture(req, fmt) 121 awb_gains_m2, awb_xform_m2 = extract_awb_gains_and_xform( 122 cap_man2, 'manual_wb_tm', log_path) 123 124 # Check AWB gains & transform in manual results match values from do_3a 125 for g, x in [(awb_gains_m1, awb_xform_m1), (awb_gains_m2, awb_xform_m2)]: 126 e_msg = 'awb_xform 3A: %s, manual: %s, ATOL=%.2f' % ( 127 str(awb_xform), str(x), AWB_MANUAL_ATOL) 128 assert np.allclose(awb_xform, x, atol=AWB_MANUAL_ATOL, rtol=0), e_msg 129 e_msg = 'awb_gains 3A: %s, manual: %s, ATOL=%.2f' % ( 130 str(awb_gains), str(g), AWB_MANUAL_ATOL) 131 assert np.allclose(awb_gains, g, atol=AWB_MANUAL_ATOL, rtol=0), e_msg 132 133 # Check AWB gains & transform in auto results match values from do_3a 134 e_msg = 'awb_xform 3A: %s, auto: %s, RTOL=%.2f, ATOL=%.2f' % ( 135 str(awb_xform), str(awb_xform_a), AWB_AUTO_RTOL, AWB_AUTO_ATOL) 136 assert np.allclose(awb_xform_a, awb_xform, atol=AWB_AUTO_ATOL, 137 rtol=AWB_AUTO_RTOL), e_msg 138 e_msg = 'awb_gains 3A: %s, auto: %s, RTOL=%.2f, ATOL=%.2f' % ( 139 str(awb_gains), str(awb_gains_a), AWB_AUTO_RTOL, AWB_AUTO_ATOL) 140 assert np.allclose(awb_gains_a, awb_gains, atol=AWB_AUTO_ATOL, 141 rtol=AWB_AUTO_RTOL), e_msg 142 143if __name__ == '__main__': 144 test_runner.main() 145 146