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