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
29AWB_AUTO_ATOL = 0.10
30AWB_AUTO_RTOL = 0.25
31AWB_MANUAL_ATOL = 0.05
32NAME = os.path.splitext(os.path.basename(__file__))[0]
33TONEMAP_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  image_processing_utils.write_image(img, '%s_%s.jpg' % (
49      os.path.join(log_path, NAME), cap_name))
50  awb_gains = cap['metadata']['android.colorCorrection.gains']
51  awb_xform = capture_request_utils.rational_to_float(
52      cap['metadata']['android.colorCorrection.transform'])
53  logging.debug('%s gains: %s', cap_name, str(awb_gains))
54  logging.debug('%s transform: %s', cap_name, str(awb_xform))
55  return awb_gains, awb_xform
56
57
58class AutoVsManualTest(its_base_test.ItsBaseTest):
59  """Capture auto and manual shots that should look the same.
60
61  Manual shots taken with just manual WB, and also with manual WB+tonemap.
62
63  In all cases, the general color/look of the shots should be the same,
64  however there can be variations in brightness/contrast due to different
65  'auto' ISP blocks that may be disabled in the manual flows.
66  """
67
68  def test_auto_vs_manual(self):
69    logging.debug('Starting %s', NAME)
70    with its_session_utils.ItsSession(
71        device_id=self.dut.serial,
72        camera_id=self.camera_id,
73        hidden_physical_id=self.hidden_physical_id) as cam:
74      props = cam.get_camera_properties()
75      props = cam.override_with_hidden_physical_camera_props(props)
76      mono_camera = camera_properties_utils.mono_camera(props)
77      log_path = self.log_path
78
79      # check SKIP conditions
80      camera_properties_utils.skip_unless(
81          camera_properties_utils.read_3a(props) and
82          camera_properties_utils.per_frame_control(props))
83
84      # Load chart for scene
85      its_session_utils.load_scene(
86          cam, props, self.scene, self.tablet, self.chart_distance)
87
88      # Converge 3A and get the estimates
89      largest_yuv = capture_request_utils.get_largest_yuv_format(props)
90      match_ar = (largest_yuv['width'], largest_yuv['height'])
91      fmt = capture_request_utils.get_smallest_yuv_format(
92          props, match_ar=match_ar)
93      s, e, awb_gains, awb_xform, fd = cam.do_3a(get_results=True,
94                                                 mono_camera=mono_camera)
95      awb_xform_rat = capture_request_utils.float_to_rational(awb_xform)
96      logging.debug('AE sensitivity: %d, exposure: %dms', s, e/1000000.0)
97      logging.debug('AWB gains: %s', str(awb_gains))
98      logging.debug('AWB transform: %s', str(awb_xform))
99      logging.debug('AF distance: %.3f', fd)
100
101      # Auto capture
102      req = capture_request_utils.auto_capture_request()
103      cap_auto = cam.do_capture(req, fmt)
104      awb_gains_a, awb_xform_a = extract_awb_gains_and_xform(
105          cap_auto, 'auto', log_path)
106
107      # Manual capture 1: WB
108      req = capture_request_utils.manual_capture_request(s, e, fd)
109      req['android.colorCorrection.transform'] = awb_xform_rat
110      req['android.colorCorrection.gains'] = awb_gains
111      cap_man1 = cam.do_capture(req, fmt)
112      awb_gains_m1, awb_xform_m1 = extract_awb_gains_and_xform(
113          cap_man1, 'manual_wb', log_path)
114
115      # Manual capture 2: WB + tonemap
116      req['android.tonemap.mode'] = 0
117      req['android.tonemap.curve'] = {'red': TONEMAP_GAMMA,
118                                      'green': TONEMAP_GAMMA,
119                                      'blue': TONEMAP_GAMMA}
120      cap_man2 = cam.do_capture(req, fmt)
121      awb_gains_m2, awb_xform_m2 = extract_awb_gains_and_xform(
122          cap_man2, 'manual_wb_tm', log_path)
123
124      # Check AWB gains & transform in manual results match values from do_3a
125      for g, x in [(awb_gains_m1, awb_xform_m1), (awb_gains_m2, awb_xform_m2)]:
126        e_msg = 'awb_xform 3A: %s, manual: %s, ATOL=%.2f' % (
127            str(awb_xform), str(x), AWB_MANUAL_ATOL)
128        assert np.allclose(awb_xform, x, atol=AWB_MANUAL_ATOL, rtol=0), e_msg
129        e_msg = 'awb_gains 3A: %s, manual: %s, ATOL=%.2f' % (
130            str(awb_gains), str(g), AWB_MANUAL_ATOL)
131        assert np.allclose(awb_gains, g, atol=AWB_MANUAL_ATOL, rtol=0), e_msg
132
133      # Check AWB gains & transform in auto results match values from do_3a
134      e_msg = 'awb_xform 3A: %s, auto: %s, RTOL=%.2f, ATOL=%.2f' % (
135          str(awb_xform), str(awb_xform_a), AWB_AUTO_RTOL, AWB_AUTO_ATOL)
136      assert np.allclose(awb_xform_a, awb_xform, atol=AWB_AUTO_ATOL,
137                         rtol=AWB_AUTO_RTOL), e_msg
138      e_msg = 'awb_gains 3A: %s, auto: %s, RTOL=%.2f, ATOL=%.2f' % (
139          str(awb_gains), str(awb_gains_a), AWB_AUTO_RTOL, AWB_AUTO_ATOL)
140      assert np.allclose(awb_gains_a, awb_gains, atol=AWB_AUTO_ATOL,
141                         rtol=AWB_AUTO_RTOL), e_msg
142
143if __name__ == '__main__':
144  test_runner.main()
145
146