• 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, f_distance = 0.0, 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        f_distance: The focus distance to populate the request with.
85        linear_tonemap: [Optional] whether a linear tonemap should be used
86            in this request.
87        props: [Optional] the object returned from
88            its.device.get_camera_properties(). Must present when
89            linear_tonemap is True.
90
91    Returns:
92        The default manual capture request, ready to be passed to the
93        its.device.do_capture function.
94    """
95    req = {
96        "android.control.captureIntent": 6,
97        "android.control.mode": 0,
98        "android.control.aeMode": 0,
99        "android.control.awbMode": 0,
100        "android.control.afMode": 0,
101        "android.control.effectMode": 0,
102        "android.sensor.frameDuration": 0,
103        "android.sensor.sensitivity": sensitivity,
104        "android.sensor.exposureTime": exp_time,
105        "android.colorCorrection.mode": 0,
106        "android.colorCorrection.transform":
107                int_to_rational([1,0,0, 0,1,0, 0,0,1]),
108        "android.colorCorrection.gains": [1,1,1,1],
109        "android.lens.focusDistance" : f_distance,
110        "android.tonemap.mode": 1,
111        "android.shading.mode": 1
112        }
113    if linear_tonemap:
114        assert(props is not None)
115        #CONTRAST_CURVE mode
116        if 0 in props["android.tonemap.availableToneMapModes"]:
117            req["android.tonemap.mode"] = 0
118            req["android.tonemap.curveRed"] = [0.0,0.0, 1.0,1.0]
119            req["android.tonemap.curveGreen"] = [0.0,0.0, 1.0,1.0]
120            req["android.tonemap.curveBlue"] = [0.0,0.0, 1.0,1.0]
121        #GAMMA_VALUE mode
122        elif 3 in props["android.tonemap.availableToneMapModes"]:
123            req["android.tonemap.mode"] = 3
124            req["android.tonemap.gamma"] = 1.0
125        else:
126            print "Linear tonemap is not supported"
127            assert(False)
128    return req
129
130def auto_capture_request():
131    """Return a capture request with everything set to auto.
132    """
133    return {
134        "android.control.mode": 1,
135        "android.control.aeMode": 1,
136        "android.control.awbMode": 1,
137        "android.control.afMode": 1,
138        "android.colorCorrection.mode": 1,
139        "android.tonemap.mode": 1,
140        }
141
142def fastest_auto_capture_request(props):
143    """Return an auto capture request for the fastest capture.
144
145    Args:
146        props: the object returned from its.device.get_camera_properties().
147
148    Returns:
149        A capture request with everything set to auto and all filters that
150            may slow down capture set to OFF or FAST if possible
151    """
152    req = auto_capture_request()
153    turn_slow_filters_off(props, req)
154
155    return req
156
157def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
158    """Return a sorted list of available output sizes for a given format.
159
160    Args:
161        fmt: the output format, as a string in
162            ["jpg", "yuv", "raw", "raw10", "raw12"].
163        props: the object returned from its.device.get_camera_properties().
164        max_size: (Optional) A (w,h) tuple.
165            Sizes larger than max_size (either w or h)  will be discarded.
166        match_ar_size: (Optional) A (w,h) tuple.
167            Sizes not matching the aspect ratio of match_ar_size will be
168            discarded.
169
170    Returns:
171        A sorted list of (w,h) tuples (sorted large-to-small).
172    """
173    AR_TOLERANCE = 0.03
174    fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26,"yuv":0x23,
175                 "jpg":0x100, "jpeg":0x100}
176    configs = props['android.scaler.streamConfigurationMap']\
177                   ['availableStreamConfigurations']
178    fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
179    out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
180    out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
181    if max_size:
182        out_sizes = [s for s in out_sizes if
183                s[0] <= max_size[0] and s[1] <= max_size[1]]
184    if match_ar_size:
185        ar = match_ar_size[0] / float(match_ar_size[1])
186        out_sizes = [s for s in out_sizes if
187                abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
188    out_sizes.sort(reverse=True, key=lambda s: s[0]) # 1st pass, sort by width
189    out_sizes.sort(reverse=True, key=lambda s: s[0]*s[1]) # sort by area
190    return out_sizes
191
192def set_filter_off_or_fast_if_possible(props, req, available_modes, filter):
193    """Check and set controlKey to off or fast in req.
194
195    Args:
196        props: the object returned from its.device.get_camera_properties().
197        req: the input request. filter will be set to OFF or FAST if possible.
198        available_modes: the key to check available modes.
199        filter: the filter key
200
201    Returns:
202        Nothing.
203    """
204    if props.has_key(available_modes):
205        if 0 in props[available_modes]:
206            req[filter] = 0
207        elif 1 in props[available_modes]:
208            req[filter] = 1
209
210def turn_slow_filters_off(props, req):
211    """Turn filters that may slow FPS down to OFF or FAST in input request.
212
213    This function modifies the request argument, such that filters that may
214    reduce the frames-per-second throughput of the camera device will be set to
215    OFF or FAST if possible.
216
217    Args:
218        props: the object returned from its.device.get_camera_properties().
219        req: the input request.
220
221    Returns:
222        Nothing.
223    """
224    set_filter_off_or_fast_if_possible(props, req,
225        "android.noiseReduction.availableNoiseReductionModes",
226        "android.noiseReduction.mode")
227    set_filter_off_or_fast_if_possible(props, req,
228        "android.colorCorrection.availableAberrationModes",
229        "android.colorCorrection.aberrationMode")
230    if props.has_key("android.request.availableCharacteristicsKeys"):
231        hot_pixel_modes = 393217 in props["android.request.availableCharacteristicsKeys"]
232        edge_modes = 196610 in props["android.request.availableCharacteristicsKeys"]
233    if props.has_key("android.request.availableRequestKeys"):
234        hot_pixel_mode = 393216 in props["android.request.availableRequestKeys"]
235        edge_mode = 196608 in props["android.request.availableRequestKeys"]
236    if hot_pixel_modes and hot_pixel_mode:
237        set_filter_off_or_fast_if_possible(props, req,
238            "android.hotPixel.availableHotPixelModes",
239            "android.hotPixel.mode")
240    if edge_modes and edge_mode:
241        set_filter_off_or_fast_if_possible(props, req,
242            "android.edge.availableEdgeModes",
243            "android.edge.mode")
244
245def get_fastest_manual_capture_settings(props):
246    """Return a capture request and format spec for the fastest capture.
247
248    Args:
249        props: the object returned from its.device.get_camera_properties().
250
251    Returns:
252        Two values, the first is a capture request, and the second is an output
253        format specification, for the fastest possible (legal) capture that
254        can be performed on this device (with the smallest output size).
255    """
256    fmt = "yuv"
257    size = get_available_output_sizes(fmt, props)[-1]
258    out_spec = {"format":fmt, "width":size[0], "height":size[1]}
259    s = min(props['android.sensor.info.sensitivityRange'])
260    e = min(props['android.sensor.info.exposureTimeRange'])
261    req = manual_capture_request(s,e)
262
263    turn_slow_filters_off(props, req)
264
265    return req, out_spec
266
267
268def get_smallest_yuv_format(props):
269    """Return a capture request and format spec for the smallest yuv size.
270
271    Args:
272        props: the object returned from its.device.get_camera_properties().
273
274    Returns:
275        fmt:    an output format specification, for the smallest possible yuv
276        format for this device.
277    """
278    size = get_available_output_sizes("yuv", props)[-1]
279    fmt = {"format":"yuv", "width":size[0], "height":size[1]}
280
281    return fmt
282
283
284def get_largest_yuv_format(props):
285    """Return a capture request and format spec for the smallest yuv size.
286
287    Args:
288        props: the object returned from its.device.get_camera_properties().
289
290    Returns:
291        fmt:    an output format specification, for the smallest possible yuv
292        format for this device.
293    """
294    size = get_available_output_sizes("yuv", props)[0]
295    fmt = {"format":"yuv", "width":size[0], "height":size[1]}
296
297    return fmt
298
299
300def get_max_digital_zoom(props):
301    """Returns the maximum amount of zooming possible by the camera device.
302
303    Args:
304        props: the object returned from its.device.get_camera_properties().
305
306    Return:
307        A float indicating the maximum amount of zooming possible by the
308        camera device.
309    """
310
311    maxz = 1.0
312
313    if props.has_key("android.scaler.availableMaxDigitalZoom"):
314        maxz = props["android.scaler.availableMaxDigitalZoom"]
315
316    return maxz
317
318
319class __UnitTest(unittest.TestCase):
320    """Run a suite of unit tests on this module.
321    """
322
323    def test_int_to_rational(self):
324        """Unit test for int_to_rational.
325        """
326        self.assertEqual(int_to_rational(10),
327                         {"numerator":10,"denominator":1})
328        self.assertEqual(int_to_rational([1,2]),
329                         [{"numerator":1,"denominator":1},
330                          {"numerator":2,"denominator":1}])
331
332    def test_float_to_rational(self):
333        """Unit test for float_to_rational.
334        """
335        self.assertEqual(float_to_rational(0.5001, 64),
336                        {"numerator":32, "denominator":64})
337
338    def test_rational_to_float(self):
339        """Unit test for rational_to_float.
340        """
341        self.assertTrue(
342                abs(rational_to_float({"numerator":32,"denominator":64})-0.5)
343                < 0.0001)
344
345if __name__ == '__main__':
346    unittest.main()
347
348