• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 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"""Verify capture burst of full size images is fast enough to not timeout."""
15
16import logging
17import os
18
19from mobly import test_runner
20
21import its_base_test
22import camera_properties_utils
23import capture_request_utils
24import image_processing_utils
25import its_session_utils
26
27_FRAME_TIME_DELTA_RTOL = 0.1  # allow 10% variation from reported value
28_NAME = os.path.splitext(os.path.basename(__file__))[0]
29_NR_MODE_FAST = 1  # burst capture uses noise reduction mode FAST
30_NUM_TEST_FRAMES = 15
31_PATCH_H = 0.1  # center 10% patch params
32_PATCH_W = 0.1
33_PATCH_X = 0.5 - _PATCH_W/2
34_PATCH_Y = 0.5 - _PATCH_H/2
35_START_FRAME = 2  # allow 1st frame to have some push out (see test_jitter.py)
36_THRESH_MIN_LEVEL = 0.1  # check images aren't too dark
37
38
39class BurstCaptureTest(its_base_test.ItsBaseTest):
40  """Test capture a burst of full size images is fast enough and doesn't timeout.
41
42  This test verifies that the entire capture pipeline can keep up the speed of
43  fullsize capture + CPU read for at least some time.
44  """
45
46  def test_burst_capture(self):
47    with its_session_utils.ItsSession(
48        device_id=self.dut.serial,
49        camera_id=self.camera_id,
50        hidden_physical_id=self.hidden_physical_id) as cam:
51      props = cam.get_camera_properties()
52      props = cam.override_with_hidden_physical_camera_props(props)
53      camera_properties_utils.skip_unless(
54          camera_properties_utils.backward_compatible(props) and
55          camera_properties_utils.burst_capture_capable
56      )
57
58      # Load chart for scene
59      its_session_utils.load_scene(
60          cam, props, self.scene, self.tablet, self.chart_distance)
61
62      req = capture_request_utils.auto_capture_request()
63      if camera_properties_utils.noise_reduction_mode(props, _NR_MODE_FAST):
64        req['android.noiseReduction.mode'] = _NR_MODE_FAST
65      cam.do_3a()
66      caps = cam.do_capture([req] * _NUM_TEST_FRAMES)
67      img = image_processing_utils.convert_capture_to_rgb_image(
68          caps[0], props=props)
69      name_with_log_path = os.path.join(self.log_path, _NAME)
70      image_processing_utils.write_image(img, f'{name_with_log_path}.jpg')
71      logging.debug('Image W, H: %d, %d', caps[0]['width'], caps[0]['height'])
72
73      # Confirm center patch brightness
74      patch = image_processing_utils.get_image_patch(
75          img, _PATCH_X, _PATCH_Y, _PATCH_W, _PATCH_H)
76      r, g, b = image_processing_utils.compute_image_means(patch)
77      logging.debug('RGB levels %.3f, %.3f, %.3f', r, g, b)
78      if g < _THRESH_MIN_LEVEL:
79        raise AssertionError(f'Image is too dark! G center patch avg: {g:.3f}, '
80                             f'THRESH: {_THRESH_MIN_LEVEL}')
81
82      # Check frames are consecutive
83      error_msg = []
84      first_api_level = its_session_utils.get_first_api_level(self.dut.serial)
85      frame_time_duration_deltas = []
86      if first_api_level >= its_session_utils.ANDROID15_API_LEVEL:
87        frame_times = [cap['metadata']['android.sensor.timestamp']
88                       for cap in caps]
89        for i, time in enumerate(frame_times):
90          if i < _START_FRAME:
91            continue
92          frame_time_delta = time - frame_times[i-1]
93          frame_duration = caps[i]['metadata']['android.sensor.frameDuration']
94          logging.debug('cap %d frameDuration: %d ns', i, frame_duration)
95          frame_time_delta_atol = frame_duration * (1+_FRAME_TIME_DELTA_RTOL)
96          frame_time_duration_deltas.append(frame_time_delta - frame_duration)
97          logging.debug(
98              'frame_time-frameDuration: %d ns', frame_time_delta-frame_duration
99          )
100          if frame_time_delta > frame_time_delta_atol:
101            error_msg.append(
102                f'Frame {i-1} --> {i} delta: {frame_time_delta}, '
103                f'ATOL: {frame_time_delta_atol:.1f} ns. '
104            )
105        # Note: Do not change from print to logging. print used for data-mining
106        print(
107            f'{_NAME}_max_frame_time_minus_frameDuration_ns: '
108            f'{max(frame_time_duration_deltas)}'
109        )
110        if error_msg:
111          raise AssertionError(f'Frame drop(s)! {error_msg}')
112
113
114if __name__ == '__main__':
115  test_runner.main()
116