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.mode": 0,
     92         "android.control.aeMode": 0,
     93         "android.control.awbMode": 0,
     94         "android.control.afMode": 0,
     95         "android.control.effectMode": 0,
     96         "android.sensor.frameDuration": 0,
     97         "android.sensor.sensitivity": sensitivity,
     98         "android.sensor.exposureTime": exp_time,
     99         "android.colorCorrection.mode": 0,
    100         "android.colorCorrection.transform":
    101                 int_to_rational([1,0,0, 0,1,0, 0,0,1]),
    102         "android.colorCorrection.gains": [1,1,1,1],
    103         "android.tonemap.mode": 1,
    104         }
    105     if linear_tonemap:
    106         req["android.tonemap.mode"] = 0
    107         req["android.tonemap.curveRed"] = [0.0,0.0, 1.0,1.0]
    108         req["android.tonemap.curveGreen"] = [0.0,0.0, 1.0,1.0]
    109         req["android.tonemap.curveBlue"] = [0.0,0.0, 1.0,1.0]
    110     return req
    111 
    112 def auto_capture_request():
    113     """Return a capture request with everything set to auto.
    114     """
    115     return {
    116         "android.control.mode": 1,
    117         "android.control.aeMode": 1,
    118         "android.control.awbMode": 1,
    119         "android.control.afMode": 1,
    120         "android.colorCorrection.mode": 1,
    121         "android.tonemap.mode": 1,
    122         }
    123 
    124 def get_available_output_sizes(fmt, props):
    125     """Return a sorted list of available output sizes for a given format.
    126 
    127     Args:
    128         fmt: the output format, as a string in ["jpg", "yuv", "raw"].
    129         props: the object returned from its.device.get_camera_properties().
    130 
    131     Returns:
    132         A sorted list of (w,h) tuples (sorted large-to-small).
    133     """
    134     fmt_codes = {"raw":0x20, "raw10":0x25, "yuv":0x23, "jpg":0x100, "jpeg":0x100}
    135     configs = props['android.scaler.streamConfigurationMap']\
    136                    ['availableStreamConfigurations']
    137     fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
    138     out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
    139     out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
    140     out_sizes.sort(reverse=True)
    141     return out_sizes
    142 
    143 def get_fastest_manual_capture_settings(props):
    144     """Return a capture request and format spec for the fastest capture.
    145 
    146     Args:
    147         props: the object returned from its.device.get_camera_properties().
    148 
    149     Returns:
    150         Two values, the first is a capture request, and the second is an output
    151         format specification, for the fastest possible (legal) capture that
    152         can be performed on this device (with the smallest output size).
    153     """
    154     fmt = "yuv"
    155     size = get_available_output_sizes(fmt, props)[-1]
    156     out_spec = {"format":fmt, "width":size[0], "height":size[1]}
    157     s = min(props['android.sensor.info.sensitivityRange'])
    158     e = min(props['android.sensor.info.exposureTimeRange'])
    159     req = manual_capture_request(s,e)
    160     return req, out_spec
    161 
    162 class __UnitTest(unittest.TestCase):
    163     """Run a suite of unit tests on this module.
    164     """
    165 
    166     def test_int_to_rational(self):
    167         """Unit test for int_to_rational.
    168         """
    169         self.assertEqual(int_to_rational(10),
    170                          {"numerator":10,"denominator":1})
    171         self.assertEqual(int_to_rational([1,2]),
    172                          [{"numerator":1,"denominator":1},
    173                           {"numerator":2,"denominator":1}])
    174 
    175     def test_float_to_rational(self):
    176         """Unit test for float_to_rational.
    177         """
    178         self.assertEqual(float_to_rational(0.5001, 64),
    179                         {"numerator":32, "denominator":64})
    180 
    181     def test_rational_to_float(self):
    182         """Unit test for rational_to_float.
    183         """
    184         self.assertTrue(
    185                 abs(rational_to_float({"numerator":32,"denominator":64})-0.5)
    186                 < 0.0001)
    187 
    188 if __name__ == '__main__':
    189     unittest.main()
    190 
    191