1*b7c941bbSAndroid Build Coastguard Worker# Copyright 2020 The Android Open Source Project 2*b7c941bbSAndroid Build Coastguard Worker# 3*b7c941bbSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*b7c941bbSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*b7c941bbSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*b7c941bbSAndroid Build Coastguard Worker# 7*b7c941bbSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*b7c941bbSAndroid Build Coastguard Worker# 9*b7c941bbSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*b7c941bbSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*b7c941bbSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*b7c941bbSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*b7c941bbSAndroid Build Coastguard Worker# limitations under the License. 14*b7c941bbSAndroid Build Coastguard Worker"""Verify zoom ratio scales ArUco marker sizes correctly.""" 15*b7c941bbSAndroid Build Coastguard Worker 16*b7c941bbSAndroid Build Coastguard Worker 17*b7c941bbSAndroid Build Coastguard Workerimport logging 18*b7c941bbSAndroid Build Coastguard Workerimport math 19*b7c941bbSAndroid Build Coastguard Workerimport os.path 20*b7c941bbSAndroid Build Coastguard Worker 21*b7c941bbSAndroid Build Coastguard Workerimport camera_properties_utils 22*b7c941bbSAndroid Build Coastguard Workerimport capture_request_utils 23*b7c941bbSAndroid Build Coastguard Workerimport image_processing_utils 24*b7c941bbSAndroid Build Coastguard Workerimport its_base_test 25*b7c941bbSAndroid Build Coastguard Workerimport its_session_utils 26*b7c941bbSAndroid Build Coastguard Workerimport opencv_processing_utils 27*b7c941bbSAndroid Build Coastguard Workerimport cv2 28*b7c941bbSAndroid Build Coastguard Workerfrom mobly import test_runner 29*b7c941bbSAndroid Build Coastguard Workerimport numpy as np 30*b7c941bbSAndroid Build Coastguard Workerimport zoom_capture_utils 31*b7c941bbSAndroid Build Coastguard Worker 32*b7c941bbSAndroid Build Coastguard Worker_NAME = os.path.splitext(os.path.basename(__file__))[0] 33*b7c941bbSAndroid Build Coastguard Worker_NUM_STEPS = 10 34*b7c941bbSAndroid Build Coastguard Worker_TEST_FORMATS = ['yuv'] # list so can be appended for newer Android versions 35*b7c941bbSAndroid Build Coastguard Worker_TEST_REQUIRED_MPC = 33 36*b7c941bbSAndroid Build Coastguard Worker_SINGLE_CAMERA_NUMBER_OF_CAMERAS_TO_TEST = 1 37*b7c941bbSAndroid Build Coastguard Worker_ULTRAWIDE_NUMBER_OF_CAMERAS_TO_TEST = 2 # UW and W 38*b7c941bbSAndroid Build Coastguard Worker# Wider zoom ratio range will be tested by test_zoom_tele 39*b7c941bbSAndroid Build Coastguard Worker_WIDE_ZOOM_RATIO_MAX = 2.2 40*b7c941bbSAndroid Build Coastguard Worker_ZOOM_RATIO_REQUEST_RESULT_DIFF_RTOL = 0.1 41*b7c941bbSAndroid Build Coastguard Worker 42*b7c941bbSAndroid Build Coastguard Worker 43*b7c941bbSAndroid Build Coastguard Workerclass ZoomTest(its_base_test.ItsBaseTest): 44*b7c941bbSAndroid Build Coastguard Worker """Test the camera zoom behavior.""" 45*b7c941bbSAndroid Build Coastguard Worker 46*b7c941bbSAndroid Build Coastguard Worker def test_zoom(self): 47*b7c941bbSAndroid Build Coastguard Worker with its_session_utils.ItsSession( 48*b7c941bbSAndroid Build Coastguard Worker device_id=self.dut.serial, 49*b7c941bbSAndroid Build Coastguard Worker camera_id=self.camera_id, 50*b7c941bbSAndroid Build Coastguard Worker hidden_physical_id=self.hidden_physical_id) as cam: 51*b7c941bbSAndroid Build Coastguard Worker props = cam.get_camera_properties() 52*b7c941bbSAndroid Build Coastguard Worker props = cam.override_with_hidden_physical_camera_props(props) 53*b7c941bbSAndroid Build Coastguard Worker camera_properties_utils.skip_unless( 54*b7c941bbSAndroid Build Coastguard Worker camera_properties_utils.zoom_ratio_range(props)) 55*b7c941bbSAndroid Build Coastguard Worker 56*b7c941bbSAndroid Build Coastguard Worker # Load chart for scene 57*b7c941bbSAndroid Build Coastguard Worker its_session_utils.load_scene( 58*b7c941bbSAndroid Build Coastguard Worker cam, props, self.scene, self.tablet, self.chart_distance) 59*b7c941bbSAndroid Build Coastguard Worker 60*b7c941bbSAndroid Build Coastguard Worker # Determine test zoom range 61*b7c941bbSAndroid Build Coastguard Worker z_range = props['android.control.zoomRatioRange'] 62*b7c941bbSAndroid Build Coastguard Worker debug = self.debug_mode 63*b7c941bbSAndroid Build Coastguard Worker z_min, z_max = float(z_range[0]), float(z_range[1]) 64*b7c941bbSAndroid Build Coastguard Worker camera_properties_utils.skip_unless( 65*b7c941bbSAndroid Build Coastguard Worker z_max >= z_min * zoom_capture_utils.ZOOM_MIN_THRESH) 66*b7c941bbSAndroid Build Coastguard Worker z_max = min(z_max, _WIDE_ZOOM_RATIO_MAX) 67*b7c941bbSAndroid Build Coastguard Worker z_list = np.arange(z_min, z_max, (z_max - z_min) / (_NUM_STEPS - 1)) 68*b7c941bbSAndroid Build Coastguard Worker z_list = np.append(z_list, z_max) 69*b7c941bbSAndroid Build Coastguard Worker logging.debug('Testing zoom range: %s', str(z_list)) 70*b7c941bbSAndroid Build Coastguard Worker 71*b7c941bbSAndroid Build Coastguard Worker # Check media performance class 72*b7c941bbSAndroid Build Coastguard Worker media_performance_class = its_session_utils.get_media_performance_class( 73*b7c941bbSAndroid Build Coastguard Worker self.dut.serial) 74*b7c941bbSAndroid Build Coastguard Worker ultrawide_camera_found = cam.has_ultrawide_camera( 75*b7c941bbSAndroid Build Coastguard Worker facing=props['android.lens.facing']) 76*b7c941bbSAndroid Build Coastguard Worker if (media_performance_class >= _TEST_REQUIRED_MPC and 77*b7c941bbSAndroid Build Coastguard Worker cam.is_primary_camera() and 78*b7c941bbSAndroid Build Coastguard Worker ultrawide_camera_found and 79*b7c941bbSAndroid Build Coastguard Worker int(z_min) >= 1): 80*b7c941bbSAndroid Build Coastguard Worker raise AssertionError( 81*b7c941bbSAndroid Build Coastguard Worker f'With primary camera {self.camera_id}, ' 82*b7c941bbSAndroid Build Coastguard Worker f'MPC >= {_TEST_REQUIRED_MPC}, and ' 83*b7c941bbSAndroid Build Coastguard Worker 'an ultrawide camera facing in the same direction as the primary, ' 84*b7c941bbSAndroid Build Coastguard Worker 'zoom_ratio minimum must be less than 1.0. ' 85*b7c941bbSAndroid Build Coastguard Worker f'Found media performance class {media_performance_class} ' 86*b7c941bbSAndroid Build Coastguard Worker f'and minimum zoom {z_min}.') 87*b7c941bbSAndroid Build Coastguard Worker 88*b7c941bbSAndroid Build Coastguard Worker # set TOLs based on camera and test rig params 89*b7c941bbSAndroid Build Coastguard Worker if camera_properties_utils.logical_multi_camera(props): 90*b7c941bbSAndroid Build Coastguard Worker test_tols, size = zoom_capture_utils.get_test_tols_and_cap_size( 91*b7c941bbSAndroid Build Coastguard Worker cam, props, self.chart_distance, debug) 92*b7c941bbSAndroid Build Coastguard Worker else: 93*b7c941bbSAndroid Build Coastguard Worker test_tols = {} 94*b7c941bbSAndroid Build Coastguard Worker fls = props['android.lens.info.availableFocalLengths'] 95*b7c941bbSAndroid Build Coastguard Worker for fl in fls: 96*b7c941bbSAndroid Build Coastguard Worker test_tols[fl] = (zoom_capture_utils.RADIUS_RTOL, 97*b7c941bbSAndroid Build Coastguard Worker zoom_capture_utils.OFFSET_RTOL) 98*b7c941bbSAndroid Build Coastguard Worker yuv_size = capture_request_utils.get_largest_format('yuv', props) 99*b7c941bbSAndroid Build Coastguard Worker size = [yuv_size['width'], yuv_size['height']] 100*b7c941bbSAndroid Build Coastguard Worker logging.debug('capture size: %s', str(size)) 101*b7c941bbSAndroid Build Coastguard Worker logging.debug('test TOLs: %s', str(test_tols)) 102*b7c941bbSAndroid Build Coastguard Worker 103*b7c941bbSAndroid Build Coastguard Worker # determine first API level and test_formats to test 104*b7c941bbSAndroid Build Coastguard Worker test_formats = _TEST_FORMATS 105*b7c941bbSAndroid Build Coastguard Worker first_api_level = its_session_utils.get_first_api_level(self.dut.serial) 106*b7c941bbSAndroid Build Coastguard Worker if first_api_level >= its_session_utils.ANDROID14_API_LEVEL: 107*b7c941bbSAndroid Build Coastguard Worker test_formats.append(zoom_capture_utils.JPEG_STR) 108*b7c941bbSAndroid Build Coastguard Worker 109*b7c941bbSAndroid Build Coastguard Worker # do captures over zoom range and find ArUco markers with cv2 110*b7c941bbSAndroid Build Coastguard Worker img_name_stem = f'{os.path.join(self.log_path, _NAME)}' 111*b7c941bbSAndroid Build Coastguard Worker req = capture_request_utils.auto_capture_request() 112*b7c941bbSAndroid Build Coastguard Worker test_failed = False 113*b7c941bbSAndroid Build Coastguard Worker for fmt in test_formats: 114*b7c941bbSAndroid Build Coastguard Worker logging.debug('testing %s format', fmt) 115*b7c941bbSAndroid Build Coastguard Worker test_data = [] 116*b7c941bbSAndroid Build Coastguard Worker all_aruco_ids = [] 117*b7c941bbSAndroid Build Coastguard Worker all_aruco_corners = [] 118*b7c941bbSAndroid Build Coastguard Worker images = [] 119*b7c941bbSAndroid Build Coastguard Worker physical_ids = set() 120*b7c941bbSAndroid Build Coastguard Worker for z in z_list: 121*b7c941bbSAndroid Build Coastguard Worker req['android.control.zoomRatio'] = z 122*b7c941bbSAndroid Build Coastguard Worker logging.debug('zoom ratio: %.3f', z) 123*b7c941bbSAndroid Build Coastguard Worker cam.do_3a( 124*b7c941bbSAndroid Build Coastguard Worker zoom_ratio=z, 125*b7c941bbSAndroid Build Coastguard Worker out_surfaces={ 126*b7c941bbSAndroid Build Coastguard Worker 'format': fmt, 127*b7c941bbSAndroid Build Coastguard Worker 'width': size[0], 128*b7c941bbSAndroid Build Coastguard Worker 'height': size[1] 129*b7c941bbSAndroid Build Coastguard Worker }, 130*b7c941bbSAndroid Build Coastguard Worker repeat_request=None, 131*b7c941bbSAndroid Build Coastguard Worker ) 132*b7c941bbSAndroid Build Coastguard Worker cap = cam.do_capture( 133*b7c941bbSAndroid Build Coastguard Worker req, {'format': fmt, 'width': size[0], 'height': size[1]}, 134*b7c941bbSAndroid Build Coastguard Worker reuse_session=True) 135*b7c941bbSAndroid Build Coastguard Worker cap_physical_id = ( 136*b7c941bbSAndroid Build Coastguard Worker cap['metadata']['android.logicalMultiCamera.activePhysicalId'] 137*b7c941bbSAndroid Build Coastguard Worker ) 138*b7c941bbSAndroid Build Coastguard Worker cap_zoom_ratio = float(cap['metadata']['android.control.zoomRatio']) 139*b7c941bbSAndroid Build Coastguard Worker if not math.isclose(cap_zoom_ratio, z, 140*b7c941bbSAndroid Build Coastguard Worker rel_tol=_ZOOM_RATIO_REQUEST_RESULT_DIFF_RTOL): 141*b7c941bbSAndroid Build Coastguard Worker raise AssertionError( 142*b7c941bbSAndroid Build Coastguard Worker 'Request and result zoom ratios too different! ' 143*b7c941bbSAndroid Build Coastguard Worker f'Request zoom ratio: {z}. ' 144*b7c941bbSAndroid Build Coastguard Worker f'Result zoom ratio: {cap_zoom_ratio}. ', 145*b7c941bbSAndroid Build Coastguard Worker f'RTOL: {_ZOOM_RATIO_REQUEST_RESULT_DIFF_RTOL}' 146*b7c941bbSAndroid Build Coastguard Worker ) 147*b7c941bbSAndroid Build Coastguard Worker 148*b7c941bbSAndroid Build Coastguard Worker physical_ids.add(cap_physical_id) 149*b7c941bbSAndroid Build Coastguard Worker logging.debug('Physical IDs: %s', physical_ids) 150*b7c941bbSAndroid Build Coastguard Worker 151*b7c941bbSAndroid Build Coastguard Worker img = image_processing_utils.convert_capture_to_rgb_image( 152*b7c941bbSAndroid Build Coastguard Worker cap, props=props) 153*b7c941bbSAndroid Build Coastguard Worker img_name = (f'{img_name_stem}_{fmt}_{round(z, 2)}.' 154*b7c941bbSAndroid Build Coastguard Worker f'{zoom_capture_utils.JPEG_STR}') 155*b7c941bbSAndroid Build Coastguard Worker image_processing_utils.write_image(img, img_name) 156*b7c941bbSAndroid Build Coastguard Worker 157*b7c941bbSAndroid Build Coastguard Worker # determine radius tolerance of capture 158*b7c941bbSAndroid Build Coastguard Worker cap_fl = cap['metadata']['android.lens.focalLength'] 159*b7c941bbSAndroid Build Coastguard Worker radius_tol, offset_tol = test_tols.get( 160*b7c941bbSAndroid Build Coastguard Worker cap_fl, 161*b7c941bbSAndroid Build Coastguard Worker (zoom_capture_utils.RADIUS_RTOL, zoom_capture_utils.OFFSET_RTOL) 162*b7c941bbSAndroid Build Coastguard Worker ) 163*b7c941bbSAndroid Build Coastguard Worker 164*b7c941bbSAndroid Build Coastguard Worker # Find ArUco markers 165*b7c941bbSAndroid Build Coastguard Worker bgr_img = cv2.cvtColor( 166*b7c941bbSAndroid Build Coastguard Worker image_processing_utils.convert_image_to_uint8(img), 167*b7c941bbSAndroid Build Coastguard Worker cv2.COLOR_RGB2BGR 168*b7c941bbSAndroid Build Coastguard Worker ) 169*b7c941bbSAndroid Build Coastguard Worker try: 170*b7c941bbSAndroid Build Coastguard Worker corners, ids, _ = opencv_processing_utils.find_aruco_markers( 171*b7c941bbSAndroid Build Coastguard Worker bgr_img, 172*b7c941bbSAndroid Build Coastguard Worker (f'{img_name_stem}_{fmt}_{z:.2f}_' 173*b7c941bbSAndroid Build Coastguard Worker f'ArUco.{zoom_capture_utils.JPEG_STR}'), 174*b7c941bbSAndroid Build Coastguard Worker aruco_marker_count=1, 175*b7c941bbSAndroid Build Coastguard Worker force_greyscale=True # Maximize number of markers detected 176*b7c941bbSAndroid Build Coastguard Worker ) 177*b7c941bbSAndroid Build Coastguard Worker except AssertionError as e: 178*b7c941bbSAndroid Build Coastguard Worker logging.debug('Could not find ArUco marker at zoom ratio %.2f: %s', 179*b7c941bbSAndroid Build Coastguard Worker z, e) 180*b7c941bbSAndroid Build Coastguard Worker break 181*b7c941bbSAndroid Build Coastguard Worker all_aruco_corners.append([corner[0] for corner in corners]) 182*b7c941bbSAndroid Build Coastguard Worker all_aruco_ids.append([id[0] for id in ids]) 183*b7c941bbSAndroid Build Coastguard Worker images.append(bgr_img) 184*b7c941bbSAndroid Build Coastguard Worker 185*b7c941bbSAndroid Build Coastguard Worker test_data.append( 186*b7c941bbSAndroid Build Coastguard Worker zoom_capture_utils.ZoomTestData( 187*b7c941bbSAndroid Build Coastguard Worker result_zoom=cap_zoom_ratio, 188*b7c941bbSAndroid Build Coastguard Worker radius_tol=radius_tol, 189*b7c941bbSAndroid Build Coastguard Worker offset_tol=offset_tol, 190*b7c941bbSAndroid Build Coastguard Worker focal_length=cap_fl, 191*b7c941bbSAndroid Build Coastguard Worker physical_id=cap_physical_id, 192*b7c941bbSAndroid Build Coastguard Worker ) 193*b7c941bbSAndroid Build Coastguard Worker ) 194*b7c941bbSAndroid Build Coastguard Worker 195*b7c941bbSAndroid Build Coastguard Worker # Find ArUco markers in all captures and update test data 196*b7c941bbSAndroid Build Coastguard Worker zoom_capture_utils.update_zoom_test_data_with_shared_aruco_marker( 197*b7c941bbSAndroid Build Coastguard Worker test_data, all_aruco_ids, all_aruco_corners, size) 198*b7c941bbSAndroid Build Coastguard Worker # Mark ArUco marker center and image center 199*b7c941bbSAndroid Build Coastguard Worker opencv_processing_utils.mark_zoom_images( 200*b7c941bbSAndroid Build Coastguard Worker images, test_data, f'{img_name_stem}_{fmt}') 201*b7c941bbSAndroid Build Coastguard Worker 202*b7c941bbSAndroid Build Coastguard Worker number_of_cameras_to_test = ( 203*b7c941bbSAndroid Build Coastguard Worker _ULTRAWIDE_NUMBER_OF_CAMERAS_TO_TEST 204*b7c941bbSAndroid Build Coastguard Worker if ultrawide_camera_found 205*b7c941bbSAndroid Build Coastguard Worker else _SINGLE_CAMERA_NUMBER_OF_CAMERAS_TO_TEST 206*b7c941bbSAndroid Build Coastguard Worker ) 207*b7c941bbSAndroid Build Coastguard Worker if not zoom_capture_utils.verify_zoom_data( 208*b7c941bbSAndroid Build Coastguard Worker test_data, size, 209*b7c941bbSAndroid Build Coastguard Worker offset_plot_name_stem=f'{img_name_stem}_{fmt}', 210*b7c941bbSAndroid Build Coastguard Worker number_of_cameras_to_test=number_of_cameras_to_test): 211*b7c941bbSAndroid Build Coastguard Worker test_failed = True 212*b7c941bbSAndroid Build Coastguard Worker 213*b7c941bbSAndroid Build Coastguard Worker if test_failed: 214*b7c941bbSAndroid Build Coastguard Worker raise AssertionError(f'{_NAME} failed! Check test_log.DEBUG for errors') 215*b7c941bbSAndroid Build Coastguard Worker 216*b7c941bbSAndroid Build Coastguard Workerif __name__ == '__main__': 217*b7c941bbSAndroid Build Coastguard Worker test_runner.main() 218