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 auto and manual captures are similar with same scene."""
15
16
17import logging
18import math
19import os.path
20from mobly import test_runner
21import numpy as np
22
23import its_base_test
24import camera_properties_utils
25import capture_request_utils
26import image_processing_utils
27import its_session_utils
28
29_AWB_AUTO_ATOL = 0.10
30_AWB_AUTO_RTOL = 0.25
31_AWB_MANUAL_ATOL = 0.05
32_NAME = os.path.splitext(os.path.basename(__file__))[0]
33_TONEMAP_GAMMA = sum([[t/63.0, math.pow(t/63.0, 1/2.2)] for t in range(64)], [])
34
35
36def extract_awb_gains_and_xform(cap, cap_name, log_path):
37  """Extract the AWB transform and gains, save image, and log info.
38
39  Args:
40    cap: camera capture
41    cap_name: text string to identify cap type
42    log_path: location to save images
43
44  Returns:
45    awb_gains, awb_xform
46  """
47  img = image_processing_utils.convert_capture_to_rgb_image(cap)
48  name_with_log_path = os.path.join(log_path, _NAME)
49  image_processing_utils.write_image(
50      img, f'{name_with_log_path}_{cap_name}.jpg')
51  awb_gains = cap['metadata']['android.colorCorrection.gains']
52  awb_xform = capture_request_utils.rational_to_float(
53      cap['metadata']['android.colorCorrection.transform'])
54  logging.debug('%s gains: %s', cap_name, str(awb_gains))
55  logging.debug('%s transform: %s', cap_name, str(awb_xform))
56  return awb_gains, awb_xform
57
58
59class AutoVsManualTest(its_base_test.ItsBaseTest):
60  """Capture auto and manual shots that should look the same.
61
62  Manual shots taken with just manual WB, and also with manual WB+tonemap.
63
64  In all cases, the general color/look of the shots should be the same,
65  however there can be variations in brightness/contrast due to different
66  'auto' ISP blocks that may be disabled in the manual flows.
67  """
68
69  def test_auto_vs_manual(self):
70    logging.debug('Starting %s', _NAME)
71    with its_session_utils.ItsSession(
72        device_id=self.dut.serial,
73        camera_id=self.camera_id,
74        hidden_physical_id=self.hidden_physical_id) as cam:
75      props = cam.get_camera_properties()
76      props = cam.override_with_hidden_physical_camera_props(props)
77      mono_camera = camera_properties_utils.mono_camera(props)
78      log_path = self.log_path
79
80      # check SKIP conditions
81      camera_properties_utils.skip_unless(
82          camera_properties_utils.read_3a(props) and
83          camera_properties_utils.per_frame_control(props))
84
85      # Load chart for scene
86      its_session_utils.load_scene(
87          cam, props, self.scene, self.tablet, self.chart_distance)
88
89      # Converge 3A and get the estimates
90      largest_yuv = capture_request_utils.get_largest_yuv_format(props)
91      match_ar = (largest_yuv['width'], largest_yuv['height'])
92      fmt = capture_request_utils.get_near_vga_yuv_format(
93          props, match_ar=match_ar)
94      s, e, awb_gains, awb_xform, fd = cam.do_3a(get_results=True,
95                                                 mono_camera=mono_camera)
96      awb_xform_rat = capture_request_utils.float_to_rational(awb_xform)
97      logging.debug('AE sensitivity: %d, exposure: %dms', s, e/1000000.0)
98      logging.debug('AWB gains: %s', str(awb_gains))
99      logging.debug('AWB transform: %s', str(awb_xform))
100      logging.debug('AF distance: %.3f', fd)
101
102      # Auto capture
103      req = capture_request_utils.auto_capture_request()
104      cap_auto = cam.do_capture(req, fmt)
105      awb_gains_a, awb_xform_a = extract_awb_gains_and_xform(
106          cap_auto, 'auto', log_path)
107
108      # Manual capture 1: WB
109      req = capture_request_utils.manual_capture_request(s, e, fd)
110      req['android.colorCorrection.transform'] = awb_xform_rat
111      req['android.colorCorrection.gains'] = awb_gains
112      cap_man1 = cam.do_capture(req, fmt)
113      awb_gains_m1, awb_xform_m1 = extract_awb_gains_and_xform(
114          cap_man1, 'manual_wb', log_path)
115
116      # Manual capture 2: WB + tonemap
117      req['android.tonemap.mode'] = 0
118      req['android.tonemap.curve'] = {'red': _TONEMAP_GAMMA,
119                                      'green': _TONEMAP_GAMMA,
120                                      'blue': _TONEMAP_GAMMA}
121      cap_man2 = cam.do_capture(req, fmt)
122      awb_gains_m2, awb_xform_m2 = extract_awb_gains_and_xform(
123          cap_man2, 'manual_wb_tm', log_path)
124
125      # Check AWB gains & transform in manual results match values from do_3a
126      for g, x in [(awb_gains_m1, awb_xform_m1), (awb_gains_m2, awb_xform_m2)]:
127        if not np.allclose(awb_xform, x, atol=_AWB_MANUAL_ATOL, rtol=0):
128          raise AssertionError(
129              f'awb_xform 3A: {awb_xform}, '
130              f'manual: {x}, ATOL={_AWB_MANUAL_ATOL}')
131        if not np.allclose(awb_gains, g, atol=_AWB_MANUAL_ATOL, rtol=0):
132          raise AssertionError(
133              f'awb_gains 3A: {awb_gains}, '
134              f'manual: {g}, ATOL={_AWB_MANUAL_ATOL}')
135
136      # Check AWB gains & transform in auto results match values from do_3a
137      if not np.allclose(awb_xform_a, awb_xform, atol=_AWB_AUTO_ATOL,
138                         rtol=_AWB_AUTO_RTOL):
139        raise AssertionError(f'awb_xform 3A: {awb_xform}, auto: {awb_xform_a}, '
140                             f'RTOL={_AWB_AUTO_RTOL}, ATOL={_AWB_AUTO_ATOL}')
141      if not np.allclose(awb_gains_a, awb_gains, atol=_AWB_AUTO_ATOL,
142                         rtol=_AWB_AUTO_RTOL):
143        raise AssertionError(f'awb_gains 3A: {awb_gains}, auto: {awb_gains_a}, '
144                             f'RTOL={_AWB_AUTO_RTOL}, ATOL={_AWB_AUTO_ATOL}')
145
146if __name__ == '__main__':
147  test_runner.main()
148
149