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