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 single capture of both DNG and YUV.""" 15 16 17import logging 18import os.path 19from mobly import test_runner 20 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_MAX_IMG_SIZE = (1920, 1080) 30_NAME = os.path.splitext(os.path.basename(__file__))[0] 31_SENSOR_CALIBRATION_XFORM_ATOL = 0.1 32_UNITY_MATRIX = np.array([1, 0, 0, 33 0, 1, 0, 34 0, 0, 1]) 35 36 37class YuvPlusDngTest(its_base_test.ItsBaseTest): 38 """Test capturing a single frame as both DNG and YUV outputs.""" 39 40 def test_yuv_plus_dng(self): 41 with its_session_utils.ItsSession( 42 device_id=self.dut.serial, 43 camera_id=self.camera_id, 44 hidden_physical_id=self.hidden_physical_id) as cam: 45 props = cam.get_camera_properties() 46 props = cam.override_with_hidden_physical_camera_props(props) 47 name_with_log_path = os.path.join(self.log_path, _NAME) 48 49 # Check SKIP conditions 50 camera_properties_utils.skip_unless(camera_properties_utils.raw(props)) 51 52 # Load chart for scene 53 its_session_utils.load_scene( 54 cam, props, self.scene, self.tablet, self.chart_distance) 55 56 # Create requests 57 mono_camera = camera_properties_utils.mono_camera(props) 58 cam.do_3a(mono_camera=mono_camera) 59 req = capture_request_utils.auto_capture_request() 60 max_dng_size = capture_request_utils.get_available_output_sizes( 61 'raw', props)[0] 62 if capture_request_utils.is_common_aspect_ratio(max_dng_size): 63 w, h = capture_request_utils.get_available_output_sizes( 64 'yuv', props, _MAX_IMG_SIZE, max_dng_size)[0] 65 else: 66 w, h = capture_request_utils.get_available_output_sizes( 67 'yuv', props, max_size=_MAX_IMG_SIZE)[0] 68 out_surfaces = [{'format': 'dng'}, 69 {'format': 'yuv', 'width': w, 'height': h}] 70 cap_dng, cap_yuv = cam.do_capture(req, out_surfaces) 71 72 img = image_processing_utils.convert_capture_to_rgb_image(cap_yuv) 73 image_processing_utils.write_image(img, f'{name_with_log_path}_yuv.jpg') 74 75 with open(f'{name_with_log_path}.dng', 'wb') as f: 76 f.write(cap_dng['data']) 77 78 # Transform/Matrix check: note value can return None 79 first_api_level = its_session_utils.get_first_api_level(self.dut.serial) 80 e_msg = '' 81 if first_api_level >= its_session_utils.ANDROID15_API_LEVEL: 82 for i in (1, 2): 83 if props.get(f'android.sensor.calibrationTransform{i}'): 84 calibration_xform = props[f'android.sensor.calibrationTransform{i}'] 85 logging.debug('Rational Cal Transform%d: %s', 86 i, str(calibration_xform)) 87 calibration_xform = capture_request_utils.rational_to_float( 88 calibration_xform) 89 logging.debug('Float Cal Transform%d: %s', 90 i, str(calibration_xform)) 91 if not np.allclose(calibration_xform, _UNITY_MATRIX, 92 atol=_SENSOR_CALIBRATION_XFORM_ATOL): 93 e_msg += ( 94 f'SENSOR_CALIBRATION_TRANSFORM{i} {calibration_xform} !~= ' 95 f'{_UNITY_MATRIX}, ATOL: {_SENSOR_CALIBRATION_XFORM_ATOL}\n' 96 ) 97 else: 98 logging.debug('Check skipped: no SENSOR_CALIBRATION_TRANSFORM%d', i) 99 if e_msg: 100 raise AssertionError(e_msg) 101 102if __name__ == '__main__': 103 test_runner.main() 104