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 28NAME = os.path.basename(__file__).split('.')[0] 29# Spec to be within 3% but not over for exposure in capture vs exposure request. 30RTOL_EXP_GAIN = 0.97 31TEST_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 49 valid_formats = ['yuv', 'jpg'] 50 if camera_properties_utils.raw16(props): 51 valid_formats.insert(0, 'raw') 52 # grab exp/gain ranges from camera 53 sensor_exp_range = props['android.sensor.info.exposureTimeRange'] 54 sens_range = props['android.sensor.info.sensitivityRange'] 55 logging.debug('sensor exposure time range: %s', sensor_exp_range) 56 logging.debug('sensor sensitivity range: %s', sens_range) 57 58 # determine if exposure test range is within sensor reported range 59 assert sensor_exp_range[0] != 0 60 exp_range = [] 61 if sensor_exp_range[0] < TEST_EXP_RANGE[0]: 62 exp_range.append(TEST_EXP_RANGE[0]) 63 else: 64 exp_range.append(sensor_exp_range[0]) 65 if sensor_exp_range[1] > TEST_EXP_RANGE[1]: 66 exp_range.append(TEST_EXP_RANGE[1]) 67 else: 68 exp_range.append(sensor_exp_range[1]) 69 70 data = {} 71 # build requests 72 for fmt in valid_formats: 73 logging.debug('format: %s', fmt) 74 size = capture_request_utils.get_available_output_sizes(fmt, props)[-1] 75 out_surface = {'width': size[0], 'height': size[1], 'format': fmt} 76 # pylint: disable=protected-access 77 if cam._hidden_physical_id: 78 out_surface['physicalCamera'] = cam._hidden_physical_id 79 reqs = [] 80 index_list = [] 81 for exp in exp_range: 82 for sens in sens_range: 83 reqs.append(capture_request_utils.manual_capture_request(sens, exp)) 84 index_list.append((fmt, exp, sens)) 85 logging.debug('exp_write: %d, sens_write: %d', exp, sens) 86 87 # take shots 88 caps = cam.do_capture(reqs, out_surface) 89 90 # extract exp/sensitivity data 91 for i, cap in enumerate(caps): 92 exposure_read = cap['metadata']['android.sensor.exposureTime'] 93 sensitivity_read = cap['metadata']['android.sensor.sensitivity'] 94 data[index_list[i]] = (fmt, exposure_read, sensitivity_read) 95 96 # check read/write match across all shots 97 e_failed = [] 98 s_failed = [] 99 for fmt_write in valid_formats: 100 for e_write in exp_range: 101 for s_write in sens_range: 102 fmt_read, e_read, s_read = data[(fmt_write, e_write, s_write)] 103 if (e_write < e_read or e_read / float(e_write) <= RTOL_EXP_GAIN): 104 e_failed.append({ 105 'format': fmt_read, 106 'e_write': e_write, 107 'e_read': e_read, 108 's_write': s_write, 109 's_read': s_read 110 }) 111 if (s_write < s_read or s_read / float(s_write) <= RTOL_EXP_GAIN): 112 s_failed.append({ 113 'format': fmt_read, 114 'e_write': e_write, 115 'e_read': e_read, 116 's_write': s_write, 117 's_read': s_read 118 }) 119 120 # print results 121 if e_failed: 122 logging.debug('FAILs for exposure time') 123 for fail in e_failed: 124 logging.debug('format: %s, e_write: %d, e_read: %d, RTOL: %.2f, ', 125 fail['format'], fail['e_write'], fail['e_read'], 126 RTOL_EXP_GAIN) 127 logging.debug('s_write: %d, s_read: %d, RTOL: %.2f', 128 fail['s_write'], fail['s_read'], RTOL_EXP_GAIN) 129 if s_failed: 130 logging.debug('FAILs for sensitivity(ISO)') 131 for fail in s_failed: 132 logging.debug('format: %s, s_write: %d, s_read: %d, RTOL: %.2f, ', 133 fail['format'], fail['s_write'], fail['s_read'], 134 RTOL_EXP_GAIN) 135 logging.debug('e_write: %d, e_read: %d, RTOL: %.2f', 136 fail['e_write'], fail['e_read'], RTOL_EXP_GAIN) 137 138 # assert PASS/FAIL 139 assert not e_failed + s_failed 140 141 142if __name__ == '__main__': 143 test_runner.main() 144