blob: e2bd04066abcf96eca1e3b291f49bb1799f4c7be [file] [log] [blame]
Clemenz Portmann812236f2016-07-19 17:51:44 -07001# 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
Yin-Chia Yehf3506572016-10-10 15:46:46 -070016
Clemenz Portmann812236f2016-07-19 17:51:44 -070017import its.caps
18import its.device
19import its.image
20import its.objects
21import numpy as np
22
23NUM_TRYS = 2
24NUM_STEPS = 6
25SHARPNESS_TOL = 10 # percentage
26POSITION_TOL = 10 # percentage
27FRAME_TIME_TOL = 10 # ms
28VGA_WIDTH = 640
29VGA_HEIGHT = 480
30NAME = os.path.basename(__file__).split('.')[0]
Clemenz Portmann1475df32016-10-06 07:59:11 -070031CHART_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
Clemenz Portmann812236f2016-07-19 17:51:44 -070038
39
40def test_lens_position(cam, props, fmt, sensitivity, exp, af_fd):
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 sensitivity: 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
54 Returns:
55 Dictionary of results for different focal distance captures
56 with static lens positions and moving lens positions
57 d_static, d_moving
58 """
59
Clemenz Portmann1475df32016-10-06 07:59:11 -070060 # initialize chart class
61 chart = its.image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
62 CHART_SCALE_START, CHART_SCALE_STOP,
63 CHART_SCALE_STEP)
64
65 # find chart location
66 xnorm, ynorm, wnorm, hnorm = chart.locate(cam, props, fmt, sensitivity,
67 exp, af_fd)
68
69 # initialize variables and take data sets
Clemenz Portmann812236f2016-07-19 17:51:44 -070070 data_static = {}
71 data_moving = {}
72 white_level = int(props['android.sensor.info.whiteLevel'])
73 min_fd = props['android.lens.info.minimumFocusDistance']
74 hyperfocal = props['android.lens.info.hyperfocalDistance']
75 fds_f = np.arange(hyperfocal, min_fd, (min_fd-hyperfocal)/(NUM_STEPS-1))
76 fds_f = np.append(fds_f, min_fd)
77 fds_f = fds_f.tolist()
78 fds_b = list(reversed(fds_f))
79 fds_fb = list(fds_f)
80 fds_fb.extend(fds_b) # forward and back
81 # take static data set
82 for i, fd in enumerate(fds_fb):
83 req = its.objects.manual_capture_request(sensitivity, exp)
84 req['android.lens.focusDistance'] = fd
85 cap = its.image.stationary_lens_cap(cam, req, fmt)
86 data = {'fd': fds_fb[i]}
87 data['loc'] = cap['metadata']['android.lens.focusDistance']
88 print ' focus distance (diopters): %.3f' % data['fd']
89 print ' current lens location (diopters): %.3f' % data['loc']
90 y, _, _ = its.image.convert_capture_to_planes(cap, props)
91 chart = its.image.normalize_img(its.image.get_image_patch(y,
92 xnorm, ynorm,
93 wnorm, hnorm))
94 its.image.write_image(chart, '%s_stat_i=%d_chart.jpg' % (NAME, i))
95 data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
96 print 'Chart sharpness: %.1f\n' % data['sharpness']
97 data_static[i] = data
98 # take moving data set
99 reqs = []
100 for i, fd in enumerate(fds_f):
101 reqs.append(its.objects.manual_capture_request(sensitivity, exp))
102 reqs[i]['android.lens.focusDistance'] = fd
Yin-Chia Yehf3506572016-10-10 15:46:46 -0700103 caps = cam.do_capture(reqs, fmt)
104 for i, cap in enumerate(caps):
Clemenz Portmann812236f2016-07-19 17:51:44 -0700105 data = {'fd': fds_f[i]}
Yin-Chia Yehf3506572016-10-10 15:46:46 -0700106 data['loc'] = cap['metadata']['android.lens.focusDistance']
107 data['lens_moving'] = (cap['metadata']['android.lens.state']
Clemenz Portmann812236f2016-07-19 17:51:44 -0700108 == 1)
Yin-Chia Yehf3506572016-10-10 15:46:46 -0700109 timestamp = cap['metadata']['android.sensor.timestamp'] * 1E-6
Clemenz Portmann812236f2016-07-19 17:51:44 -0700110 if i == 0:
111 timestamp_init = timestamp
112 timestamp -= timestamp_init
113 data['timestamp'] = timestamp
114 print ' focus distance (diopters): %.3f' % data['fd']
115 print ' current lens location (diopters): %.3f' % data['loc']
Yin-Chia Yehf3506572016-10-10 15:46:46 -0700116 y, _, _ = its.image.convert_capture_to_planes(cap, props)
117 y = its.image.flip_mirror_img_per_argv(y)
Clemenz Portmann812236f2016-07-19 17:51:44 -0700118 chart = its.image.normalize_img(its.image.get_image_patch(y,
119 xnorm, ynorm,
120 wnorm, hnorm))
121 its.image.write_image(chart, '%s_move_i=%d_chart.jpg' % (NAME, i))
122 data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
123 print 'Chart sharpness: %.1f\n' % data['sharpness']
124 data_moving[i] = data
125 return data_static, data_moving
126
127
128def main():
129 """Test if focus position is properly reported for moving lenses."""
130
131 print '\nStarting test_lens_position.py'
132 with its.device.ItsSession() as cam:
133 props = cam.get_camera_properties()
134 its.caps.skip_unless(not its.caps.fixed_focus(props))
135 its.caps.skip_unless(its.caps.lens_calibrated(props))
136 fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT}
137
138 # Get proper sensitivity, exposure time, and focus distance with 3A.
139 s, e, _, _, fd = cam.do_3a(get_results=True)
140
141 # Get sharpness for each focal distance
142 d_stat, d_move = test_lens_position(cam, props, fmt, s, e, fd)
143 print 'Lens stationary'
144 for k in sorted(d_stat):
145 print ('i: %d\tfd: %.3f\tlens location (diopters): %.3f \t'
146 'sharpness: %.1f' % (k, d_stat[k]['fd'],
147 d_stat[k]['loc'],
148 d_stat[k]['sharpness']))
149 print 'Lens moving'
150 for k in sorted(d_move):
151 print ('i: %d\tfd: %.3f\tlens location (diopters): %.3f \t'
152 'sharpness: %.1f \tlens_moving: %r \t'
153 'timestamp: %.1fms' % (k, d_move[k]['fd'],
154 d_move[k]['loc'],
155 d_move[k]['sharpness'],
156 d_move[k]['lens_moving'],
157 d_move[k]['timestamp']))
158
159 # assert static reported location/sharpness is close
160 print 'Asserting static lens locations/sharpness are similar'
161 for i in range(len(d_stat)/2):
162 j = 2 * NUM_STEPS - 1 - i
163 print (' lens position: %.3f'
164 % d_stat[i]['fd'])
165 assert np.isclose(d_stat[i]['loc'], d_stat[i]['fd'],
166 rtol=POSITION_TOL/100.0)
167 assert np.isclose(d_stat[i]['loc'], d_stat[j]['loc'],
168 rtol=POSITION_TOL/100.0)
169 assert np.isclose(d_stat[i]['sharpness'], d_stat[j]['sharpness'],
170 rtol=SHARPNESS_TOL/100.0)
171 # assert moving frames approximately consecutive with even distribution
172 print 'Asserting moving frames are consecutive'
173 times = [v['timestamp'] for v in d_move.itervalues()]
174 diffs = np.gradient(times)
175 assert np.isclose(np.amin(diffs), np.amax(diffs), atol=FRAME_TIME_TOL)
176 # assert reported location/sharpness is correct in moving frames
177 print 'Asserting moving lens locations/sharpness are similar'
178 for i in range(len(d_move)):
179 print ' lens position: %.3f' % d_stat[i]['fd']
180 assert np.isclose(d_stat[i]['loc'], d_move[i]['loc'],
181 rtol=POSITION_TOL)
182 if d_move[i]['lens_moving'] and i > 0:
183 if d_stat[i]['sharpness'] > d_stat[i-1]['sharpness']:
184 assert (d_stat[i]['sharpness']*(1.0+SHARPNESS_TOL) >
185 d_move[i]['sharpness'] >
186 d_stat[i-1]['sharpness']*(1.0-SHARPNESS_TOL))
187 else:
188 assert (d_stat[i-1]['sharpness']*(1.0+SHARPNESS_TOL) >
189 d_move[i]['sharpness'] >
190 d_stat[i]['sharpness']*(1.0-SHARPNESS_TOL))
191 elif not d_move[i]['lens_moving']:
192 assert np.isclose(d_stat[i]['sharpness'],
193 d_move[i]['sharpness'], rtol=SHARPNESS_TOL)
194 else:
195 raise its.error.Error('Lens is moving at frame 0!')
196
197if __name__ == '__main__':
198 main()
199