test_capture_result.py revision 87b68b020dd72c4cdcf3b8c1f9196c060f947991
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    # TODO: Query the allowable tonemap curve sizes; here, it's hardcoded to
31    # a length=64 list of tuples. The max allowed length should be inside the
32    # camera properties object.
33    L = 32
34    LM1 = float(L-1)
35
36    manual_tonemap = sum([[i/LM1, i/LM1] for i in range(L)], [])
37    manual_transform = its.objects.int_to_rational([1,2,3, 4,5,6, 7,8,9])
38    manual_gains = [1,2,3,4]
39    manual_region = [8,8,128,128]
40    manual_exp_time = 100*1000*1000
41    manual_sensitivity = 100
42
43    auto_req = its.objects.capture_request( {
44        "android.control.mode": 1,
45        "android.control.aeMode": 1,
46        "android.control.awbMode": 1,
47        "android.control.afMode": 1,
48        "android.colorCorrection.mode": 1,
49        "android.tonemap.mode": 1,
50        "android.statistics.lensShadingMapMode":1
51        })
52
53    manual_req = its.objects.capture_request( {
54        "android.control.mode": 0,
55        "android.control.aeMode": 0,
56        "android.control.awbMode": 0,
57        "android.control.afMode": 0,
58        "android.sensor.frameDuration": 0,
59        "android.sensor.sensitivity": manual_sensitivity,
60        "android.sensor.exposureTime": manual_exp_time,
61        "android.colorCorrection.mode": 0,
62        "android.colorCorrection.transform": manual_transform,
63        "android.colorCorrection.gains": manual_gains,
64        "android.tonemap.mode": 0,
65        "android.tonemap.curveRed": manual_tonemap,
66        "android.tonemap.curveGreen": manual_tonemap,
67        "android.tonemap.curveBlue": manual_tonemap,
68        "android.control.aeRegions": manual_region,
69        "android.control.afRegions": manual_region,
70        "android.control.awbRegions": manual_region,
71        "android.statistics.lensShadingMapMode":1
72        })
73
74    def r2f(r):
75        return float(r["numerator"]) / float(r["denominator"])
76
77    # A very loose definition for two floats being close to each other;
78    # there may be different interpolation and rounding used to get the
79    # two values, and all this test is looking at is whether there is
80    # something obviously broken; it's not looking for a perfect match.
81    def is_close_float(n1, n2):
82        return abs(n1 - n2) < 0.05
83
84    def is_close_rational(n1, n2):
85        return is_close_float(r2f(n1), r2f(n2))
86
87    def draw_lsc_plot(w_map, h_map, lsc_map, name):
88        fig = matplotlib.pyplot.figure()
89        ax = fig.gca(projection='3d')
90        xs = numpy.array([range(h_map)] * w_map).reshape(w_map, h_map)
91        ys = numpy.array([[i]*h_map for i in range(w_map)]).reshape(w_map, h_map)
92        for ch in range(4):
93            size = w_map*h_map
94            start = ch * size
95            zs = numpy.array(lsc_map[start:start+size]).reshape(w_map, h_map)
96            ax.plot_wireframe(xs, ys, zs)
97        matplotlib.pyplot.savefig("%s_plot_lsc_%s.png" % (NAME, name))
98
99    def test_auto(cam, w_map, h_map):
100        fname, w, h, md_obj = cam.do_capture(auto_req)
101        cap_res = md_obj["captureResult"]
102        gains = cap_res["android.colorCorrection.gains"]
103        transform = cap_res["android.colorCorrection.transform"]
104        curves = [cap_res["android.tonemap.curveRed"],
105                  cap_res["android.tonemap.curveGreen"],
106                  cap_res["android.tonemap.curveBlue"]]
107        exp_time = cap_res['android.sensor.exposureTime']
108        lsc_map = cap_res["android.statistics.lensShadingMap"]
109
110        print "Gains:", gains
111        print "Transform:", [r2f(t) for t in transform]
112        print "Tonemap:", curves[0][1::16]
113        print "AE region:", cap_res['android.control.aeRegions']
114        print "AF region:", cap_res['android.control.afRegions']
115        print "AWB region:", cap_res['android.control.awbRegions']
116        print "LSC map:", w_map, h_map, lsc_map[:8]
117
118        # Color correction gain and transform must be valid.
119        assert(len(gains) == 4)
120        assert(len(transform) == 9)
121        assert(all([g > 0 for g in gains]))
122        assert(all([t["denominator"] != 0 for t in transform]))
123
124        # Color correction should not match the manual settings.
125        assert(any([not is_close_float(gains[i], manual_gains[i])
126                    for i in xrange(4)]))
127        assert(any([not is_close_rational(transform[i], manual_transform[i])
128                    for i in xrange(9)]))
129
130        # Tonemap need not be valid, but if it is, it should hold useful values.
131        if any([len(c) > 0 for c in curves]):
132            assert(all([len(c) > 0 for c in curves]))
133
134        # Exposure time must be valid.
135        assert(exp_time > 0)
136
137        # 3A regions must be valid.
138        assert(len(cap_res['android.control.aeRegions']) == 5)
139        assert(len(cap_res['android.control.afRegions']) == 5)
140        assert(len(cap_res['android.control.awbRegions']) == 5)
141
142        # Lens shading map must be valid.
143        assert(w_map > 0 and h_map > 0 and w_map * h_map * 4 == len(lsc_map))
144        assert(all([m >= 1 for m in lsc_map]))
145
146        draw_lsc_plot(w_map, h_map, lsc_map, "auto")
147
148        return lsc_map
149
150    def test_manual(cam, w_map, h_map, lsc_map_auto):
151        fname, w, h, md_obj = cam.do_capture(manual_req)
152        cap_res = md_obj["captureResult"]
153        gains = cap_res["android.colorCorrection.gains"]
154        transform = cap_res["android.colorCorrection.transform"]
155        curves = [cap_res["android.tonemap.curveRed"],
156                  cap_res["android.tonemap.curveGreen"],
157                  cap_res["android.tonemap.curveBlue"]]
158        exp_time = cap_res['android.sensor.exposureTime']
159        lsc_map = cap_res["android.statistics.lensShadingMap"]
160
161        print "Gains:", gains
162        print "Transform:", [r2f(t) for t in transform]
163        print "Tonemap:", curves[0][1::16]
164        print "AE region:", cap_res['android.control.aeRegions']
165        print "AF region:", cap_res['android.control.afRegions']
166        print "AWB region:", cap_res['android.control.awbRegions']
167        print "LSC map:", w_map, h_map, lsc_map[:8]
168
169        # Color correction gain and transform must be valid.
170        # Color correction gains and transform should be the same size and
171        # values as the manually set values.
172        assert(len(gains) == 4)
173        assert(len(transform) == 9)
174        assert(all([is_close_float(gains[i], manual_gains[i])
175                    for i in xrange(4)]))
176        assert(all([is_close_rational(transform[i], manual_transform[i])
177                    for i in xrange(9)]))
178
179        # Tonemap must be valid.
180        # The returned tonemap can be interpolated from the provided values.
181        for c in curves:
182            assert(len(c) > 0)
183            for i, val in enumerate(c):
184                ii = int(math.floor(
185                        ((i+0.5) / float(len(c))*float(len(manual_tonemap)))))
186                assert(is_close_float(c[i], manual_tonemap[ii]))
187
188        # Exposure time must be close to the requested exposure time.
189        assert(is_close_float(exp_time/1000000.0, manual_exp_time/1000000.0))
190
191        # 3A regions must be valid, and must match the manual regions.
192        # TODO: Uncomment these assertions once the bug is fixed.
193        #assert(cap_res['android.control.aeRegions'][:4] == manual_region)
194        #assert(cap_res['android.control.afRegions'][:4] == manual_region)
195        #assert(cap_res['android.control.awbRegions'][:4] == manual_region)
196
197        # Lens shading map must be valid.
198        assert(w_map > 0 and h_map > 0 and w_map * h_map * 4 == len(lsc_map))
199        assert(all([m >= 1 for m in lsc_map]))
200
201        # Lens shading map must take into account the manual color correction
202        # settings. Test this by ensuring that the map is different between
203        # the auto and manual test cases.
204        # TODO: Uncomment these assertions once the bug is fixed.
205        #assert(lsc_map != lsc_map_auto)
206
207        draw_lsc_plot(w_map, h_map, lsc_map, "manual")
208
209    with its.device.ItsSession() as cam:
210        props = cam.get_camera_properties()
211        w_map = props["android.lens.info.shadingMapSize"]["width"]
212        h_map = props["android.lens.info.shadingMapSize"]["height"]
213
214        print "Testing auto capture results"
215        lsc_map_auto = test_auto(cam, w_map, h_map)
216        print "Testing manual capture results"
217        test_manual(cam, w_map, h_map, lsc_map_auto)
218        print "Testing auto capture results again"
219        test_auto(cam, w_map, h_map)
220
221if __name__ == '__main__':
222    main()
223
224