1# Copyright 2013 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 its.image
16import its.device
17import its.objects
18import os.path
19import pprint
20import math
21import numpy
22import matplotlib.pyplot
23import mpl_toolkits.mplot3d
24
25def main():
26    """Test that valid data comes back in CaptureResult objects.
27    """
28    NAME = os.path.basename(__file__).split(".")[0]
29
30    manual_tonemap = [0,0, 1,1] # Linear
31    manual_transform = its.objects.int_to_rational([1,2,3, 4,5,6, 7,8,9])
32    manual_gains = [1,2,3,4]
33    manual_region = [8,8,128,128]
34    manual_exp_time = 100*1000*1000
35    manual_sensitivity = 100
36
37    auto_req = its.objects.auto_capture_request()
38    auto_req["android.statistics.lensShadingMapMode"] = 1
39
40    manual_req = {
41        "android.control.mode": 0,
42        "android.control.aeMode": 0,
43        "android.control.awbMode": 0,
44        "android.control.afMode": 0,
45        "android.sensor.frameDuration": 0,
46        "android.sensor.sensitivity": manual_sensitivity,
47        "android.sensor.exposureTime": manual_exp_time,
48        "android.colorCorrection.mode": 0,
49        "android.colorCorrection.transform": manual_transform,
50        "android.colorCorrection.gains": manual_gains,
51        "android.tonemap.mode": 0,
52        "android.tonemap.curveRed": manual_tonemap,
53        "android.tonemap.curveGreen": manual_tonemap,
54        "android.tonemap.curveBlue": manual_tonemap,
55        "android.control.aeRegions": manual_region,
56        "android.control.afRegions": manual_region,
57        "android.control.awbRegions": manual_region,
58        "android.statistics.lensShadingMapMode":1
59        }
60
61    def r2f(r):
62        return float(r["numerator"]) / float(r["denominator"])
63
64    # A very loose definition for two floats being close to each other;
65    # there may be different interpolation and rounding used to get the
66    # two values, and all this test is looking at is whether there is
67    # something obviously broken; it's not looking for a perfect match.
68    def is_close_float(n1, n2):
69        return abs(n1 - n2) < 0.05
70
71    def is_close_rational(n1, n2):
72        return is_close_float(r2f(n1), r2f(n2))
73
74    def draw_lsc_plot(w_map, h_map, lsc_map, name):
75        fig = matplotlib.pyplot.figure()
76        ax = fig.gca(projection='3d')
77        xs = numpy.array([range(h_map)] * w_map).reshape(w_map, h_map)
78        ys = numpy.array([[i]*h_map for i in range(w_map)]).reshape(w_map, h_map)
79        for ch in range(4):
80            size = w_map*h_map
81            start = ch * size
82            zs = numpy.array(lsc_map[start:start+size]).reshape(w_map, h_map)
83            ax.plot_wireframe(xs, ys, zs)
84        matplotlib.pyplot.savefig("%s_plot_lsc_%s.png" % (NAME, name))
85
86    def test_auto(cam, w_map, h_map):
87        # Get 3A lock first, so the auto values in the capture result are
88        # populated properly.
89        rect = [0,0,1,1]
90        cam.do_3a(rect, rect, rect, True, True, False)
91
92        fname, w, h, cap_res = cam.do_capture(auto_req)
93        gains = cap_res["android.colorCorrection.gains"]
94        transform = cap_res["android.colorCorrection.transform"]
95        exp_time = cap_res['android.sensor.exposureTime']
96        lsc_map = cap_res["android.statistics.lensShadingMap"]
97        ctrl_mode = cap_res["android.control.mode"]
98
99        print "Control mode:", ctrl_mode
100        print "Gains:", gains
101        print "Transform:", [r2f(t) for t in transform]
102        print "AE region:", cap_res['android.control.aeRegions']
103        print "AF region:", cap_res['android.control.afRegions']
104        print "AWB region:", cap_res['android.control.awbRegions']
105        print "LSC map:", w_map, h_map, lsc_map[:8]
106
107        assert(ctrl_mode == 1)
108
109        # Color correction gain and transform must be valid.
110        assert(len(gains) == 4)
111        assert(len(transform) == 9)
112        assert(all([g > 0 for g in gains]))
113        assert(all([t["denominator"] != 0 for t in transform]))
114
115        # Color correction should not match the manual settings.
116        assert(any([not is_close_float(gains[i], manual_gains[i])
117                    for i in xrange(4)]))
118        assert(any([not is_close_rational(transform[i], manual_transform[i])
119                    for i in xrange(9)]))
120
121        # Exposure time must be valid.
122        assert(exp_time > 0)
123
124        # 3A regions must be valid.
125        assert(len(cap_res['android.control.aeRegions']) == 5)
126        assert(len(cap_res['android.control.afRegions']) == 5)
127        assert(len(cap_res['android.control.awbRegions']) == 5)
128
129        # Lens shading map must be valid.
130        assert(w_map > 0 and h_map > 0 and w_map * h_map * 4 == len(lsc_map))
131        assert(all([m >= 1 for m in lsc_map]))
132
133        draw_lsc_plot(w_map, h_map, lsc_map, "auto")
134
135        return lsc_map
136
137    def test_manual(cam, w_map, h_map, lsc_map_auto):
138        fname, w, h, cap_res = cam.do_capture(manual_req)
139        gains = cap_res["android.colorCorrection.gains"]
140        transform = cap_res["android.colorCorrection.transform"]
141        curves = [cap_res["android.tonemap.curveRed"],
142                  cap_res["android.tonemap.curveGreen"],
143                  cap_res["android.tonemap.curveBlue"]]
144        exp_time = cap_res['android.sensor.exposureTime']
145        lsc_map = cap_res["android.statistics.lensShadingMap"]
146        pred_gains = cap_res["android.statistics.predictedColorGains"]
147        pred_transform = cap_res["android.statistics.predictedColorTransform"]
148        ctrl_mode = cap_res["android.control.mode"]
149
150        print "Control mode:", ctrl_mode
151        print "Gains:", gains
152        print "Transform:", [r2f(t) for t in transform]
153        print "Predicted gains:", pred_gains
154        print "Predicted transform:", [r2f(t) for t in pred_transform]
155        print "Tonemap:", curves[0][1::16]
156        print "AE region:", cap_res['android.control.aeRegions']
157        print "AF region:", cap_res['android.control.afRegions']
158        print "AWB region:", cap_res['android.control.awbRegions']
159        print "LSC map:", w_map, h_map, lsc_map[:8]
160
161        assert(ctrl_mode == 0)
162
163        # Color correction gain and transform must be valid.
164        # Color correction gains and transform should be the same size and
165        # values as the manually set values.
166        assert(len(gains) == 4)
167        assert(len(transform) == 9)
168        assert(all([is_close_float(gains[i], manual_gains[i])
169                    for i in xrange(4)]))
170        assert(all([is_close_rational(transform[i], manual_transform[i])
171                    for i in xrange(9)]))
172
173        # The predicted gains and transform must also be valid.
174        assert(len(pred_gains) == 4)
175        assert(len(pred_transform) == 9)
176
177        # Tonemap must be valid.
178        # The returned tonemap must be linear.
179        for c in curves:
180            assert(len(c) > 0)
181            assert(all([is_close_float(c[i], c[i+1])
182                        for i in xrange(0,len(c),2)]))
183
184        # Exposure time must be close to the requested exposure time.
185        assert(is_close_float(exp_time/1000000.0, manual_exp_time/1000000.0))
186
187        # 3A regions must be valid. They don't need to actually match what was
188        # requesed, since the SOC may not support those regions exactly.
189        assert(len(cap_res['android.control.aeRegions']) == 5)
190        assert(len(cap_res['android.control.afRegions']) == 5)
191        assert(len(cap_res['android.control.awbRegions']) == 5)
192
193        # Lens shading map must be valid.
194        assert(w_map > 0 and h_map > 0 and w_map * h_map * 4 == len(lsc_map))
195        assert(all([m >= 1 for m in lsc_map]))
196
197        # Lens shading map must take into account the manual color correction
198        # settings. Test this by ensuring that the map is different between
199        # the auto and manual test cases.
200        assert(lsc_map != lsc_map_auto)
201
202        draw_lsc_plot(w_map, h_map, lsc_map, "manual")
203
204    with its.device.ItsSession() as cam:
205        props = cam.get_camera_properties()
206        w_map = props["android.lens.info.shadingMapSize"]["width"]
207        h_map = props["android.lens.info.shadingMapSize"]["height"]
208
209        print "Testing auto capture results"
210        lsc_map_auto = test_auto(cam, w_map, h_map)
211        print "Testing manual capture results"
212        test_manual(cam, w_map, h_map, lsc_map_auto)
213        print "Testing auto capture results again"
214        test_auto(cam, w_map, h_map)
215
216if __name__ == '__main__':
217    main()
218
219