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 that the device will write/read correct exp/gain values. 15""" 16 17import logging 18import os.path 19 20from mobly import test_runner 21 22import its_base_test 23import camera_properties_utils 24import capture_request_utils 25import its_session_utils 26 27 28_NAME = os.path.basename(__file__).split('.')[0] 29# Spec to be within 3% but not over for exposure in capture vs exposure request. 30_RTOL_EXP_GAIN = 0.97 31_TEST_EXP_RANGE = [6E6, 1E9] # ns [6ms, 1s] 32 33 34class ReadWriteTest(its_base_test.ItsBaseTest): 35 """Test that the device will write/read correct exp/gain values. 36 """ 37 38 def test_read_write(self): 39 with its_session_utils.ItsSession( 40 device_id=self.dut.serial, 41 camera_id=self.camera_id, 42 hidden_physical_id=self.hidden_physical_id) as cam: 43 props = cam.get_camera_properties() 44 props = cam.override_with_hidden_physical_camera_props(props) 45 camera_properties_utils.skip_unless( 46 camera_properties_utils.manual_sensor(props) and 47 camera_properties_utils.per_frame_control(props)) 48 vendor_api_level = its_session_utils.get_vendor_api_level(self.dut.serial) 49 50 valid_formats = ['yuv', 'jpg'] 51 if camera_properties_utils.raw16(props): 52 valid_formats.insert(0, 'raw') 53 # grab exp/gain ranges from camera 54 sensor_exp_range = props['android.sensor.info.exposureTimeRange'] 55 sens_range = props['android.sensor.info.sensitivityRange'] 56 logging.debug('sensor exposure time range: %s', sensor_exp_range) 57 logging.debug('sensor sensitivity range: %s', sens_range) 58 59 # determine if exposure test range is within sensor reported range 60 if sensor_exp_range[0] == 0: 61 raise AssertionError('Min expsoure == 0') 62 exp_range = [] 63 if sensor_exp_range[0] < _TEST_EXP_RANGE[0]: 64 exp_range.append(_TEST_EXP_RANGE[0]) 65 else: 66 exp_range.append(sensor_exp_range[0]) 67 if sensor_exp_range[1] > _TEST_EXP_RANGE[1]: 68 exp_range.append(_TEST_EXP_RANGE[1]) 69 else: 70 exp_range.append(sensor_exp_range[1]) 71 72 data = {} 73 # build requests 74 for fmt in valid_formats: 75 logging.debug('format: %s', fmt) 76 size = capture_request_utils.get_available_output_sizes(fmt, props)[-1] 77 out_surface = {'width': size[0], 'height': size[1], 'format': fmt} 78 # pylint: disable=protected-access 79 if cam._hidden_physical_id: 80 out_surface['physicalCamera'] = cam._hidden_physical_id 81 reqs = [] 82 index_list = [] 83 for exp in exp_range: 84 for sens in sens_range: 85 reqs.append(capture_request_utils.manual_capture_request(sens, exp)) 86 index_list.append((fmt, exp, sens)) 87 logging.debug('exp_write: %d, sens_write: %d', exp, sens) 88 89 # take shots 90 caps = cam.do_capture(reqs, out_surface) 91 92 # extract exp/sensitivity data 93 for i, cap in enumerate(caps): 94 exposure_read = cap['metadata']['android.sensor.exposureTime'] 95 sensitivity_read = cap['metadata']['android.sensor.sensitivity'] 96 data[index_list[i]] = (fmt, exposure_read, sensitivity_read) 97 98 # check read/write match across all shots 99 e_failed = [] # exposure time FAILs 100 s_failed = [] # sensitivity FAILs 101 r_failed = [] # sensitivity range FAILs 102 for fmt_write in valid_formats: 103 for e_write in exp_range: 104 for s_write in sens_range: 105 fmt_read, e_read, s_read = data[(fmt_write, e_write, s_write)] 106 if (e_write < e_read or e_read / float(e_write) <= _RTOL_EXP_GAIN): 107 e_failed.append({ 108 'format': fmt_read, 109 'e_write': e_write, 110 'e_read': e_read, 111 's_write': s_write, 112 's_read': s_read 113 }) 114 if (s_write < s_read or s_read / float(s_write) <= _RTOL_EXP_GAIN): 115 s_failed.append({ 116 'format': fmt_read, 117 'e_write': e_write, 118 'e_read': e_read, 119 's_write': s_write, 120 's_read': s_read 121 }) 122 if (vendor_api_level >= its_session_utils.ANDROID14_API_LEVEL and 123 s_read < sens_range[0]): 124 r_failed.append({ 125 'format': fmt_read, 126 'e_write': e_write, 127 'e_read': e_read, 128 's_write': s_write, 129 's_read': s_read 130 }) 131 132 # print results 133 if e_failed: 134 logging.debug('FAILs for exposure time') 135 for fail in e_failed: 136 logging.debug('format: %s, e_write: %d, e_read: %d, RTOL: %.2f, ', 137 fail['format'], fail['e_write'], fail['e_read'], 138 _RTOL_EXP_GAIN) 139 logging.debug('s_write: %d, s_read: %d, RTOL: %.2f', 140 fail['s_write'], fail['s_read'], _RTOL_EXP_GAIN) 141 if s_failed: 142 logging.debug('FAILs for sensitivity(ISO)') 143 for fail in s_failed: 144 logging.debug('format: %s, s_write: %d, s_read: %d, RTOL: %.2f, ', 145 fail['format'], fail['s_write'], fail['s_read'], 146 _RTOL_EXP_GAIN) 147 logging.debug('e_write: %d, e_read: %d, RTOL: %.2f', 148 fail['e_write'], fail['e_read'], _RTOL_EXP_GAIN) 149 if r_failed: 150 logging.debug('FAILs for sensitivity(ISO) range') 151 for fail in r_failed: 152 logging.debug('format: %s, s_write: %d, s_read: %d, RTOL: %.2f, ', 153 fail['format'], fail['s_write'], fail['s_read'], 154 _RTOL_EXP_GAIN) 155 logging.debug('e_write: %d, e_read: %d, RTOL: %.2f', 156 fail['e_write'], fail['e_read'], _RTOL_EXP_GAIN) 157 158 # PASS/FAIL 159 if e_failed: 160 raise AssertionError(f'Exposure fails: {e_failed}') 161 if s_failed: 162 raise AssertionError(f'Sensitivity fails: {s_failed}') 163 if r_failed: 164 raise AssertionError(f'Sensitivity range FAILs: {r_failed}') 165 166 167if __name__ == '__main__': 168 test_runner.main() 169