1# Copyright 2015 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.device
19import its.image
20import its.objects
21
22import matplotlib
23from matplotlib import pylab
24import numpy
25
26NAME = os.path.basename(__file__).split('.')[0]
27NUM_FRAMES = 4  # number of frames for temporal info to settle
28NUM_SHADING_MODE_SWITCH_LOOPS = 3
29SHADING_MODES = ['OFF', 'FAST', 'HQ']
30THRESHOLD_DIFF_RATIO = 0.15
31
32
33def main():
34    """Test that the android.shading.mode param is applied.
35
36    Switching shading modes and checks that the lens shading maps are
37    modified as expected.
38    """
39
40    with its.device.ItsSession() as cam:
41        props = cam.get_camera_properties()
42
43        its.caps.skip_unless(its.caps.per_frame_control(props) and
44                             its.caps.lsc_map(props) and
45                             its.caps.lsc_off(props))
46
47        mono_camera = its.caps.mono_camera(props)
48
49        # lsc_off devices should always support OFF(0), FAST(1), and HQ(2)
50        assert(props.has_key('android.shading.availableModes') and
51               set(props['android.shading.availableModes']) == set([0, 1, 2]))
52
53        # Test 1: Switching shading modes several times and verify:
54        #   1. Lens shading maps with mode OFF are all 1.0
55        #   2. Lens shading maps with mode FAST are similar after switching
56        #      shading modes.
57        #   3. Lens shading maps with mode HIGH_QUALITY are similar after
58        #      switching shading modes.
59        cam.do_3a(mono_camera=mono_camera)
60
61        # Use smallest yuv size matching the aspect ratio of largest yuv size to
62        # reduce some USB bandwidth overhead since we are only looking at output
63        # metadata in this test.
64        largest_yuv_fmt = its.objects.get_largest_yuv_format(props)
65        largest_yuv_size = (largest_yuv_fmt['width'], largest_yuv_fmt['height'])
66        cap_fmt = its.objects.get_smallest_yuv_format(props, largest_yuv_size)
67
68        # Get the reference lens shading maps for OFF, FAST, and HIGH_QUALITY
69        # in different sessions.
70        # reference_maps[mode]
71        num_shading_modes = len(SHADING_MODES)
72        reference_maps = [[] for mode in range(num_shading_modes)]
73        num_map_gains = 0
74        for mode in range(1, num_shading_modes):
75            req = its.objects.auto_capture_request()
76            req['android.statistics.lensShadingMapMode'] = 1
77            req['android.shading.mode'] = mode
78            cap_res = cam.do_capture([req]*NUM_FRAMES, cap_fmt)[NUM_FRAMES-1]['metadata']
79            lsc_map = cap_res['android.statistics.lensShadingCorrectionMap']
80            assert(lsc_map.has_key('width') and
81                   lsc_map.has_key('height') and
82                   lsc_map['width'] is not None and
83                   lsc_map['height'] is not None)
84            if mode == 1:
85                num_map_gains = lsc_map['width'] * lsc_map['height'] * 4
86                reference_maps[0] = [1.0] * num_map_gains
87            reference_maps[mode] = lsc_map['map']
88
89        # Get the lens shading maps while switching modes in one session.
90        reqs = []
91        for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
92            for mode in range(num_shading_modes):
93                for _ in range(NUM_FRAMES):
94                    req = its.objects.auto_capture_request()
95                    req['android.statistics.lensShadingMapMode'] = 1
96                    req['android.shading.mode'] = mode
97                    reqs.append(req)
98
99        caps = cam.do_capture(reqs, cap_fmt)
100
101        # shading_maps[mode][loop]
102        shading_maps = [[[] for loop in range(NUM_SHADING_MODE_SWITCH_LOOPS)]
103                        for mode in range(num_shading_modes)]
104
105        # Get the shading maps out of capture results
106        for i in range(len(caps)/NUM_FRAMES):
107            shading_maps[i%num_shading_modes][i/NUM_SHADING_MODE_SWITCH_LOOPS] = \
108                    caps[(i+1)*NUM_FRAMES-1]['metadata']['android.statistics.lensShadingCorrectionMap']['map']
109
110        # Draw the maps
111        for mode in range(num_shading_modes):
112            for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
113                pylab.clf()
114                pylab.figure(figsize=(5, 5))
115                pylab.subplot(2, 1, 1)
116                pylab.plot(range(num_map_gains), shading_maps[mode][i], '-r.',
117                           label='shading', alpha=0.7)
118                pylab.plot(range(num_map_gains), reference_maps[mode], '-g.',
119                           label='ref', alpha=0.7)
120                pylab.xlim([0, num_map_gains])
121                pylab.ylim([0.9, 4.0])
122                name = '%s_ls_maps_mode_%d_loop_%d' % (NAME, mode, i)
123                pylab.title(name)
124                pylab.xlabel('Map gains')
125                pylab.ylabel('Lens shading maps')
126                pylab.legend(loc='upper center', numpoints=1, fancybox=True)
127
128                pylab.subplot(2, 1, 2)
129                shading_ref_ratio = numpy.divide(
130                        shading_maps[mode][i], reference_maps[mode])
131                pylab.plot(range(num_map_gains), shading_ref_ratio, '-b.',
132                           clip_on=False)
133                pylab.xlim([0, num_map_gains])
134                pylab.ylim([1.0-THRESHOLD_DIFF_RATIO, 1.0+THRESHOLD_DIFF_RATIO])
135                pylab.title('Shading/reference Maps Ratio vs Gain')
136                pylab.xlabel('Map gains')
137                pylab.ylabel('Shading/ref maps ratio')
138
139                pylab.tight_layout()
140                matplotlib.pyplot.savefig('%s.png' % name)
141
142        for mode in range(num_shading_modes):
143            if mode == 0:
144                print 'Verifying lens shading maps with mode %s are all 1.0' % (
145                        SHADING_MODES[mode])
146            else:
147                print 'Verifying lens shading maps with mode %s are similar' % (
148                        SHADING_MODES[mode])
149            for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
150                e_msg = 'FAIL mode: %s, loop: %d, THRESH: %.2f' % (
151                        SHADING_MODES[mode], i, THRESHOLD_DIFF_RATIO)
152                assert (numpy.allclose(shading_maps[mode][i],
153                                       reference_maps[mode],
154                                       rtol=THRESHOLD_DIFF_RATIO)), e_msg
155
156if __name__ == '__main__':
157    main()
158