• 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(sensitivity, exp_time, linear_tonemap=False):
74    """Return a capture request with everything set to manual.
75
76    Uses identity/unit color correction, and the default tonemap curve.
77    Optionally, the tonemap can be specified as being linear.
78
79    Args:
80        sensitivity: The sensitivity value to populate the request with.
81        exp_time: The exposure time, in nanoseconds, to populate the request
82            with.
83        linear_tonemap: [Optional] whether a linear tonemap should be used
84            in this request.
85
86    Returns:
87        The default manual capture request, ready to be passed to the
88        its.device.do_capture function.
89    """
90    req = {
91        "android.control.captureIntent": 6,
92        "android.control.mode": 0,
93        "android.control.aeMode": 0,
94        "android.control.awbMode": 0,
95        "android.control.afMode": 0,
96        "android.control.effectMode": 0,
97        "android.sensor.frameDuration": 0,
98        "android.sensor.sensitivity": sensitivity,
99        "android.sensor.exposureTime": exp_time,
100        "android.colorCorrection.mode": 0,
101        "android.colorCorrection.transform":
102                int_to_rational([1,0,0, 0,1,0, 0,0,1]),
103        "android.colorCorrection.gains": [1,1,1,1],
104        "android.tonemap.mode": 1,
105        "android.shading.mode": 1
106        }
107    if linear_tonemap:
108        req["android.tonemap.mode"] = 0
109        req["android.tonemap.curveRed"] = [0.0,0.0, 1.0,1.0]
110        req["android.tonemap.curveGreen"] = [0.0,0.0, 1.0,1.0]
111        req["android.tonemap.curveBlue"] = [0.0,0.0, 1.0,1.0]
112    return req
113
114def auto_capture_request():
115    """Return a capture request with everything set to auto.
116    """
117    return {
118        "android.control.mode": 1,
119        "android.control.aeMode": 1,
120        "android.control.awbMode": 1,
121        "android.control.afMode": 1,
122        "android.colorCorrection.mode": 1,
123        "android.tonemap.mode": 1,
124        }
125
126def fastest_auto_capture_request(props):
127    """Return an auto capture request for the fastest capture.
128
129    Args:
130        props: the object returned from its.device.get_camera_properties().
131
132    Returns:
133        A capture request with everything set to auto and all filters that
134            may slow down capture set to OFF or FAST if possible
135    """
136    req = auto_capture_request()
137    turn_slow_filters_off(props, req)
138
139    return req
140
141def get_available_output_sizes(fmt, props):
142    """Return a sorted list of available output sizes for a given format.
143
144    Args:
145        fmt: the output format, as a string in ["jpg", "yuv", "raw"].
146        props: the object returned from its.device.get_camera_properties().
147
148    Returns:
149        A sorted list of (w,h) tuples (sorted large-to-small).
150    """
151    fmt_codes = {"raw":0x20, "raw10":0x25, "yuv":0x23, "jpg":0x100, "jpeg":0x100}
152    configs = props['android.scaler.streamConfigurationMap']\
153                   ['availableStreamConfigurations']
154    fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
155    out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
156    out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
157    out_sizes.sort(reverse=True)
158    return out_sizes
159
160def set_filter_off_or_fast_if_possible(props, req, available_modes, filter):
161    """Check and set controlKey to off or fast in req.
162
163    Args:
164        props: the object returned from its.device.get_camera_properties().
165        req: the input request. filter will be set to OFF or FAST if possible.
166        available_modes: the key to check available modes.
167        filter: the filter key
168
169    Returns:
170        Nothing.
171    """
172    if props.has_key(available_modes):
173        if 0 in props[available_modes]:
174            req[filter] = 0
175        elif 1 in props[available_modes]:
176            req[filter] = 1
177
178def turn_slow_filters_off(props, req):
179    """Turn filters that may slow FPS down to OFF or FAST in input request.
180
181    This function modifies the request argument, such that filters that may
182    reduce the frames-per-second throughput of the camera device will be set to
183    OFF or FAST if possible.
184
185    Args:
186        props: the object returned from its.device.get_camera_properties().
187        req: the input request.
188
189    Returns:
190        Nothing.
191    """
192    set_filter_off_or_fast_if_possible(props, req,
193        "android.noiseReduction.availableNoiseReductionModes",
194        "android.noiseReduction.mode")
195    set_filter_off_or_fast_if_possible(props, req,
196        "android.colorCorrection.availableAberrationModes",
197        "android.colorCorrection.aberrationMode")
198    set_filter_off_or_fast_if_possible(props, req,
199        "android.hotPixel.availableHotPixelModes",
200        "android.hotPixel.mode")
201    set_filter_off_or_fast_if_possible(props, req,
202        "android.edge.availableEdgeModes",
203        "android.edge.mode")
204
205def get_fastest_manual_capture_settings(props):
206    """Return a capture request and format spec for the fastest capture.
207
208    Args:
209        props: the object returned from its.device.get_camera_properties().
210
211    Returns:
212        Two values, the first is a capture request, and the second is an output
213        format specification, for the fastest possible (legal) capture that
214        can be performed on this device (with the smallest output size).
215    """
216    fmt = "yuv"
217    size = get_available_output_sizes(fmt, props)[-1]
218    out_spec = {"format":fmt, "width":size[0], "height":size[1]}
219    s = min(props['android.sensor.info.sensitivityRange'])
220    e = min(props['android.sensor.info.exposureTimeRange'])
221    req = manual_capture_request(s,e)
222
223    turn_slow_filters_off(props, req)
224
225    return req, out_spec
226
227def get_max_digital_zoom(props):
228    """Returns the maximum amount of zooming possible by the camera device.
229
230    Args:
231        props: the object returned from its.device.get_camera_properties().
232
233    Return:
234        A float indicating the maximum amount of zooming possible by the
235        camera device.
236    """
237
238    maxz = 1.0
239
240    if props.has_key("android.scaler.availableMaxDigitalZoom"):
241        maxz = props["android.scaler.availableMaxDigitalZoom"]
242
243    return maxz
244
245
246class __UnitTest(unittest.TestCase):
247    """Run a suite of unit tests on this module.
248    """
249
250    def test_int_to_rational(self):
251        """Unit test for int_to_rational.
252        """
253        self.assertEqual(int_to_rational(10),
254                         {"numerator":10,"denominator":1})
255        self.assertEqual(int_to_rational([1,2]),
256                         [{"numerator":1,"denominator":1},
257                          {"numerator":2,"denominator":1}])
258
259    def test_float_to_rational(self):
260        """Unit test for float_to_rational.
261        """
262        self.assertEqual(float_to_rational(0.5001, 64),
263                        {"numerator":32, "denominator":64})
264
265    def test_rational_to_float(self):
266        """Unit test for rational_to_float.
267        """
268        self.assertTrue(
269                abs(rational_to_float({"numerator":32,"denominator":64})-0.5)
270                < 0.0001)
271
272if __name__ == '__main__':
273    unittest.main()
274
275