1# Copyright 2016 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 15import os 16 17import its.caps 18import its.cv2image 19import its.device 20import its.image 21import its.objects 22import numpy as np 23 24NUM_IMGS = 12 25FRAME_TIME_TOL = 10 # ms 26SHARPNESS_TOL = 0.10 # percentage 27POSITION_TOL = 0.10 # percentage 28VGA_WIDTH = 640 29VGA_HEIGHT = 480 30NAME = os.path.basename(__file__).split('.')[0] 31CHART_FILE = os.path.join(os.environ['CAMERA_ITS_TOP'], 'pymodules', 'its', 32 'test_images', 'ISO12233.png') 33CHART_HEIGHT = 13.5 # cm 34CHART_DISTANCE = 30.0 # cm 35CHART_SCALE_START = 0.65 36CHART_SCALE_STOP = 1.35 37CHART_SCALE_STEP = 0.025 38 39 40def test_lens_movement_reporting(cam, props, fmt, gain, exp, af_fd, chart): 41 """Return fd, sharpness, lens state of the output images. 42 43 Args: 44 cam: An open device session. 45 props: Properties of cam 46 fmt: dict; capture format 47 gain: Sensitivity for the 3A request as defined in 48 android.sensor.sensitivity 49 exp: Exposure time for the 3A request as defined in 50 android.sensor.exposureTime 51 af_fd: Focus distance for the 3A request as defined in 52 android.lens.focusDistance 53 chart: Object that contains chart information 54 55 Returns: 56 Object containing reported sharpness of the output image, keyed by 57 the following string: 58 'sharpness' 59 """ 60 61 # initialize variables and take data sets 62 data_set = {} 63 white_level = int(props['android.sensor.info.whiteLevel']) 64 min_fd = props['android.lens.info.minimumFocusDistance'] 65 fds = [af_fd, min_fd] 66 fds = sorted(fds * NUM_IMGS) 67 reqs = [] 68 for i, fd in enumerate(fds): 69 reqs.append(its.objects.manual_capture_request(gain, exp)) 70 reqs[i]['android.lens.focusDistance'] = fd 71 caps = cam.do_capture(reqs, fmt) 72 for i, cap in enumerate(caps): 73 data = {'fd': fds[i]} 74 data['loc'] = cap['metadata']['android.lens.focusDistance'] 75 data['lens_moving'] = (cap['metadata']['android.lens.state'] 76 == 1) 77 timestamp = cap['metadata']['android.sensor.timestamp'] 78 if i == 0: 79 timestamp_init = timestamp 80 timestamp -= timestamp_init 81 timestamp *= 1E-6 82 data['timestamp'] = timestamp 83 print ' focus distance (diopters): %.3f' % data['fd'] 84 print ' current lens location (diopters): %.3f' % data['loc'] 85 print ' lens moving %r' % data['lens_moving'] 86 y, _, _ = its.image.convert_capture_to_planes(cap, props) 87 y = its.image.rotate_img_per_argv(y) 88 chart.img = its.image.normalize_img(its.image.get_image_patch( 89 y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm)) 90 its.image.write_image(chart.img, '%s_i=%d_chart.jpg' % (NAME, i)) 91 data['sharpness'] = white_level*its.image.compute_image_sharpness( 92 chart.img) 93 print 'Chart sharpness: %.1f\n' % data['sharpness'] 94 data_set[i] = data 95 return data_set 96 97 98def main(): 99 """Test if focus distance is properly reported. 100 101 Capture images at a variety of focus locations. 102 """ 103 104 print '\nStarting test_lens_movement_reporting.py' 105 # check skip conditions 106 with its.device.ItsSession() as cam: 107 props = cam.get_camera_properties() 108 its.caps.skip_unless(not its.caps.fixed_focus(props)) 109 its.caps.skip_unless(its.caps.read_3a(props) and 110 its.caps.lens_approx_calibrated(props)) 111 # initialize chart class 112 chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE, 113 CHART_SCALE_START, CHART_SCALE_STOP, 114 CHART_SCALE_STEP) 115 116 with its.device.ItsSession() as cam: 117 mono_camera = its.caps.mono_camera(props) 118 min_fd = props['android.lens.info.minimumFocusDistance'] 119 fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT} 120 121 # Get proper sensitivity, exposure time, and focus distance with 3A. 122 s, e, _, _, fd = cam.do_3a(get_results=True, mono_camera=mono_camera) 123 124 # Get sharpness for each focal distance 125 d = test_lens_movement_reporting(cam, props, fmt, s, e, fd, chart) 126 for k in sorted(d): 127 print ('i: %d\tfd: %.3f\tlens location (diopters): %.3f \t' 128 'sharpness: %.1f \tlens_moving: %r \t' 129 'timestamp: %.1fms' % (k, d[k]['fd'], d[k]['loc'], 130 d[k]['sharpness'], 131 d[k]['lens_moving'], 132 d[k]['timestamp'])) 133 134 # assert frames are consecutive 135 print 'Asserting frames are consecutive' 136 times = [v['timestamp'] for v in d.itervalues()] 137 diffs = np.gradient(times) 138 assert np.isclose(np.amax(diffs)-np.amax(diffs), 0, atol=FRAME_TIME_TOL) 139 140 # remove data when lens is moving 141 for k in sorted(d): 142 if d[k]['lens_moving']: 143 del d[k] 144 145 # split data into min_fd and af data for processing 146 d_min_fd = {} 147 d_af_fd = {} 148 for k in sorted(d): 149 if d[k]['fd'] == min_fd: 150 d_min_fd[k] = d[k] 151 if d[k]['fd'] == fd: 152 d_af_fd[k] = d[k] 153 154 # assert reported locations are close at af_fd 155 print 'Asserting lens location of af_fd data' 156 min_loc = min([v['loc'] for v in d_af_fd.itervalues()]) 157 max_loc = max([v['loc'] for v in d_af_fd.itervalues()]) 158 assert np.isclose(min_loc, max_loc, rtol=POSITION_TOL) 159 # assert reported sharpness is close at af_fd 160 print 'Asserting sharpness of af_fd data' 161 min_sharp = min([v['sharpness'] for v in d_af_fd.itervalues()]) 162 max_sharp = max([v['sharpness'] for v in d_af_fd.itervalues()]) 163 assert np.isclose(min_sharp, max_sharp, rtol=SHARPNESS_TOL) 164 # assert reported location is close to assign location for af_fd 165 print 'Asserting lens location close to assigned fd for af_fd data' 166 assert np.isclose(d_af_fd[0]['loc'], d_af_fd[0]['fd'], 167 rtol=POSITION_TOL) 168 169 # assert reported location is close for min_fd captures 170 print 'Asserting lens location similar min_fd data' 171 min_loc = min([v['loc'] for v in d_min_fd.itervalues()]) 172 max_loc = max([v['loc'] for v in d_min_fd.itervalues()]) 173 assert np.isclose(min_loc, max_loc, rtol=POSITION_TOL) 174 # assert reported sharpness is close at min_fd 175 print 'Asserting sharpness of min_fd data' 176 min_sharp = min([v['sharpness'] for v in d_min_fd.itervalues()]) 177 max_sharp = max([v['sharpness'] for v in d_min_fd.itervalues()]) 178 assert np.isclose(min_sharp, max_sharp, rtol=SHARPNESS_TOL) 179 # assert reported location is close to assign location for min_fd 180 print 'Asserting lens location close to assigned fd for min_fd data' 181 assert np.isclose(d_min_fd[NUM_IMGS*2-1]['loc'], 182 d_min_fd[NUM_IMGS*2-1]['fd'], rtol=POSITION_TOL) 183 184 185if __name__ == '__main__': 186 main() 187