1# Copyright 2014 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.path
16
17import its.caps
18import its.device
19import its.image
20import its.objects
21import matplotlib
22from matplotlib import pylab
23import numpy as np
24
25NAME = os.path.basename(__file__).split('.')[0]
26LOCKED = 3
27LUMA_LOCKED_TOL = 0.05
28THRESH_CONVERGE_FOR_EV = 8  # AE must converge within this num
29YUV_FULL_SCALE = 255.0
30YUV_SAT_MIN = 250.0
31YUV_SAT_TOL = 3.0
32
33
34def main():
35    """Tests that EV compensation is applied."""
36
37    with its.device.ItsSession() as cam:
38        props = cam.get_camera_properties()
39        its.caps.skip_unless(its.caps.ev_compensation(props) and
40                             its.caps.ae_lock(props))
41
42        debug = its.caps.debug_mode()
43        mono_camera = its.caps.mono_camera(props)
44        largest_yuv = its.objects.get_largest_yuv_format(props)
45        if debug:
46            fmt = largest_yuv
47        else:
48            match_ar = (largest_yuv['width'], largest_yuv['height'])
49            fmt = its.objects.get_smallest_yuv_format(props, match_ar=match_ar)
50
51        ev_per_step = its.objects.rational_to_float(
52                props['android.control.aeCompensationStep'])
53        steps_per_ev = int(1.0 / ev_per_step)
54        evs = range(-2 * steps_per_ev, 2 * steps_per_ev + 1, steps_per_ev)
55        lumas = []
56
57        # Converge 3A, and lock AE once converged. skip AF trigger as
58        # dark/bright scene could make AF convergence fail and this test
59        # doesn't care the image sharpness.
60        cam.do_3a(ev_comp=0, lock_ae=True, do_af=False, mono_camera=mono_camera)
61
62        for ev in evs:
63            # Capture a single shot with the same EV comp and locked AE.
64            req = its.objects.auto_capture_request()
65            req['android.control.aeExposureCompensation'] = ev
66            req['android.control.aeLock'] = True
67            caps = cam.do_capture([req]*THRESH_CONVERGE_FOR_EV, fmt)
68            luma_locked = []
69            for i, cap in enumerate(caps):
70                if cap['metadata']['android.control.aeState'] == LOCKED:
71                    y = its.image.convert_capture_to_planes(cap)[0]
72                    tile = its.image.get_image_patch(y, 0.45, 0.45, 0.1, 0.1)
73                    luma = its.image.compute_image_means(tile)[0]
74                    luma_locked.append(luma)
75                    if i == THRESH_CONVERGE_FOR_EV-1:
76                        lumas.append(luma)
77                        print 'lumas in AE locked captures: ', luma_locked
78                        msg = 'AE locked lumas: %s, RTOL: %.2f' % (
79                                str(luma_locked), LUMA_LOCKED_TOL)
80                        assert np.isclose(min(luma_locked), max(luma_locked),
81                                          rtol=LUMA_LOCKED_TOL), msg
82            assert caps[THRESH_CONVERGE_FOR_EV-1]['metadata']['android.control.aeState'] == LOCKED
83
84        pylab.plot(evs, lumas, '-ro')
85        pylab.title(NAME)
86        pylab.xlabel('EV Compensation')
87        pylab.ylabel('Mean Luma (Normalized)')
88        matplotlib.pyplot.savefig('%s_plot_means.png' % (NAME))
89
90        # Trim extra saturated images
91        while (lumas[-2] >= YUV_SAT_MIN/YUV_FULL_SCALE and
92               lumas[-1] >= YUV_SAT_MIN/YUV_FULL_SCALE and
93               len(lumas) > 2):
94            lumas.pop(-1)
95            print 'Removed saturated image.'
96
97        # Only allow positive EVs to give saturated image
98        assert len(lumas) > 2, '3 or more unsaturated images needed'
99        min_luma_diffs = min(np.diff(lumas))
100        print 'Min of the luma value difference between adjacent ev comp: ',
101        print min_luma_diffs
102        # All luma brightness should be increasing with increasing ev comp.
103        assert min_luma_diffs > 0, 'Luma is not increasing!'
104
105if __name__ == '__main__':
106    main()
107