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 os
16
17import its.caps
18import its.device
19import its.image
20import its.objects
21import numpy as np
22
23NAME = os.path.basename(__file__).split('.')[0]
24PATTERNS = [1, 2]
25COLOR_BAR_ORDER = ['WHITE', 'YELLOW', 'CYAN', 'GREEN', 'MAGENTA', 'RED',
26                   'BLUE', 'BLACK']
27COLOR_CHECKER = {'BLACK': [0, 0, 0], 'RED': [1, 0, 0], 'GREEN': [0, 1, 0],
28                 'BLUE': [0, 0, 1], 'MAGENTA': [1, 0, 1], 'CYAN': [0, 1, 1],
29                 'YELLOW': [1, 1, 0], 'WHITE': [1, 1, 1]}
30CH_TOL = 2E-3  # 1/2 DN in [0:1]
31LSFR_COEFFS = 0b100010000  # PN9
32
33
34def check_solid_color(cap, props):
35    """Simple test for solid color.
36
37    Args:
38        cap: capture element
39        props: capture properties
40    Returns:
41        True/False
42    """
43    print 'Checking solid TestPattern...'
44    r, gr, gb, b = its.image.convert_capture_to_planes(cap, props)
45    r_tile = its.image.get_image_patch(r, 0.0, 0.0, 1.0, 1.0)
46    gr_tile = its.image.get_image_patch(gr, 0.0, 0.0, 1.0, 1.0)
47    gb_tile = its.image.get_image_patch(gb, 0.0, 0.0, 1.0, 1.0)
48    b_tile = its.image.get_image_patch(b, 0.0, 0.0, 1.0, 1.0)
49    var_max = max(np.amax(r_tile), np.amax(gr_tile), np.amax(gb_tile),
50                  np.amax(b_tile))
51    var_min = min(np.amin(r_tile), np.amin(gr_tile), np.amin(gb_tile),
52                  np.amin(b_tile))
53    white_level = int(props['android.sensor.info.whiteLevel'])
54    print ' pixel min: %.f, pixel max: %.f' % (white_level*var_min,
55                                               white_level*var_max)
56    return np.isclose(var_max, var_min, atol=CH_TOL)
57
58
59def check_color_bars(cap, props, mirror=False):
60    """Test image for color bars.
61
62    Compute avg of bars and compare to ideal
63
64    Args:
65        cap:            capture element
66        props:          capture properties
67        mirror (bool):  whether to mirror image or not
68    Returns:
69        True/False
70    """
71    print 'Checking color bar TestPattern...'
72    delta = 0.0005
73    num_bars = len(COLOR_BAR_ORDER)
74    color_match = []
75    img = its.image.convert_capture_to_rgb_image(cap, props=props)
76    if mirror:
77        print ' Image mirrored'
78        img = np.fliplr(img)
79    for i, color in enumerate(COLOR_BAR_ORDER):
80        tile = its.image.get_image_patch(img, float(i)/num_bars+delta,
81                                         0.0, 1.0/num_bars-2*delta, 1.0)
82        color_match.append(np.allclose(its.image.compute_image_means(tile),
83                                       COLOR_CHECKER[color], atol=CH_TOL))
84    print COLOR_BAR_ORDER
85    print color_match
86    return all(color_match)
87
88
89def check_pattern(cap, props, pattern):
90    """Simple tests for pattern correctness.
91
92    Args:
93        cap: capture element
94        props: capture properties
95        pattern (int): valid number for pattern
96    Returns:
97        boolean
98    """
99
100    # white_level = int(props['android.sensor.info.whiteLevel'])
101    if pattern == 1:  # solid color
102        return check_solid_color(cap, props)
103
104    elif pattern == 2:  # color bars
105        striped = check_color_bars(cap, props, mirror=False)
106        # check mirrored version in case image rotated from sensor orientation
107        if not striped:
108            striped = check_color_bars(cap, props, mirror=True)
109        return striped
110
111    else:
112        print 'No specific test for TestPattern %d' % pattern
113        return True
114
115
116def test_test_patterns(cam, props, af_fd):
117    """test image sensor test patterns.
118
119    Args:
120        cam: An open device session.
121        props: Properties of cam
122        af_fd: Focus distance
123    """
124
125    avail_patterns = props['android.sensor.availableTestPatternModes']
126    print 'avail_patterns: ', avail_patterns
127    sens_min, _ = props['android.sensor.info.sensitivityRange']
128    exposure = min(props['android.sensor.info.exposureTimeRange'])
129
130    for pattern in PATTERNS:
131        if pattern in avail_patterns:
132            req = its.objects.manual_capture_request(int(sens_min),
133                                                     exposure)
134            req['android.lens.focusDistance'] = af_fd
135            req['android.sensor.testPatternMode'] = pattern
136            fmt = {'format': 'raw'}
137            cap = cam.do_capture(req, fmt)
138            img = its.image.convert_capture_to_rgb_image(cap, props=props)
139
140            # Save pattern
141            its.image.write_image(img, '%s_%d.jpg' % (NAME, pattern), True)
142
143            # Check pattern for correctness
144            assert check_pattern(cap, props, pattern)
145        else:
146            print 'Pattern not in android.sensor.availableTestPatternModes.'
147
148
149def main():
150    """Test pattern generation test.
151
152    Test: capture frames for each valid test pattern and check if
153    generated correctly.
154    android.sensor.testPatternMode
155    0: OFF
156    1: SOLID_COLOR
157    2: COLOR_BARS
158    3: COLOR_BARS_FADE_TO_GREY
159    4: PN9
160    """
161
162    print '\nStarting %s' % NAME
163    with its.device.ItsSession() as cam:
164        props = cam.get_camera_properties()
165        its.caps.skip_unless(its.caps.raw16(props) and
166                             its.caps.manual_sensor(props) and
167                             its.caps.per_frame_control(props))
168
169        # For test pattern, use min_fd
170        fd = props['android.lens.info.minimumFocusDistance']
171        test_test_patterns(cam, props, fd)
172
173if __name__ == '__main__':
174    main()
175