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.caps
17import its.device
18import its.objects
19import its.target
20import numpy
21import math
22from matplotlib import pylab
23import os.path
24import matplotlib
25import matplotlib.pyplot
26
27NAME = os.path.basename(__file__).split('.')[0]
28RESIDUAL_THRESHOLD = 0.0003  # approximately each sample is off by 2/255
29# The HAL3.2 spec requires that curves up to 64 control points in length
30# must be supported.
31L = 64
32LM1 = float(L-1)
33
34
35def main():
36    """Test that device processing can be inverted to linear pixels.
37
38    Captures a sequence of shots with the device pointed at a uniform
39    target. Attempts to invert all the ISP processing to get back to
40    linear R,G,B pixel data.
41    """
42    gamma_lut = numpy.array(
43        sum([[i/LM1, math.pow(i/LM1, 1/2.2)] for i in xrange(L)], []))
44    inv_gamma_lut = numpy.array(
45        sum([[i/LM1, math.pow(i/LM1, 2.2)] for i in xrange(L)], []))
46
47    with its.device.ItsSession() as cam:
48        props = cam.get_camera_properties()
49        its.caps.skip_unless(its.caps.compute_target_exposure(props) and
50                             its.caps.per_frame_control(props))
51
52        debug = its.caps.debug_mode()
53        largest_yuv = its.objects.get_largest_yuv_format(props)
54        if debug:
55            fmt = largest_yuv
56        else:
57            match_ar = (largest_yuv['width'], largest_yuv['height'])
58            fmt = its.objects.get_smallest_yuv_format(props, match_ar=match_ar)
59
60        e,s = its.target.get_target_exposure_combos(cam)["midSensitivity"]
61        s /= 2
62        sens_range = props['android.sensor.info.sensitivityRange']
63        sensitivities = [s*1.0/3.0, s*2.0/3.0, s, s*4.0/3.0, s*5.0/3.0]
64        sensitivities = [s for s in sensitivities
65                         if s > sens_range[0] and s < sens_range[1]]
66
67        req = its.objects.manual_capture_request(0, e)
68        req['android.blackLevel.lock'] = True
69        req['android.tonemap.mode'] = 0
70        req['android.tonemap.curveRed'] = gamma_lut.tolist()
71        req['android.tonemap.curveGreen'] = gamma_lut.tolist()
72        req['android.tonemap.curveBlue'] = gamma_lut.tolist()
73
74        r_means = []
75        g_means = []
76        b_means = []
77
78        for sens in sensitivities:
79            req["android.sensor.sensitivity"] = sens
80            cap = cam.do_capture(req, fmt)
81            img = its.image.convert_capture_to_rgb_image(cap)
82            its.image.write_image(
83                img, '%s_sens=%04d.jpg' % (NAME, sens))
84            img = its.image.apply_lut_to_image(img, inv_gamma_lut[1::2] * LM1)
85            tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
86            rgb_means = its.image.compute_image_means(tile)
87            r_means.append(rgb_means[0])
88            g_means.append(rgb_means[1])
89            b_means.append(rgb_means[2])
90
91        pylab.title(NAME)
92        pylab.plot(sensitivities, r_means, '-ro')
93        pylab.plot(sensitivities, g_means, '-go')
94        pylab.plot(sensitivities, b_means, '-bo')
95        pylab.xlim([sens_range[0], sens_range[1]/2])
96        pylab.ylim([0, 1])
97        pylab.xlabel('sensitivity(ISO)')
98        pylab.ylabel('RGB avg [0, 1]')
99        matplotlib.pyplot.savefig('%s_plot_means.png' % (NAME))
100
101        # Check that each plot is actually linear.
102        for means in [r_means, g_means, b_means]:
103            line, residuals, _, _, _ = numpy.polyfit(range(len(sensitivities)),
104                                                     means, 1, full=True)
105            print 'Line: m=%f, b=%f, resid=%f'%(line[0], line[1], residuals[0])
106            assert residuals[0] < RESIDUAL_THRESHOLD
107
108if __name__ == '__main__':
109    main()
110
111