• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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