• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
16import os.path
17import sys
18import re
19import json
20import tempfile
21import time
22import unittest
23import subprocess
24import math
25
26def int_to_rational(i):
27    """Function to convert Python integers to Camera2 rationals.
28
29    Args:
30        i: Python integer or list of integers.
31
32    Returns:
33        Python dictionary or list of dictionaries representing the given int(s)
34        as rationals with denominator=1.
35    """
36    if isinstance(i, list):
37        return [{"numerator":val, "denominator":1} for val in i]
38    else:
39        return {"numerator":i, "denominator":1}
40
41def float_to_rational(f, denom=128):
42    """Function to convert Python floats to Camera2 rationals.
43
44    Args:
45        f: Python float or list of floats.
46        denom: (Optonal) the denominator to use in the output rationals.
47
48    Returns:
49        Python dictionary or list of dictionaries representing the given
50        float(s) as rationals.
51    """
52    if isinstance(f, list):
53        return [{"numerator":math.floor(val*denom+0.5), "denominator":denom}
54                for val in f]
55    else:
56        return {"numerator":math.floor(f*denom+0.5), "denominator":denom}
57
58def rational_to_float(r):
59    """Function to convert Camera2 rational objects to Python floats.
60
61    Args:
62        r: Rational or list of rationals, as Python dictionaries.
63
64    Returns:
65        Float or list of floats.
66    """
67    if isinstance(r, list):
68        return [float(val["numerator"]) / float(val["denominator"])
69                for val in r]
70    else:
71        return float(r["numerator"]) / float(r["denominator"])
72
73def manual_capture_request(
74        sensitivity, exp_time, linear_tonemap=False, props=None):
75    """Return a capture request with everything set to manual.
76
77    Uses identity/unit color correction, and the default tonemap curve.
78    Optionally, the tonemap can be specified as being linear.
79
80    Args:
81        sensitivity: The sensitivity value to populate the request with.
82        exp_time: The exposure time, in nanoseconds, to populate the request
83            with.
84        linear_tonemap: [Optional] whether a linear tonemap should be used
85            in this request.
86        props: [Optional] the object returned from
87            its.device.get_camera_properties(). Must present when
88            linear_tonemap is True.
89
90    Returns:
91        The default manual capture request, ready to be passed to the
92        its.device.do_capture function.
93    """
94    req = {
95        "android.control.captureIntent": 6,
96        "android.control.mode": 0,
97        "android.control.aeMode": 0,
98        "android.control.awbMode": 0,
99        "android.control.afMode": 0,
100        "android.control.effectMode": 0,
101        "android.sensor.frameDuration": 0,
102        "android.sensor.sensitivity": sensitivity,
103        "android.sensor.exposureTime": exp_time,
104        "android.colorCorrection.mode": 0,
105        "android.colorCorrection.transform":
106                int_to_rational([1,0,0, 0,1,0, 0,0,1]),
107        "android.colorCorrection.gains": [1,1,1,1],
108        "android.tonemap.mode": 1,
109        "android.shading.mode": 1
110        }
111    if linear_tonemap:
112        assert(props is not None)
113        #CONTRAST_CURVE mode
114        if 0 in props["android.tonemap.availableToneMapModes"]:
115            req["android.tonemap.mode"] = 0
116            req["android.tonemap.curveRed"] = [0.0,0.0, 1.0,1.0]
117            req["android.tonemap.curveGreen"] = [0.0,0.0, 1.0,1.0]
118            req["android.tonemap.curveBlue"] = [0.0,0.0, 1.0,1.0]
119        #GAMMA_VALUE mode
120        elif 3 in props["android.tonemap.availableToneMapModes"]:
121            req["android.tonemap.mode"] = 3
122            req["android.tonemap.gamma"] = 1.0
123        else:
124            print "Linear tonemap is not supported"
125            assert(False)
126    return req
127
128def auto_capture_request():
129    """Return a capture request with everything set to auto.
130    """
131    return {
132        "android.control.mode": 1,
133        "android.control.aeMode": 1,
134        "android.control.awbMode": 1,
135        "android.control.afMode": 1,
136        "android.colorCorrection.mode": 1,
137        "android.tonemap.mode": 1,
138        }
139
140def fastest_auto_capture_request(props):
141    """Return an auto capture request for the fastest capture.
142
143    Args:
144        props: the object returned from its.device.get_camera_properties().
145
146    Returns:
147        A capture request with everything set to auto and all filters that
148            may slow down capture set to OFF or FAST if possible
149    """
150    req = auto_capture_request()
151    turn_slow_filters_off(props, req)
152
153    return req
154
155def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
156    """Return a sorted list of available output sizes for a given format.
157
158    Args:
159        fmt: the output format, as a string in
160            ["jpg", "yuv", "raw", "raw10", "raw12"].
161        props: the object returned from its.device.get_camera_properties().
162        max_size: (Optional) A (w,h) tuple.
163            Sizes larger than max_size (either w or h)  will be discarded.
164        match_ar_size: (Optional) A (w,h) tuple.
165            Sizes not matching the aspect ratio of match_ar_size will be
166            discarded.
167
168    Returns:
169        A sorted list of (w,h) tuples (sorted large-to-small).
170    """
171    AR_TOLERANCE = 0.03
172    fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26,"yuv":0x23,
173                 "jpg":0x100, "jpeg":0x100}
174    configs = props['android.scaler.streamConfigurationMap']\
175                   ['availableStreamConfigurations']
176    fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
177    out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
178    out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
179    if max_size:
180        out_sizes = [s for s in out_sizes if
181                s[0] <= max_size[0] and s[1] <= max_size[1]]
182    if match_ar_size:
183        ar = match_ar_size[0] / float(match_ar_size[1])
184        out_sizes = [s for s in out_sizes if
185                abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
186    out_sizes.sort(reverse=True)
187    return out_sizes
188
189def set_filter_off_or_fast_if_possible(props, req, available_modes, filter):
190    """Check and set controlKey to off or fast in req.
191
192    Args:
193        props: the object returned from its.device.get_camera_properties().
194        req: the input request. filter will be set to OFF or FAST if possible.
195        available_modes: the key to check available modes.
196        filter: the filter key
197
198    Returns:
199        Nothing.
200    """
201    if props.has_key(available_modes):
202        if 0 in props[available_modes]:
203            req[filter] = 0
204        elif 1 in props[available_modes]:
205            req[filter] = 1
206
207def turn_slow_filters_off(props, req):
208    """Turn filters that may slow FPS down to OFF or FAST in input request.
209
210    This function modifies the request argument, such that filters that may
211    reduce the frames-per-second throughput of the camera device will be set to
212    OFF or FAST if possible.
213
214    Args:
215        props: the object returned from its.device.get_camera_properties().
216        req: the input request.
217
218    Returns:
219        Nothing.
220    """
221    set_filter_off_or_fast_if_possible(props, req,
222        "android.noiseReduction.availableNoiseReductionModes",
223        "android.noiseReduction.mode")
224    set_filter_off_or_fast_if_possible(props, req,
225        "android.colorCorrection.availableAberrationModes",
226        "android.colorCorrection.aberrationMode")
227    if props.has_key("android.request.availableCharacteristicsKeys"):
228        hot_pixel_modes = 393217 in props["android.request.availableCharacteristicsKeys"]
229        edge_modes = 196610 in props["android.request.availableCharacteristicsKeys"]
230    if props.has_key("android.request.availableRequestKeys"):
231        hot_pixel_mode = 393216 in props["android.request.availableRequestKeys"]
232        edge_mode = 196608 in props["android.request.availableRequestKeys"]
233    if hot_pixel_modes and hot_pixel_mode:
234        set_filter_off_or_fast_if_possible(props, req,
235            "android.hotPixel.availableHotPixelModes",
236            "android.hotPixel.mode")
237    if edge_modes and edge_mode:
238        set_filter_off_or_fast_if_possible(props, req,
239            "android.edge.availableEdgeModes",
240            "android.edge.mode")
241
242def get_fastest_manual_capture_settings(props):
243    """Return a capture request and format spec for the fastest capture.
244
245    Args:
246        props: the object returned from its.device.get_camera_properties().
247
248    Returns:
249        Two values, the first is a capture request, and the second is an output
250        format specification, for the fastest possible (legal) capture that
251        can be performed on this device (with the smallest output size).
252    """
253    fmt = "yuv"
254    size = get_available_output_sizes(fmt, props)[-1]
255    out_spec = {"format":fmt, "width":size[0], "height":size[1]}
256    s = min(props['android.sensor.info.sensitivityRange'])
257    e = min(props['android.sensor.info.exposureTimeRange'])
258    req = manual_capture_request(s,e)
259
260    turn_slow_filters_off(props, req)
261
262    return req, out_spec
263
264def get_max_digital_zoom(props):
265    """Returns the maximum amount of zooming possible by the camera device.
266
267    Args:
268        props: the object returned from its.device.get_camera_properties().
269
270    Return:
271        A float indicating the maximum amount of zooming possible by the
272        camera device.
273    """
274
275    maxz = 1.0
276
277    if props.has_key("android.scaler.availableMaxDigitalZoom"):
278        maxz = props["android.scaler.availableMaxDigitalZoom"]
279
280    return maxz
281
282
283class __UnitTest(unittest.TestCase):
284    """Run a suite of unit tests on this module.
285    """
286
287    def test_int_to_rational(self):
288        """Unit test for int_to_rational.
289        """
290        self.assertEqual(int_to_rational(10),
291                         {"numerator":10,"denominator":1})
292        self.assertEqual(int_to_rational([1,2]),
293                         [{"numerator":1,"denominator":1},
294                          {"numerator":2,"denominator":1}])
295
296    def test_float_to_rational(self):
297        """Unit test for float_to_rational.
298        """
299        self.assertEqual(float_to_rational(0.5001, 64),
300                        {"numerator":32, "denominator":64})
301
302    def test_rational_to_float(self):
303        """Unit test for rational_to_float.
304        """
305        self.assertTrue(
306                abs(rational_to_float({"numerator":32,"denominator":64})-0.5)
307                < 0.0001)
308
309if __name__ == '__main__':
310    unittest.main()
311
312