• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018 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"""CameraITS test for tonemap curve with sensor test pattern."""
15
16import logging
17import os
18
19from mobly import test_runner
20import numpy as np
21
22import its_base_test
23import camera_properties_utils
24import capture_request_utils
25import image_processing_utils
26import its_session_utils
27
28
29NAME = os.path.basename(__file__).split('.')[0]
30COLOR_BAR_PATTERN = 2  # Note scene0/test_test_patterns must PASS
31COLOR_BARS = ['WHITE', 'YELLOW', 'CYAN', 'GREEN', 'MAGENTA', 'RED',
32              'BLUE', 'BLACK']
33N_BARS = len(COLOR_BARS)
34COLOR_CHECKER = {'BLACK': [0, 0, 0], 'RED': [1, 0, 0], 'GREEN': [0, 1, 0],
35                 'BLUE': [0, 0, 1], 'MAGENTA': [1, 0, 1], 'CYAN': [0, 1, 1],
36                 'YELLOW': [1, 1, 0], 'WHITE': [1, 1, 1]}
37DELTA = 0.0005  # crop on edge of color bars
38RAW_TOL = 0.001  # 1 DN in [0:1] (1/(1023-64)
39RGB_VAR_TOL = 0.0039  # 1/255
40RGB_MEAN_TOL = 0.1
41TONEMAP_MAX = 0.5
42YUV_H = 480
43YUV_W = 640
44# Normalized co-ordinates for the color bar patch.
45Y_NORM = 0.0
46W_NORM = 1.0 / N_BARS - 2 * DELTA
47H_NORM = 1.0
48
49# Linear tonemap with maximum of 0.5
50LINEAR_TONEMAP = sum([[i/63.0, i/126.0] for i in range(64)], [])
51
52
53def get_x_norm(num):
54  """Returns the normalized x co-ordinate for the title.
55
56  Args:
57   num: int; position on color in the color bar.
58
59  Returns:
60    normalized x co-ordinate.
61  """
62  return float(num) / N_BARS + DELTA
63
64
65def check_raw_pattern(img_raw):
66  """Checks for RAW capture matches color bar pattern.
67
68  Args:
69    img_raw: RAW image
70  """
71  logging.debug('Checking RAW/PATTERN match')
72  color_match = []
73  for n in range(N_BARS):
74    x_norm = get_x_norm(n)
75    raw_patch = image_processing_utils.get_image_patch(img_raw, x_norm, Y_NORM,
76                                                       W_NORM, H_NORM)
77    raw_means = image_processing_utils.compute_image_means(raw_patch)
78    logging.debug('patch: %d, x_norm: %.3f, RAW means: %s',
79                  n, x_norm, str(raw_means))
80    for color in COLOR_BARS:
81      if np.allclose(COLOR_CHECKER[color], raw_means, atol=RAW_TOL):
82        color_match.append(color)
83        logging.debug('%s match', color)
84        break
85      else:
86        logging.debug('No match w/ %s: %s, ATOL: %.3f',
87                      color, str(COLOR_CHECKER[color]), RAW_TOL)
88  if set(color_match) != set(COLOR_BARS):
89    raise AssertionError('RAW COLOR_BARS test pattern does not have all colors')
90
91
92def check_yuv_vs_raw(img_raw, img_yuv):
93  """Checks for YUV vs RAW match in 8 patches.
94
95  Check for correct values and color consistency
96
97  Args:
98    img_raw: RAW image
99    img_yuv: YUV image
100  """
101  logging.debug('Checking YUV/RAW match')
102  color_match_errs = []
103  color_variance_errs = []
104  for n in range(N_BARS):
105    x_norm = get_x_norm(n)
106    logging.debug('x_norm: %.3f', x_norm)
107    raw_patch = image_processing_utils.get_image_patch(img_raw, x_norm, Y_NORM,
108                                                       W_NORM, H_NORM)
109    yuv_patch = image_processing_utils.get_image_patch(img_yuv, x_norm, Y_NORM,
110                                                       W_NORM, H_NORM)
111    raw_means = np.array(image_processing_utils.compute_image_means(raw_patch))
112    raw_vars = np.array(
113        image_processing_utils.compute_image_variances(raw_patch))
114    yuv_means = np.array(image_processing_utils.compute_image_means(yuv_patch))
115    yuv_means /= TONEMAP_MAX  # Normalize to tonemap max
116    yuv_vars = np.array(
117        image_processing_utils.compute_image_variances(yuv_patch))
118    if not np.allclose(raw_means, yuv_means, atol=RGB_MEAN_TOL):
119      color_match_errs.append(
120          'RAW: %s, RGB(norm): %s, ATOL: %.2f' %
121          (str(raw_means), str(np.round(yuv_means, 3)), RGB_MEAN_TOL))
122    if not np.allclose(raw_vars, yuv_vars, atol=RGB_VAR_TOL):
123      color_variance_errs.append('RAW: %s, RGB: %s, ATOL: %.4f' %
124                                 (str(raw_vars), str(yuv_vars), RGB_VAR_TOL))
125
126  # Print all errors before assertion
127  if color_match_errs:
128    for err in color_match_errs:
129      logging.debug(err)
130    for err in color_variance_errs:
131      logging.error(err)
132    raise AssertionError('Color match errors. See test_log.DEBUG')
133  if color_variance_errs:
134    for err in color_variance_errs:
135      logging.error(err)
136    raise AssertionError('Color variance errors. See test_log.DEBUG')
137
138
139def test_tonemap_curve_impl(name, cam, props):
140  """Test tonemap curve with sensor test pattern.
141
142  Args:
143   name: Path to save the captured image.
144   cam: An open device session.
145   props: Properties of cam.
146  """
147
148  avail_patterns = props['android.sensor.availableTestPatternModes']
149  logging.debug('Available Patterns: %s', avail_patterns)
150  sens_min, _ = props['android.sensor.info.sensitivityRange']
151  min_exposure = min(props['android.sensor.info.exposureTimeRange'])
152
153  # RAW image
154  req_raw = capture_request_utils.manual_capture_request(
155      int(sens_min), min_exposure)
156  req_raw['android.sensor.testPatternMode'] = COLOR_BAR_PATTERN
157  fmt_raw = {'format': 'raw'}
158  cap_raw = cam.do_capture(req_raw, fmt_raw)
159  img_raw = image_processing_utils.convert_capture_to_rgb_image(
160      cap_raw, props=props)
161
162  # Save RAW pattern
163  image_processing_utils.write_image(
164      img_raw, '%s_raw_%d.jpg' % (name, COLOR_BAR_PATTERN), True)
165  check_raw_pattern(img_raw)
166
167  # YUV image
168  req_yuv = capture_request_utils.manual_capture_request(
169      int(sens_min), min_exposure)
170  req_yuv['android.sensor.testPatternMode'] = COLOR_BAR_PATTERN
171  req_yuv['android.distortionCorrection.mode'] = 0
172  req_yuv['android.tonemap.mode'] = 0
173  req_yuv['android.tonemap.curve'] = {
174      'red': LINEAR_TONEMAP,
175      'green': LINEAR_TONEMAP,
176      'blue': LINEAR_TONEMAP
177  }
178  fmt_yuv = {'format': 'yuv', 'width': YUV_W, 'height': YUV_H}
179  cap_yuv = cam.do_capture(req_yuv, fmt_yuv)
180  img_yuv = image_processing_utils.convert_capture_to_rgb_image(cap_yuv, True)
181
182  # Save YUV pattern
183  image_processing_utils.write_image(
184      img_yuv, '%s_yuv_%d.jpg' % (name, COLOR_BAR_PATTERN), True)
185
186  # Check pattern for correctness
187  check_yuv_vs_raw(img_raw, img_yuv)
188
189
190class TonemapCurveTest(its_base_test.ItsBaseTest):
191  """Test conversion of test pattern from RAW to YUV with linear tonemap.
192
193  Test makes use of android.sensor.testPatternMode 2 (COLOR_BARS).
194  """
195
196  def test_tonemap_curve(self):
197    logging.debug('Starting %s', NAME)
198    name = os.path.join(self.log_path, NAME)
199    with its_session_utils.ItsSession(
200        device_id=self.dut.serial,
201        camera_id=self.camera_id,
202        hidden_physical_id=self.hidden_physical_id) as cam:
203      props = cam.get_camera_properties()
204      camera_properties_utils.skip_unless(
205          camera_properties_utils.raw16(props) and
206          camera_properties_utils.manual_sensor(props) and
207          camera_properties_utils.per_frame_control(props) and
208          camera_properties_utils.manual_post_proc(props) and
209          camera_properties_utils.color_bars_test_pattern(props))
210
211      test_tonemap_curve_impl(name, cam, props)
212
213
214if __name__ == '__main__':
215  test_runner.main()
216