Home | History | Annotate | Download | only in its
      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 
     15 import os
     16 import os.path
     17 import sys
     18 import re
     19 import json
     20 import tempfile
     21 import time
     22 import unittest
     23 import subprocess
     24 import math
     25 
     26 def 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 
     41 def 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 
     58 def 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 
     73 def 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 
    128 def 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 
    140 def 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 
    155 def get_available_output_sizes(fmt, props):
    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 
    163     Returns:
    164         A sorted list of (w,h) tuples (sorted large-to-small).
    165     """
    166     fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26, "yuv":0x23,
    167                  "jpg":0x100, "jpeg":0x100}
    168     configs = props['android.scaler.streamConfigurationMap']\
    169                    ['availableStreamConfigurations']
    170     fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
    171     out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
    172     out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
    173     out_sizes.sort(reverse=True)
    174     return out_sizes
    175 
    176 def set_filter_off_or_fast_if_possible(props, req, available_modes, filter):
    177     """Check and set controlKey to off or fast in req.
    178 
    179     Args:
    180         props: the object returned from its.device.get_camera_properties().
    181         req: the input request. filter will be set to OFF or FAST if possible.
    182         available_modes: the key to check available modes.
    183         filter: the filter key
    184 
    185     Returns:
    186         Nothing.
    187     """
    188     if props.has_key(available_modes):
    189         if 0 in props[available_modes]:
    190             req[filter] = 0
    191         elif 1 in props[available_modes]:
    192             req[filter] = 1
    193 
    194 def turn_slow_filters_off(props, req):
    195     """Turn filters that may slow FPS down to OFF or FAST in input request.
    196 
    197     This function modifies the request argument, such that filters that may
    198     reduce the frames-per-second throughput of the camera device will be set to
    199     OFF or FAST if possible.
    200 
    201     Args:
    202         props: the object returned from its.device.get_camera_properties().
    203         req: the input request.
    204 
    205     Returns:
    206         Nothing.
    207     """
    208     set_filter_off_or_fast_if_possible(props, req,
    209         "android.noiseReduction.availableNoiseReductionModes",
    210         "android.noiseReduction.mode")
    211     set_filter_off_or_fast_if_possible(props, req,
    212         "android.colorCorrection.availableAberrationModes",
    213         "android.colorCorrection.aberrationMode")
    214     if props.has_key("android.request.availableCharacteristicsKeys"):
    215         hot_pixel_modes = 393217 in props["android.request.availableCharacteristicsKeys"]
    216         edge_modes = 196610 in props["android.request.availableCharacteristicsKeys"]
    217     if props.has_key("android.request.availableRequestKeys"):
    218         hot_pixel_mode = 393216 in props["android.request.availableRequestKeys"]
    219         edge_mode = 196608 in props["android.request.availableRequestKeys"]
    220     if hot_pixel_modes and hot_pixel_mode:
    221         set_filter_off_or_fast_if_possible(props, req,
    222             "android.hotPixel.availableHotPixelModes",
    223             "android.hotPixel.mode")
    224     if edge_modes and edge_mode:
    225         set_filter_off_or_fast_if_possible(props, req,
    226             "android.edge.availableEdgeModes",
    227             "android.edge.mode")
    228 
    229 def get_fastest_manual_capture_settings(props):
    230     """Return a capture request and format spec for the fastest capture.
    231 
    232     Args:
    233         props: the object returned from its.device.get_camera_properties().
    234 
    235     Returns:
    236         Two values, the first is a capture request, and the second is an output
    237         format specification, for the fastest possible (legal) capture that
    238         can be performed on this device (with the smallest output size).
    239     """
    240     fmt = "yuv"
    241     size = get_available_output_sizes(fmt, props)[-1]
    242     out_spec = {"format":fmt, "width":size[0], "height":size[1]}
    243     s = min(props['android.sensor.info.sensitivityRange'])
    244     e = min(props['android.sensor.info.exposureTimeRange'])
    245     req = manual_capture_request(s,e)
    246 
    247     turn_slow_filters_off(props, req)
    248 
    249     return req, out_spec
    250 
    251 def get_max_digital_zoom(props):
    252     """Returns the maximum amount of zooming possible by the camera device.
    253 
    254     Args:
    255         props: the object returned from its.device.get_camera_properties().
    256 
    257     Return:
    258         A float indicating the maximum amount of zooming possible by the
    259         camera device.
    260     """
    261 
    262     maxz = 1.0
    263 
    264     if props.has_key("android.scaler.availableMaxDigitalZoom"):
    265         maxz = props["android.scaler.availableMaxDigitalZoom"]
    266 
    267     return maxz
    268 
    269 
    270 class __UnitTest(unittest.TestCase):
    271     """Run a suite of unit tests on this module.
    272     """
    273 
    274     def test_int_to_rational(self):
    275         """Unit test for int_to_rational.
    276         """
    277         self.assertEqual(int_to_rational(10),
    278                          {"numerator":10,"denominator":1})
    279         self.assertEqual(int_to_rational([1,2]),
    280                          [{"numerator":1,"denominator":1},
    281                           {"numerator":2,"denominator":1}])
    282 
    283     def test_float_to_rational(self):
    284         """Unit test for float_to_rational.
    285         """
    286         self.assertEqual(float_to_rational(0.5001, 64),
    287                         {"numerator":32, "denominator":64})
    288 
    289     def test_rational_to_float(self):
    290         """Unit test for rational_to_float.
    291         """
    292         self.assertTrue(
    293                 abs(rational_to_float({"numerator":32,"denominator":64})-0.5)
    294                 < 0.0001)
    295 
    296 if __name__ == '__main__':
    297     unittest.main()
    298 
    299