1# Copyright 2018 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]
24PATTERN = 2  # Note scene0/test_test_patterns must PASS
25COLOR_BARS = ['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]}
30DELTA = 0.0005  # crop on edge of color bars
31RAW_TOL = 0.001  # 1 DN in [0:1] (1/(1023-64)
32RGB_VAR_TOL = 0.0039  # 1/255
33RGB_MEAN_TOL = 0.1
34TONEMAP_MAX = 0.5
35
36
37def check_raw_pattern(img_raw):
38    """Check for RAW capture matches color bar pattern.
39
40    Args:
41        img_raw: RAW image
42    """
43
44    print 'Checking RAW/PATTERN match'
45    n_bars = len(COLOR_BARS)
46    color_match = []
47    for i in range(n_bars):
48        print 'patch:', i,
49        raw_patch = its.image.get_image_patch(
50                img_raw, float(i)/n_bars+DELTA, 0.0, 1.0/n_bars-2*DELTA, 1.0)
51        raw_means = its.image.compute_image_means(raw_patch)
52        for color in COLOR_BARS:
53            if np.allclose(COLOR_CHECKER[color], raw_means, atol=RAW_TOL):
54                color_match.append(color)
55                print '%s' % color
56    assert set(color_match) == set(COLOR_BARS), 'RAW does not have all colors'
57
58
59def check_yuv_vs_raw(img_raw, img_yuv):
60    """Check for YUV vs RAW match in 8 patches.
61
62    Check for correct values and color consistency
63
64    Args:
65        img_raw: RAW image
66        img_yuv: YUV image
67    """
68
69    print 'Checking YUV/RAW match'
70    n_bars = len(COLOR_BARS)
71    color_match_errs = []
72    color_variance_errs = []
73    for i in range(n_bars):
74        raw_patch = its.image.get_image_patch(
75                img_raw, float(i)/n_bars+DELTA, 0.0, 1.0/n_bars-2*DELTA, 1.0)
76        yuv_patch = its.image.get_image_patch(
77                img_yuv, float(i)/n_bars+DELTA, 0.0, 1.0/n_bars-2*DELTA, 1.0)
78        raw_means = np.array(its.image.compute_image_means(raw_patch))
79        raw_vars = np.array(its.image.compute_image_variances(raw_patch))
80        yuv_means = np.array(its.image.compute_image_means(yuv_patch))
81        yuv_means /= TONEMAP_MAX  # Normalize to tonemap max
82        yuv_vars = np.array(its.image.compute_image_variances(yuv_patch))
83        if not np.allclose(raw_means, yuv_means, atol=RGB_MEAN_TOL):
84            color_match_errs.append('RAW: %s, RGB(norm): %s, ATOL: %.2f' % (
85                    str(raw_means), str(np.round(yuv_means, 3)), RGB_MEAN_TOL))
86        if not np.allclose(raw_vars, yuv_vars, atol=RGB_VAR_TOL):
87            color_variance_errs.append('RAW: %s, RGB: %s, ATOL: %.4f' % (
88                    str(raw_vars), str(yuv_vars), RGB_VAR_TOL))
89    if color_match_errs:
90        print '\nColor match errors'
91        for err in color_match_errs:
92            print err
93    if color_variance_errs:
94        print '\nColor variance errors'
95        for err in color_variance_errs:
96            print err
97    assert not color_match_errs
98    assert not color_variance_errs
99
100
101def test_tonemap_curve(cam, props):
102    """test tonemap curve with sensor test pattern.
103
104    Args:
105        cam: An open device session.
106        props: Properties of cam
107    """
108
109    avail_patterns = props['android.sensor.availableTestPatternModes']
110    print 'avail_patterns: ', avail_patterns
111    sens_min, _ = props['android.sensor.info.sensitivityRange']
112    exp = min(props['android.sensor.info.exposureTimeRange'])
113
114    # Linear tonemap with maximum of 0.5
115    tmap = sum([[i/63.0, i/126.0] for i in range(64)], [])
116
117    if PATTERN in avail_patterns:
118        # RAW image
119        req_raw = its.objects.manual_capture_request(int(sens_min), exp)
120        req_raw['android.sensor.testPatternMode'] = PATTERN
121        fmt_raw = {'format': 'raw'}
122        cap_raw = cam.do_capture(req_raw, fmt_raw)
123        img_raw = its.image.convert_capture_to_rgb_image(
124                cap_raw, props=props)
125
126        # Save RAW pattern
127        its.image.write_image(img_raw, '%s_raw_%d.jpg' % (
128                NAME, PATTERN), True)
129        check_raw_pattern(img_raw)
130
131        # YUV image
132        req_yuv = its.objects.manual_capture_request(int(sens_min), exp)
133        req_yuv['android.sensor.testPatternMode'] = PATTERN
134        req_yuv['android.distortionCorrection.mode'] = 0
135        req_yuv['android.tonemap.mode'] = 0
136        req_yuv['android.tonemap.curve'] = {
137                'red': tmap, 'green': tmap, 'blue': tmap}
138        fmt_yuv = {'format': 'yuv', 'width': 640, 'height': 480}
139        cap_yuv = cam.do_capture(req_yuv, fmt_yuv)
140        img_yuv = its.image.convert_capture_to_rgb_image(cap_yuv, True)
141
142        # Save YUV pattern
143        its.image.write_image(img_yuv, '%s_yuv_%d.jpg' % (
144                NAME, PATTERN), True)
145
146        # Check pattern for correctness
147        check_yuv_vs_raw(img_raw, img_yuv)
148    else:
149        print 'Pattern not in android.sensor.availableTestPatternModes.'
150        assert 0
151
152
153def main():
154    """Test conversion of test pattern from RAW to YUV.
155
156    android.sensor.testPatternMode
157    2: COLOR_BARS
158    """
159
160    print '\nStarting %s' % NAME
161    with its.device.ItsSession() as cam:
162        props = cam.get_camera_properties()
163        its.caps.skip_unless(its.caps.raw16(props) and
164                             its.caps.manual_sensor(props) and
165                             its.caps.per_frame_control(props) and
166                             its.caps.manual_post_proc(props))
167
168        test_tonemap_curve(cam, props)
169
170if __name__ == '__main__':
171    main()
172