1# Copyright 2015 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 math
21import matplotlib
22import matplotlib.pyplot
23import numpy
24import os.path
25import pylab
26
27
28def test_edge_mode(cam, edge_mode, sensitivity, exp, fd, out_surface,
29                   reprocess_format=None):
30    """Return sharpness of the output image and the capture result metadata
31       for a capture request with the given edge mode, sensitivity, exposure
32       time, focus distance, output surface parameter, and reprocess format
33       (None for a regular request.)
34
35    Args:
36        cam: An open device session.
37        edge_mode: Edge mode for the request as defined in android.edge.mode
38        sensitivity: Sensitivity for the request as defined in
39            android.sensor.sensitivity
40        exp: Exposure time for the request as defined in
41            android.sensor.exposureTime.
42        fd: Focus distance for the request as defined in
43            android.lens.focusDistance
44        output_surface: Specifications of the output image format and size.
45        reprocess_format: (Optional) The reprocessing format. If not None,
46                reprocessing will be enabled.
47
48    Returns:
49        Object containing reported edge mode and the sharpness of the output
50        image, keyed by the following strings:
51            "edge_mode"
52            "sharpness"
53    """
54
55    NAME = os.path.basename(__file__).split(".")[0]
56
57    req = its.objects.manual_capture_request(sensitivity, exp)
58    req["android.lens.focusDistance"] = fd
59    req["android.edge.mode"] = edge_mode
60    if (reprocess_format != None):
61        req["android.reprocess.effectiveExposureFactor"] = 1.0
62    cap = cam.do_capture(req, out_surface, reprocess_format)
63
64    img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
65    its.image.write_image(img, "%s_edge=%d_reprocess_fmt_%s.jpg" %
66        (NAME, edge_mode, reprocess_format))
67    tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
68
69    ret = {}
70    ret["edge_mode"] = cap["metadata"]["android.edge.mode"]
71    ret["sharpness"] = its.image.compute_image_sharpness(tile)
72
73    return ret
74
75def main():
76    """Test that the android.edge.mode param is applied when set for
77       reprocessing requests.
78
79    Capture non-reprocess images for each edge mode and calculate their
80    sharpness as a baseline.
81
82    Capture reprocessed images for each supported reprocess format and edge_mode
83    mode. Calculate the sharpness of reprocessed images and compare them against
84    the sharpess of non-reprocess images.
85    """
86
87    THRESHOLD_RELATIVE_SHARPNESS_DIFF = 0.1
88
89    with its.device.ItsSession() as cam:
90        props = cam.get_camera_properties()
91
92        its.caps.skip_unless(its.caps.read_3a(props) and
93                             its.caps.per_frame_control(props) and
94                             its.caps.edge_mode(props, 0) and
95                             (its.caps.yuv_reprocess(props) or
96                              its.caps.private_reprocess(props)))
97
98        # If reprocessing is supported, ZSL EE mode must be avaiable.
99        assert(its.caps.edge_mode(props, 3))
100
101        reprocess_formats = []
102        if (its.caps.yuv_reprocess(props)):
103            reprocess_formats.append("yuv")
104        if (its.caps.private_reprocess(props)):
105            reprocess_formats.append("private")
106
107        size = its.objects.get_available_output_sizes("jpg", props)[0]
108        out_surface = {"width":size[0], "height":size[1], "format":"jpg"}
109
110        # Get proper sensitivity, exposure time, and focus distance.
111        s,e,_,_,fd = cam.do_3a(get_results=True)
112
113        # Get the sharpness for each edge mode for regular requests
114        sharpness_regular = []
115        edge_mode_reported_regular = []
116        for edge_mode in range(4):
117            # Skip unavailable modes
118            if not its.caps.edge_mode(props, edge_mode):
119                edge_mode_reported_regular.append(edge_mode)
120                sharpness_regular.append(0)
121                continue
122            ret = test_edge_mode(cam, edge_mode, s, e, fd, out_surface)
123            edge_mode_reported_regular.append(ret["edge_mode"])
124            sharpness_regular.append(ret["sharpness"])
125
126        print "Reported edge modes:", edge_mode_reported_regular
127        print "Sharpness with EE mode [0,1,2,3]:", sharpness_regular
128
129        # Get the sharpness for each reprocess format and edge mode for
130        # reprocess requests.
131        sharpnesses_reprocess = []
132        edge_mode_reported_reprocess = []
133
134        for reprocess_format in reprocess_formats:
135            # List of sharpness
136            sharpnesses = []
137            edge_mode_reported = []
138            for edge_mode in range(4):
139                # Skip unavailable modes
140                if not its.caps.edge_mode(props, edge_mode):
141                    edge_mode_reported.append(edge_mode)
142                    sharpnesses.append(0)
143                    continue
144
145                ret = test_edge_mode(cam, edge_mode, s, e, fd, out_surface,
146                    reprocess_format)
147                edge_mode_reported.append(ret["edge_mode"])
148                sharpnesses.append(ret["sharpness"])
149
150            sharpnesses_reprocess.append(sharpnesses)
151            edge_mode_reported_reprocess.append(edge_mode_reported)
152
153            print "Reported edge modes:", edge_mode_reported
154            print "Sharpness with EE mode [0,1,2,3] for %s reprocess:" % \
155                (reprocess_format) , sharpnesses
156
157
158        # Verify HQ(2) is sharper than OFF(0)
159        assert(sharpness_regular[2] > sharpness_regular[0])
160
161        # Verify ZSL(3) is similar to OFF(0)
162        assert(numpy.isclose(sharpness_regular[3], sharpness_regular[0],
163                             THRESHOLD_RELATIVE_SHARPNESS_DIFF))
164
165        # Verify OFF(0) is not sharper than FAST(1)
166        assert(sharpness_regular[1] >
167               sharpness_regular[0] * (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF))
168
169        # Verify FAST(1) is not sharper than HQ(2)
170        assert(sharpness_regular[2] >
171               sharpness_regular[1] * (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF))
172
173        for reprocess_format in range(len(reprocess_formats)):
174            # Verify HQ(2) is sharper than OFF(0)
175            assert(sharpnesses_reprocess[reprocess_format][2] >
176                   sharpnesses_reprocess[reprocess_format][0])
177
178            # Verify ZSL(3) is similar to OFF(0)
179            assert(numpy.isclose(sharpnesses_reprocess[reprocess_format][3],
180                                 sharpnesses_reprocess[reprocess_format][0],
181                                 THRESHOLD_RELATIVE_SHARPNESS_DIFF))
182
183            # Verify OFF(0) is not sharper than FAST(1)
184            assert(sharpnesses_reprocess[reprocess_format][1] >
185                   sharpnesses_reprocess[reprocess_format][0] *
186                   (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF))
187
188            # Verify FAST(1) is not sharper than HQ(2)
189            assert(sharpnesses_reprocess[reprocess_format][2] >
190                   sharpnesses_reprocess[reprocess_format][1] *
191                   (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF))
192
193            # Verify reprocessing HQ(2) is similar to regular HQ(2) relative to
194            # OFF(0)
195            assert(numpy.isclose(sharpnesses_reprocess[reprocess_format][2] /
196                                    sharpnesses_reprocess[reprocess_format][0],
197                                 sharpness_regular[2] / sharpness_regular[0],
198                                 THRESHOLD_RELATIVE_SHARPNESS_DIFF))
199
200if __name__ == '__main__':
201    main()
202
203