• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2014 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 manual burst capture consistency."""
15
16
17import logging
18import os.path
19from matplotlib import pylab
20import matplotlib.pyplot
21from mobly import test_runner
22import numpy as np
23
24import its_base_test
25import camera_properties_utils
26import capture_request_utils
27import image_processing_utils
28import its_session_utils
29import target_exposure_utils
30
31_API_LEVEL_30 = 30
32_BURST_LEN = 50
33_COLORS = ('R', 'G', 'B')
34_NAME = os.path.splitext(os.path.basename(__file__))[0]
35_NUM_BURSTS = 2
36_PATCH_H = 0.1  # center 10%
37_PATCH_W = 0.1
38_PATCH_X = 0.5 - _PATCH_W/2
39_PATCH_Y = 0.5 - _PATCH_H/2
40_SPREAD_THRESH = 0.03
41_SPREAD_THRESH_API_LEVEL_30 = 0.02
42
43_NUM_FRAMES = _BURST_LEN * _NUM_BURSTS
44
45
46class BurstSamenessManualTest(its_base_test.ItsBaseTest):
47  """Take long bursts of images and check that they're all identical.
48
49  Assumes a static scene. Can be used to idenfity if there are sporadic
50  frames that are processed differently or have artifacts. Uses manual
51  capture settings.
52  """
53
54  def test_burst_sameness_manual(self):
55    logging.debug('Starting %s', _NAME)
56    with its_session_utils.ItsSession(
57        device_id=self.dut.serial,
58        camera_id=self.camera_id,
59        hidden_physical_id=self.hidden_physical_id) as cam:
60      props = cam.get_camera_properties()
61      props = cam.override_with_hidden_physical_camera_props(props)
62      log_path = self.log_path
63      name_with_path = os.path.join(log_path, _NAME)
64
65      # check SKIP conditions
66      camera_properties_utils.skip_unless(
67          camera_properties_utils.compute_target_exposure(props) and
68          camera_properties_utils.per_frame_control(props))
69
70      # Load chart for scene
71      its_session_utils.load_scene(
72          cam, props, self.scene, self.tablet,
73          its_session_utils.CHART_DISTANCE_NO_SCALING)
74
75      # Capture at the smallest resolution
76      _, fmt = capture_request_utils.get_fastest_manual_capture_settings(props)
77      e, s = target_exposure_utils.get_target_exposure_combos(
78          log_path, cam)['minSensitivity']
79      req = capture_request_utils.manual_capture_request(s, e)
80      w, h = fmt['width'], fmt['height']
81
82      # Capture bursts of YUV shots.
83      # Get the mean values of a center patch for each.
84      # Also build a 4D array, imgs, which is an array of all RGB images.
85      r_means = []
86      g_means = []
87      b_means = []
88      imgs = np.empty([_NUM_FRAMES, h, w, 3])
89      for j in range(_NUM_BURSTS):
90        caps = cam.do_capture([req]*_BURST_LEN, [fmt])
91        for i, cap in enumerate(caps):
92          n = j*_BURST_LEN + i
93          imgs[n] = image_processing_utils.convert_capture_to_rgb_image(cap)
94          patch = image_processing_utils.get_image_patch(
95              imgs[n], _PATCH_X, _PATCH_Y, _PATCH_W, _PATCH_H)
96          means = image_processing_utils.compute_image_means(patch)
97          r_means.append(means[0])
98          g_means.append(means[1])
99          b_means.append(means[2])
100
101      # Save first frame for setup debug
102      image_processing_utils.write_image(
103          imgs[0], f'{name_with_path}_frame000.jpg')
104
105      # Plot RGB means vs frames
106      frames = range(_NUM_FRAMES)
107      pylab.figure(_NAME)
108      pylab.title(_NAME)
109      pylab.plot(frames, r_means, '-ro')
110      pylab.plot(frames, g_means, '-go')
111      pylab.plot(frames, b_means, '-bo')
112      pylab.ylim([0, 1])
113      pylab.xlabel('frame number')
114      pylab.ylabel('RGB avg [0, 1]')
115      matplotlib.pyplot.savefig(f'{name_with_path}_plot_means.png')
116
117      # determine spread_thresh
118      spread_thresh = _SPREAD_THRESH
119      if its_session_utils.get_first_api_level(
120          self.dut.serial) >= _API_LEVEL_30:
121        spread_thresh = _SPREAD_THRESH_API_LEVEL_30
122
123      # PASS/FAIL based on center patch similarity
124      for plane, means in enumerate([r_means, g_means, b_means]):
125        spread = max(means) - min(means)
126        logging.debug('%s spread: %.5f', _COLORS[plane], spread)
127        if spread > spread_thresh:
128          # Save all frames if FAIL
129          logging.debug('Dumping all images')
130          for i in range(1, _NUM_FRAMES):
131            image_processing_utils.write_image(
132                imgs[i], f'{name_with_path}_frame{i:03d}.jpg')
133          raise AssertionError(f'{_COLORS[plane]} spread > THRESH. spread: '
134                               f'{spread}, THRESH: {spread_thresh:.2f}')
135
136if __name__ == '__main__':
137  test_runner.main()
138