| # Copyright 2014 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """Verifies auto and manual captures are similar with same scene.""" |
| |
| |
| import logging |
| import math |
| import os.path |
| from mobly import test_runner |
| import numpy as np |
| |
| import its_base_test |
| import camera_properties_utils |
| import capture_request_utils |
| import image_processing_utils |
| import its_session_utils |
| |
| AWB_AUTO_ATOL = 0.10 |
| AWB_AUTO_RTOL = 0.25 |
| AWB_MANUAL_ATOL = 0.05 |
| NAME = os.path.splitext(os.path.basename(__file__))[0] |
| TONEMAP_GAMMA = sum([[t/63.0, math.pow(t/63.0, 1/2.2)] for t in range(64)], []) |
| |
| |
| def extract_awb_gains_and_xform(cap, cap_name, log_path): |
| """Extract the AWB transform and gains, save image, and log info. |
| |
| Args: |
| cap: camera capture |
| cap_name: text string to identify cap type |
| log_path: location to save images |
| |
| Returns: |
| awb_gains, awb_xform |
| """ |
| img = image_processing_utils.convert_capture_to_rgb_image(cap) |
| image_processing_utils.write_image(img, '%s_%s.jpg' % ( |
| os.path.join(log_path, NAME), cap_name)) |
| awb_gains = cap['metadata']['android.colorCorrection.gains'] |
| awb_xform = capture_request_utils.rational_to_float( |
| cap['metadata']['android.colorCorrection.transform']) |
| logging.debug('%s gains: %s', cap_name, str(awb_gains)) |
| logging.debug('%s transform: %s', cap_name, str(awb_xform)) |
| return awb_gains, awb_xform |
| |
| |
| class AutoVsManualTest(its_base_test.ItsBaseTest): |
| """Capture auto and manual shots that should look the same. |
| |
| Manual shots taken with just manual WB, and also with manual WB+tonemap. |
| |
| In all cases, the general color/look of the shots should be the same, |
| however there can be variations in brightness/contrast due to different |
| 'auto' ISP blocks that may be disabled in the manual flows. |
| """ |
| |
| def test_auto_vs_manual(self): |
| logging.debug('Starting %s', NAME) |
| with its_session_utils.ItsSession( |
| device_id=self.dut.serial, |
| camera_id=self.camera_id, |
| hidden_physical_id=self.hidden_physical_id) as cam: |
| props = cam.get_camera_properties() |
| props = cam.override_with_hidden_physical_camera_props(props) |
| mono_camera = camera_properties_utils.mono_camera(props) |
| log_path = self.log_path |
| |
| # check SKIP conditions |
| camera_properties_utils.skip_unless( |
| camera_properties_utils.read_3a(props) and |
| camera_properties_utils.per_frame_control(props)) |
| |
| # Load chart for scene |
| its_session_utils.load_scene( |
| cam, props, self.scene, self.tablet, self.chart_distance) |
| |
| # Converge 3A and get the estimates |
| largest_yuv = capture_request_utils.get_largest_yuv_format(props) |
| match_ar = (largest_yuv['width'], largest_yuv['height']) |
| fmt = capture_request_utils.get_smallest_yuv_format( |
| props, match_ar=match_ar) |
| s, e, awb_gains, awb_xform, fd = cam.do_3a(get_results=True, |
| mono_camera=mono_camera) |
| awb_xform_rat = capture_request_utils.float_to_rational(awb_xform) |
| logging.debug('AE sensitivity: %d, exposure: %dms', s, e/1000000.0) |
| logging.debug('AWB gains: %s', str(awb_gains)) |
| logging.debug('AWB transform: %s', str(awb_xform)) |
| logging.debug('AF distance: %.3f', fd) |
| |
| # Auto capture |
| req = capture_request_utils.auto_capture_request() |
| cap_auto = cam.do_capture(req, fmt) |
| awb_gains_a, awb_xform_a = extract_awb_gains_and_xform( |
| cap_auto, 'auto', log_path) |
| |
| # Manual capture 1: WB |
| req = capture_request_utils.manual_capture_request(s, e, fd) |
| req['android.colorCorrection.transform'] = awb_xform_rat |
| req['android.colorCorrection.gains'] = awb_gains |
| cap_man1 = cam.do_capture(req, fmt) |
| awb_gains_m1, awb_xform_m1 = extract_awb_gains_and_xform( |
| cap_man1, 'manual_wb', log_path) |
| |
| # Manual capture 2: WB + tonemap |
| req['android.tonemap.mode'] = 0 |
| req['android.tonemap.curve'] = {'red': TONEMAP_GAMMA, |
| 'green': TONEMAP_GAMMA, |
| 'blue': TONEMAP_GAMMA} |
| cap_man2 = cam.do_capture(req, fmt) |
| awb_gains_m2, awb_xform_m2 = extract_awb_gains_and_xform( |
| cap_man2, 'manual_wb_tm', log_path) |
| |
| # Check AWB gains & transform in manual results match values from do_3a |
| for g, x in [(awb_gains_m1, awb_xform_m1), (awb_gains_m2, awb_xform_m2)]: |
| e_msg = 'awb_xform 3A: %s, manual: %s, ATOL=%.2f' % ( |
| str(awb_xform), str(x), AWB_MANUAL_ATOL) |
| assert np.allclose(awb_xform, x, atol=AWB_MANUAL_ATOL, rtol=0), e_msg |
| e_msg = 'awb_gains 3A: %s, manual: %s, ATOL=%.2f' % ( |
| str(awb_gains), str(g), AWB_MANUAL_ATOL) |
| assert np.allclose(awb_gains, g, atol=AWB_MANUAL_ATOL, rtol=0), e_msg |
| |
| # Check AWB gains & transform in auto results match values from do_3a |
| e_msg = 'awb_xform 3A: %s, auto: %s, RTOL=%.2f, ATOL=%.2f' % ( |
| str(awb_xform), str(awb_xform_a), AWB_AUTO_RTOL, AWB_AUTO_ATOL) |
| assert np.allclose(awb_xform_a, awb_xform, atol=AWB_AUTO_ATOL, |
| rtol=AWB_AUTO_RTOL), e_msg |
| e_msg = 'awb_gains 3A: %s, auto: %s, RTOL=%.2f, ATOL=%.2f' % ( |
| str(awb_gains), str(awb_gains_a), AWB_AUTO_RTOL, AWB_AUTO_ATOL) |
| assert np.allclose(awb_gains_a, awb_gains, atol=AWB_AUTO_ATOL, |
| rtol=AWB_AUTO_RTOL), e_msg |
| |
| if __name__ == '__main__': |
| test_runner.main() |
| |