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 15import os 16import os.path 17import sys 18import re 19import json 20import tempfile 21import time 22import unittest 23import subprocess 24import math 25 26def 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 41def 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 58def 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 73def 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 128def 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 140def 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 155def 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 176def 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 194def 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 229def 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 251def 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 270class __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 296if __name__ == '__main__': 297 unittest.main() 298 299