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 different combinations of output formats.""" 15 16 17import logging 18import os 19 20from mobly import test_runner 21 22import its_base_test 23import camera_properties_utils 24import capture_request_utils 25import image_processing_utils 26import its_session_utils 27import target_exposure_utils 28 29 30_AUTO_REQUEST_MODE = 1 31_NAME = os.path.splitext(os.path.basename(__file__))[0] 32_STOP_AT_FIRST_FAILURE = False # change to True to have test break @ 1st FAIL 33 34 35class FormatCombosTest(its_base_test.ItsBaseTest): 36 """Test different combinations of output formats. 37 38 Note the test does not require a specific target but does perform 39 both automatic and manual captures so it requires a fixed scene 40 where 3A can converge. 41 """ 42 43 def test_format_combos(self): 44 logging.debug('Starting %s', _NAME) 45 with its_session_utils.ItsSession( 46 device_id=self.dut.serial, 47 camera_id=self.camera_id, 48 hidden_physical_id=self.hidden_physical_id) as cam: 49 50 props = cam.get_camera_properties() 51 props = cam.override_with_hidden_physical_camera_props(props) 52 53 # Load chart for scene 54 its_session_utils.load_scene( 55 cam, props, self.scene, self.tablet, self.chart_distance) 56 57 successes = [] 58 failures = [] 59 debug = self.debug_mode 60 61 # Up to 2 possible request types: auto and manual 62 req_aut = capture_request_utils.auto_capture_request() 63 reqs = [req_aut] 64 if camera_properties_utils.compute_target_exposure(props): 65 e, s = target_exposure_utils.get_target_exposure_combos( 66 self.log_path, cam)['midExposureTime'] 67 req_man = capture_request_utils.manual_capture_request(s, e) 68 reqs.append(req_man) 69 70 # Up to 10 different combos of output formats. 71 # Some are single surfaces and some are multiple surfaces. 72 wyuv, hyuv = capture_request_utils.get_available_output_sizes( 73 'yuv', props)[-1] 74 wjpg, hjpg = capture_request_utils.get_available_output_sizes( 75 'jpg', props)[-1] 76 fmt_yuv_prev = {'format': 'yuv', 'width': wyuv, 'height': hyuv} 77 fmt_yuv_full = {'format': 'yuv'} 78 fmt_jpg_prev = {'format': 'jpeg', 'width': wjpg, 'height': hjpg} 79 fmt_jpg_full = {'format': 'jpeg'} 80 fmt_combos = [ 81 [fmt_yuv_prev], 82 [fmt_yuv_full], 83 [fmt_jpg_prev], 84 [fmt_jpg_full], 85 [fmt_yuv_prev, fmt_jpg_prev], 86 [fmt_yuv_prev, fmt_jpg_full], 87 ] 88 if camera_properties_utils.raw16(props): 89 fmt_raw_full = {'format': 'raw'} 90 fmt_combos.extend([ 91 [fmt_raw_full], 92 [fmt_yuv_prev, fmt_raw_full], 93 [fmt_yuv_prev, fmt_jpg_prev, fmt_raw_full], 94 [fmt_yuv_prev, fmt_jpg_full, fmt_raw_full]]) 95 96 if camera_properties_utils.y8(props): 97 wy8, hy8 = capture_request_utils.get_available_output_sizes( 98 'y8', props)[-1] 99 fmt_y8_prev = {'format': 'y8', 'width': wy8, 'height': hy8} 100 fmt_y8_full = {'format': 'y8'} 101 fmt_combos.extend([ 102 [fmt_y8_prev], 103 [fmt_y8_full]]) 104 105 # Two different burst lengths: single frame and 3 frames. 106 burst_lens = [1, 3] 107 108 # There are 2xlen(fmt_combos)x2 different combinations. 109 # Run through them all. 110 n = 0 111 for r, req in enumerate(reqs): 112 if req['android.control.mode'] == _AUTO_REQUEST_MODE: 113 req_str = 'auto' 114 else: 115 req_str = 'manual' 116 for f, fmt_combo in enumerate(fmt_combos): 117 for b, burst_len in enumerate(burst_lens): 118 try: 119 caps = cam.do_capture([req] * burst_len, fmt_combo) 120 successes.append((n, r, f, b)) 121 logging.debug('Success[%02d]', n) 122 logging.debug(' req: %s', req_str) 123 logging.debug(' fmt: %s', str(fmt_combo)) 124 logging.debug(' burst_len: %d\n', burst_len) 125 126 # Dump the captures out to jpegs in debug mode. 127 if debug: 128 name_with_path = os.path.join(self.log_path, _NAME) 129 if not isinstance(caps, list): 130 caps = [caps] 131 elif isinstance(caps[0], list): 132 caps = sum(caps, []) 133 for c, cap in enumerate(caps): 134 img = image_processing_utils.convert_capture_to_rgb_image( 135 cap, props=props) 136 img_name = (f'{name_with_path}_{n:02d}_{req_str}_fmt{f}_' 137 f'burst{burst_len}_cap{c}.jpg') 138 image_processing_utils.write_image(img, img_name) 139 # pylint: disable=broad-except 140 except Exception as e: 141 logging.error(e) 142 logging.error('Failure[%02d]', n) 143 logging.debug(' req: %s', req_str) 144 logging.error(' fmt: %s', str(fmt_combo)) 145 logging.error(' burst_len: %d\n', burst_len) 146 failures.append((n, r, f, b)) 147 if _STOP_AT_FIRST_FAILURE: 148 raise AssertionError( 149 f'Capture fail at combo req: {req_str}, fmt: {fmt_combo}, ' 150 f'burst: {burst_len}') from e 151 n += 1 152 153 num_fail = len(failures) 154 num_success = len(successes) 155 num_total = len(reqs)*len(fmt_combos)*len(burst_lens) 156 num_not_run = num_total - num_success - num_fail 157 158 logging.debug('Success: %d / %d', num_success, num_total) 159 160 # assert all combinations successfully capture 161 if num_fail != 0: 162 raise AssertionError(f'Number of fails: {num_fail} / {num_total}') 163 if num_not_run > 0: 164 raise AssertionError(f'Number of combos not run: {num_not_run}') 165 166if __name__ == '__main__': 167 test_runner.main() 168