| # Copyright 2022 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """Verifies that flash is fired when lighting conditions are dark.""" |
| |
| |
| import logging |
| import os.path |
| import pathlib |
| |
| import cv2 |
| from mobly import test_runner |
| import numpy as np |
| |
| import its_base_test |
| import camera_properties_utils |
| import image_processing_utils |
| import its_session_utils |
| import lighting_control_utils |
| import opencv_processing_utils |
| import ui_interaction_utils |
| |
| _JETPACK_CAMERA_APP_PACKAGE_NAME = 'com.google.jetpackcamera' |
| _MEAN_DELTA_ATOL = 15 # mean used for reflective charts |
| _PATCH_H = 0.25 # center 25% |
| _PATCH_W = 0.25 |
| _PATCH_X = 0.5 - _PATCH_W/2 |
| _PATCH_Y = 0.5 - _PATCH_H/2 |
| _SAVE_IMAGE_DELAY = 10 # empirically determined |
| _TEST_NAME = os.path.splitext(os.path.basename(__file__))[0] |
| |
| |
| class AutoFlashTest(its_base_test.UiAutomatorItsBaseTest): |
| """Test that flash is fired when lighting conditions are dark using JCA.""" |
| |
| def setup_class(self): |
| super().setup_class() |
| self.ui_app = _JETPACK_CAMERA_APP_PACKAGE_NAME |
| # restart CtsVerifier to ensure that correct flags are set |
| ui_interaction_utils.force_stop_app( |
| self.dut, its_base_test.CTS_VERIFIER_PKG) |
| self.dut.adb.shell( |
| 'am start -n com.android.cts.verifier/.CtsVerifierActivity') |
| |
| def teardown_test(self): |
| ui_interaction_utils.force_stop_app(self.dut, self.ui_app) |
| |
| def test_auto_flash(self): |
| with its_session_utils.ItsSession( |
| device_id=self.dut.serial, |
| camera_id=self.camera_id, |
| hidden_physical_id=self.hidden_physical_id) as cam: |
| props = cam.get_camera_properties() |
| props = cam.override_with_hidden_physical_camera_props(props) |
| test_name = os.path.join(self.log_path, _TEST_NAME) |
| |
| # close camera after props retrieved, so that ItsTestActivity can open it |
| cam.close_camera() |
| |
| # check SKIP conditions |
| first_api_level = its_session_utils.get_first_api_level(self.dut.serial) |
| facing_front = (props['android.lens.facing'] == |
| camera_properties_utils.LENS_FACING['FRONT']) |
| should_run_front = ( |
| facing_front and |
| first_api_level >= its_session_utils.ANDROID15_API_LEVEL |
| ) |
| should_run_rear = ( |
| camera_properties_utils.flash(props) and |
| first_api_level >= its_session_utils.ANDROID13_API_LEVEL |
| ) |
| camera_properties_utils.skip_unless(should_run_front or should_run_rear) |
| |
| # establish connection with lighting controller |
| arduino_serial_port = lighting_control_utils.lighting_control( |
| self.lighting_cntl, self.lighting_ch) |
| |
| # turn OFF lights to darken scene |
| lighting_control_utils.set_lighting_state( |
| arduino_serial_port, self.lighting_ch, 'OFF') |
| |
| # take capture with no flash as baseline |
| path = pathlib.Path( |
| cam.do_jca_capture( |
| self.dut, |
| self.log_path, |
| flash_mode_desc=ui_interaction_utils.FLASH_MODE_OFF_CONTENT_DESC, |
| lens_facing=props['android.lens.facing'], |
| ).capture_path |
| ) |
| no_flash_capture_path = path.with_name( |
| f'{path.stem}_no_flash{path.suffix}' |
| ) |
| os.rename(path, no_flash_capture_path) |
| cv2_no_flash_image = cv2.imread(str(no_flash_capture_path)) |
| y = opencv_processing_utils.convert_to_y(cv2_no_flash_image, 'BGR') |
| # Add a color channel dimension for interoperability |
| y = np.expand_dims(y, axis=2) |
| patch = image_processing_utils.get_image_patch( |
| y, _PATCH_X, _PATCH_Y, _PATCH_W, _PATCH_H |
| ) |
| no_flash_mean = image_processing_utils.compute_image_means(patch)[0] |
| image_processing_utils.write_image(y, f'{test_name}_no_flash_Y.jpg') |
| logging.debug('No flash frames Y mean: %.4f', no_flash_mean) |
| |
| # take capture with auto flash enabled |
| logging.debug('Taking capture with auto flash enabled.') |
| path = pathlib.Path( |
| cam.do_jca_capture( |
| self.dut, |
| self.log_path, |
| flash_mode_desc=ui_interaction_utils.FLASH_MODE_AUTO_CONTENT_DESC, |
| lens_facing=props['android.lens.facing'], |
| save_image_delay=_SAVE_IMAGE_DELAY, |
| ).capture_path |
| ) |
| auto_flash_capture_path = path.with_name( |
| f'{path.stem}_auto_flash{path.suffix}' |
| ) |
| os.rename(path, auto_flash_capture_path) |
| cv2_auto_flash_image = cv2.imread(str(auto_flash_capture_path)) |
| y = opencv_processing_utils.convert_to_y(cv2_auto_flash_image, 'BGR') |
| # Add a color channel dimension for interoperability |
| y = np.expand_dims(y, axis=2) |
| patch = image_processing_utils.get_image_patch( |
| y, _PATCH_X, _PATCH_Y, _PATCH_W, _PATCH_H |
| ) |
| flash_mean = image_processing_utils.compute_image_means(patch)[0] |
| image_processing_utils.write_image(y, f'{test_name}_auto_flash_Y.jpg') |
| logging.debug('Flash frames Y mean: %.4f', flash_mean) |
| |
| # confirm correct behavior |
| mean_delta = flash_mean - no_flash_mean |
| if mean_delta <= _MEAN_DELTA_ATOL: |
| raise AssertionError(f'mean FLASH-OFF: {mean_delta:.3f}, ' |
| f'ATOL: {_MEAN_DELTA_ATOL}') |
| |
| # turn lights back ON |
| lighting_control_utils.set_lighting_state( |
| arduino_serial_port, self.lighting_ch, 'ON') |
| |
| if __name__ == '__main__': |
| test_runner.main() |