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(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 
    114 def 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 
    126 def 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 
    141 def 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 
    160 def 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 
    178 def 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 
    205 def 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 
    227 def 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 
    246 class __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 
    272 if __name__ == '__main__':
    273     unittest.main()
    274 
    275