普通文本  |  186行  |  7.49 KB

# Copyright 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os

import its.caps
import its.cv2image
import its.device
import its.image
import its.objects
import numpy as np

NUM_IMGS = 12
FRAME_TIME_TOL = 10  # ms
SHARPNESS_TOL = 0.10  # percentage
POSITION_TOL = 0.10  # percentage
VGA_WIDTH = 640
VGA_HEIGHT = 480
NAME = os.path.basename(__file__).split('.')[0]
CHART_FILE = os.path.join(os.environ['CAMERA_ITS_TOP'], 'pymodules', 'its',
                          'test_images', 'ISO12233.png')
CHART_HEIGHT = 13.5  # cm
CHART_DISTANCE = 30.0  # cm
CHART_SCALE_START = 0.65
CHART_SCALE_STOP = 1.35
CHART_SCALE_STEP = 0.025


def test_lens_movement_reporting(cam, props, fmt, sensitivity, exp, af_fd):
    """Return fd, sharpness, lens state of the output images.

    Args:
        cam: An open device session.
        props: Properties of cam
        fmt: dict; capture format
        sensitivity: Sensitivity for the 3A request as defined in
            android.sensor.sensitivity
        exp: Exposure time for the 3A request as defined in
            android.sensor.exposureTime
        af_fd: Focus distance for the 3A request as defined in
            android.lens.focusDistance

    Returns:
        Object containing reported sharpness of the output image, keyed by
        the following string:
            'sharpness'
    """

    # initialize chart class
    chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
                               CHART_SCALE_START, CHART_SCALE_STOP,
                               CHART_SCALE_STEP)

    # find chart location
    xnorm, ynorm, wnorm, hnorm = chart.locate(cam, props, fmt, sensitivity,
                                              exp, af_fd)

    # initialize variables and take data sets
    data_set = {}
    white_level = int(props['android.sensor.info.whiteLevel'])
    min_fd = props['android.lens.info.minimumFocusDistance']
    fds = [af_fd, min_fd]
    fds = sorted(fds * NUM_IMGS)
    reqs = []
    for i, fd in enumerate(fds):
        reqs.append(its.objects.manual_capture_request(sensitivity, exp))
        reqs[i]['android.lens.focusDistance'] = fd
    caps = cam.do_capture(reqs, fmt)
    for i, cap in enumerate(caps):
        data = {'fd': fds[i]}
        data['loc'] = cap['metadata']['android.lens.focusDistance']
        data['lens_moving'] = (cap['metadata']['android.lens.state']
                               == 1)
        timestamp = cap['metadata']['android.sensor.timestamp']
        if i == 0:
            timestamp_init = timestamp
        timestamp -= timestamp_init
        timestamp *= 1E-6
        data['timestamp'] = timestamp
        print ' focus distance (diopters): %.3f' % data['fd']
        print ' current lens location (diopters): %.3f' % data['loc']
        print ' lens moving %r' % data['lens_moving']
        y, _, _ = its.image.convert_capture_to_planes(cap, props)
        y = its.image.flip_mirror_img_per_argv(y)
        chart = its.image.normalize_img(its.image.get_image_patch(y,
                                                                  xnorm, ynorm,
                                                                  wnorm, hnorm))
        its.image.write_image(chart, '%s_i=%d_chart.jpg' % (NAME, i))
        data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
        print 'Chart sharpness: %.1f\n' % data['sharpness']
        data_set[i] = data
    return data_set


def main():
    """Test if focus distance is properly reported.

    Capture images at a variety of focus locations.
    """

    print '\nStarting test_lens_movement_reporting.py'
    with its.device.ItsSession() as cam:
        props = cam.get_camera_properties()
        its.caps.skip_unless(not its.caps.fixed_focus(props))
        its.caps.skip_unless(its.caps.lens_approx_calibrated(props))
        min_fd = props['android.lens.info.minimumFocusDistance']
        fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT}

        # Get proper sensitivity, exposure time, and focus distance with 3A.
        s, e, _, _, fd = cam.do_3a(get_results=True)

        # Get sharpness for each focal distance
        d = test_lens_movement_reporting(cam, props, fmt, s, e, fd)
        for k in sorted(d):
            print ('i: %d\tfd: %.3f\tlens location (diopters): %.3f \t'
                   'sharpness: %.1f  \tlens_moving: %r \t'
                   'timestamp: %.1fms' % (k, d[k]['fd'], d[k]['loc'],
                                          d[k]['sharpness'],
                                          d[k]['lens_moving'],
                                          d[k]['timestamp']))

        # assert frames are consecutive
        print 'Asserting frames are consecutive'
        times = [v['timestamp'] for v in d.itervalues()]
        diffs = np.gradient(times)
        assert np.isclose(np.amax(diffs)-np.amax(diffs), 0, atol=FRAME_TIME_TOL)

        # remove data when lens is moving
        for k in sorted(d):
            if d[k]['lens_moving']:
                del d[k]

        # split data into min_fd and af data for processing
        d_min_fd = {}
        d_af_fd = {}
        for k in sorted(d):
            if d[k]['fd'] == min_fd:
                d_min_fd[k] = d[k]
            if d[k]['fd'] == fd:
                d_af_fd[k] = d[k]

        # assert reported locations are close at af_fd
        print 'Asserting lens location of af_fd data'
        min_loc = min([v['loc'] for v in d_af_fd.itervalues()])
        max_loc = max([v['loc'] for v in d_af_fd.itervalues()])
        assert np.isclose(min_loc, max_loc, rtol=POSITION_TOL)
        # assert reported sharpness is close at af_fd
        print 'Asserting sharpness of af_fd data'
        min_sharp = min([v['sharpness'] for v in d_af_fd.itervalues()])
        max_sharp = max([v['sharpness'] for v in d_af_fd.itervalues()])
        assert np.isclose(min_sharp, max_sharp, rtol=SHARPNESS_TOL)
        # assert reported location is close to assign location for af_fd
        print 'Asserting lens location close to assigned fd for af_fd data'
        assert np.isclose(d_af_fd[0]['loc'], d_af_fd[0]['fd'],
                          rtol=POSITION_TOL)

        # assert reported location is close for min_fd captures
        print 'Asserting lens location similar min_fd data'
        min_loc = min([v['loc'] for v in d_min_fd.itervalues()])
        max_loc = max([v['loc'] for v in d_min_fd.itervalues()])
        assert np.isclose(min_loc, max_loc, rtol=POSITION_TOL)
        # assert reported sharpness is close at min_fd
        print 'Asserting sharpness of min_fd data'
        min_sharp = min([v['sharpness'] for v in d_min_fd.itervalues()])
        max_sharp = max([v['sharpness'] for v in d_min_fd.itervalues()])
        assert np.isclose(min_sharp, max_sharp, rtol=SHARPNESS_TOL)
        # assert reported location is close to assign location for min_fd
        print 'Asserting lens location close to assigned fd for min_fd data'
        assert np.isclose(d_min_fd[NUM_IMGS*2-1]['loc'],
                          d_min_fd[NUM_IMGS*2-1]['fd'], rtol=POSITION_TOL)


if __name__ == '__main__':
    main()