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