1# Copyright 2023 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 that autoframing can adjust fov to include all faces with different 15skin tones.""" 16 17 18import logging 19import os.path 20 21from mobly import test_runner 22 23import its_base_test 24import camera_properties_utils 25import capture_request_utils 26import image_processing_utils 27import its_session_utils 28import opencv_processing_utils 29 30_CV2_FACE_SCALE_FACTOR = 1.05 # 5% step for resizing image to find face 31_CV2_FACE_MIN_NEIGHBORS = 4 # recommended 3-6: higher for less faces 32_NUM_TEST_FRAMES = 20 33_NUM_FACES = 3 34_W, _H = 640, 480 35 36 37class AutoframingTest(its_base_test.ItsBaseTest): 38 """Test autoframing for faces with different skin tones. 39 """ 40 41 def test_autoframing(self): 42 """Test if fov gets adjusted to accommodate all the faces in the frame. 43 44 Do a large zoom on scene2_a using do_3a so that none of that faces are 45 visible initially, trigger autoframing, wait for the state to converge and 46 make sure all the faces are found. 47 """ 48 with its_session_utils.ItsSession( 49 device_id=self.dut.serial, 50 camera_id=self.camera_id, 51 hidden_physical_id=self.hidden_physical_id) as cam: 52 props = cam.get_camera_properties() 53 props = cam.override_with_hidden_physical_camera_props(props) 54 55 # Load chart for scene 56 its_session_utils.load_scene( 57 cam, props, self.scene, self.tablet, self.chart_distance, 58 log_path=self.log_path) 59 60 # Check SKIP conditions 61 # Don't run autoframing if face detection or autoframing is not supported 62 camera_properties_utils.skip_unless( 63 camera_properties_utils.face_detect(props) and 64 camera_properties_utils.autoframing(props)) 65 66 # Do max-ish zoom with the help of do_3a, keeping all the 'A's off. This 67 # zooms into the scene so that none of the faces are in the view 68 # initially - which gives room for autoframing to take place. 69 max_zoom_ratio = camera_properties_utils.get_max_digital_zoom(props) 70 cam.do_3a(do_af=False, zoom_ratio=max_zoom_ratio) 71 cam.do_autoframing(zoom_ratio=max_zoom_ratio) 72 73 req = capture_request_utils.auto_capture_request( 74 do_autoframing=True, zoom_ratio=max_zoom_ratio) 75 req['android.statistics.faceDetectMode'] = 1 # Simple 76 fmt = {'format': 'yuv', 'width': _W, 'height': _H} 77 caps = cam.do_capture([req]*_NUM_TEST_FRAMES, fmt) 78 for i, cap in enumerate(caps): 79 faces = cap['metadata']['android.statistics.faces'] 80 # Face detection and autoframing could take several frames to warm up, 81 # but should detect the correct number of faces in last frame 82 if i == _NUM_TEST_FRAMES - 1: 83 num_faces_found = len(faces) 84 if num_faces_found != _NUM_FACES: 85 raise AssertionError('Wrong num of faces found! Found: ' 86 f'{num_faces_found}, expected: {_NUM_FACES}') 87 88 # Also check the faces with open cv to make sure the scene is not 89 # distored or anything. 90 img = image_processing_utils.convert_capture_to_rgb_image( 91 cap, props=props) 92 opencv_faces = opencv_processing_utils.find_opencv_faces( 93 img, _CV2_FACE_SCALE_FACTOR, _CV2_FACE_MIN_NEIGHBORS) 94 num_opencv_faces = len(opencv_faces) 95 if num_opencv_faces != _NUM_FACES: 96 raise AssertionError('Wrong num of faces found with OpenCV! Found: ' 97 f'{num_opencv_faces}, expected: {_NUM_FACES}') 98 99 if not faces: 100 continue 101 logging.debug('Frame %d face metadata:', i) 102 logging.debug('Faces: %s', str(faces)) 103 104 105if __name__ == '__main__': 106 test_runner.main() 107