1# Copyright 2022 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 flash is fired when lighting conditions are dark.""" 15 16 17import logging 18import os.path 19import pathlib 20 21import cv2 22from mobly import test_runner 23import numpy as np 24 25import its_base_test 26import camera_properties_utils 27import image_processing_utils 28import its_session_utils 29import lighting_control_utils 30import opencv_processing_utils 31import ui_interaction_utils 32 33_JETPACK_CAMERA_APP_PACKAGE_NAME = 'com.google.jetpackcamera' 34_MEAN_DELTA_ATOL = 15 # mean used for reflective charts 35_PATCH_H = 0.25 # center 25% 36_PATCH_W = 0.25 37_PATCH_X = 0.5 - _PATCH_W/2 38_PATCH_Y = 0.5 - _PATCH_H/2 39_SAVE_IMAGE_DELAY = 10 # empirically determined 40_TEST_NAME = os.path.splitext(os.path.basename(__file__))[0] 41 42 43class AutoFlashTest(its_base_test.UiAutomatorItsBaseTest): 44 """Test that flash is fired when lighting conditions are dark using JCA.""" 45 46 def setup_class(self): 47 super().setup_class() 48 self.ui_app = _JETPACK_CAMERA_APP_PACKAGE_NAME 49 # restart CtsVerifier to ensure that correct flags are set 50 ui_interaction_utils.force_stop_app( 51 self.dut, its_base_test.CTS_VERIFIER_PKG) 52 self.dut.adb.shell( 53 'am start -n com.android.cts.verifier/.CtsVerifierActivity') 54 55 def teardown_test(self): 56 ui_interaction_utils.force_stop_app(self.dut, self.ui_app) 57 58 def test_auto_flash(self): 59 with its_session_utils.ItsSession( 60 device_id=self.dut.serial, 61 camera_id=self.camera_id, 62 hidden_physical_id=self.hidden_physical_id) as cam: 63 props = cam.get_camera_properties() 64 props = cam.override_with_hidden_physical_camera_props(props) 65 test_name = os.path.join(self.log_path, _TEST_NAME) 66 67 # close camera after props retrieved, so that ItsTestActivity can open it 68 cam.close_camera() 69 70 # check SKIP conditions 71 first_api_level = its_session_utils.get_first_api_level(self.dut.serial) 72 facing_front = (props['android.lens.facing'] == 73 camera_properties_utils.LENS_FACING['FRONT']) 74 should_run_front = ( 75 facing_front and 76 first_api_level >= its_session_utils.ANDROID15_API_LEVEL 77 ) 78 should_run_rear = ( 79 camera_properties_utils.flash(props) and 80 first_api_level >= its_session_utils.ANDROID13_API_LEVEL 81 ) 82 camera_properties_utils.skip_unless(should_run_front or should_run_rear) 83 84 # establish connection with lighting controller 85 arduino_serial_port = lighting_control_utils.lighting_control( 86 self.lighting_cntl, self.lighting_ch) 87 88 # turn OFF lights to darken scene 89 lighting_control_utils.set_lighting_state( 90 arduino_serial_port, self.lighting_ch, 'OFF') 91 92 # take capture with no flash as baseline 93 path = pathlib.Path( 94 cam.do_jca_capture( 95 self.dut, 96 self.log_path, 97 flash_mode_desc=ui_interaction_utils.FLASH_MODE_OFF_CONTENT_DESC, 98 lens_facing=props['android.lens.facing'], 99 ).capture_path 100 ) 101 no_flash_capture_path = path.with_name( 102 f'{path.stem}_no_flash{path.suffix}' 103 ) 104 os.rename(path, no_flash_capture_path) 105 cv2_no_flash_image = cv2.imread(str(no_flash_capture_path)) 106 y = opencv_processing_utils.convert_to_y(cv2_no_flash_image, 'BGR') 107 # Add a color channel dimension for interoperability 108 y = np.expand_dims(y, axis=2) 109 patch = image_processing_utils.get_image_patch( 110 y, _PATCH_X, _PATCH_Y, _PATCH_W, _PATCH_H 111 ) 112 no_flash_mean = image_processing_utils.compute_image_means(patch)[0] 113 image_processing_utils.write_image(y, f'{test_name}_no_flash_Y.jpg') 114 logging.debug('No flash frames Y mean: %.4f', no_flash_mean) 115 116 # take capture with auto flash enabled 117 logging.debug('Taking capture with auto flash enabled.') 118 path = pathlib.Path( 119 cam.do_jca_capture( 120 self.dut, 121 self.log_path, 122 flash_mode_desc=ui_interaction_utils.FLASH_MODE_AUTO_CONTENT_DESC, 123 lens_facing=props['android.lens.facing'], 124 save_image_delay=_SAVE_IMAGE_DELAY, 125 ).capture_path 126 ) 127 auto_flash_capture_path = path.with_name( 128 f'{path.stem}_auto_flash{path.suffix}' 129 ) 130 os.rename(path, auto_flash_capture_path) 131 cv2_auto_flash_image = cv2.imread(str(auto_flash_capture_path)) 132 y = opencv_processing_utils.convert_to_y(cv2_auto_flash_image, 'BGR') 133 # Add a color channel dimension for interoperability 134 y = np.expand_dims(y, axis=2) 135 patch = image_processing_utils.get_image_patch( 136 y, _PATCH_X, _PATCH_Y, _PATCH_W, _PATCH_H 137 ) 138 flash_mean = image_processing_utils.compute_image_means(patch)[0] 139 image_processing_utils.write_image(y, f'{test_name}_auto_flash_Y.jpg') 140 logging.debug('Flash frames Y mean: %.4f', flash_mean) 141 142 # confirm correct behavior 143 mean_delta = flash_mean - no_flash_mean 144 if mean_delta <= _MEAN_DELTA_ATOL: 145 raise AssertionError(f'mean FLASH-OFF: {mean_delta:.3f}, ' 146 f'ATOL: {_MEAN_DELTA_ATOL}') 147 148 # turn lights back ON 149 lighting_control_utils.set_lighting_state( 150 arduino_serial_port, self.lighting_ch, 'ON') 151 152if __name__ == '__main__': 153 test_runner.main() 154