blob: 5967599bbbee68550616d745a4bd070135846d2a [file] [log] [blame]
Ruben Brunk370e2432014-10-14 18:33:23 -07001# 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(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 = {
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -080091 "android.control.captureIntent": 6,
Ruben Brunk370e2432014-10-14 18:33:23 -070092 "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,
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800105 "android.shading.mode": 1
Ruben Brunk370e2432014-10-14 18:33:23 -0700106 }
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
114def 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
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800126def 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
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700141def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
Ruben Brunk370e2432014-10-14 18:33:23 -0700142 """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().
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700147 max_size: (Optional) A (w,h) tuple.
148 Sizes larger than max_size (either w or h) will be discarded.
149 match_ar_size: (Optional) A (w,h) tuple.
150 Sizes not matching the aspect ratio of match_ar_size will be
151 discarded.
Ruben Brunk370e2432014-10-14 18:33:23 -0700152
153 Returns:
154 A sorted list of (w,h) tuples (sorted large-to-small).
155 """
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700156 AR_TOLERANCE = 0.03
Ruben Brunk370e2432014-10-14 18:33:23 -0700157 fmt_codes = {"raw":0x20, "raw10":0x25, "yuv":0x23, "jpg":0x100, "jpeg":0x100}
158 configs = props['android.scaler.streamConfigurationMap']\
159 ['availableStreamConfigurations']
160 fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
161 out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
162 out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700163 if max_size:
164 out_sizes = [s for s in out_sizes if
165 s[0] <= max_size[0] and s[1] <= max_size[1]]
166 if match_ar_size:
167 ar = match_ar_size[0] / float(match_ar_size[1])
168 out_sizes = [s for s in out_sizes if
169 abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
Ruben Brunk370e2432014-10-14 18:33:23 -0700170 out_sizes.sort(reverse=True)
171 return out_sizes
172
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800173def set_filter_off_or_fast_if_possible(props, req, available_modes, filter):
Unsuk Jungff9926e2015-09-12 19:50:47 +0000174 """Check and set controlKey to off or fast in req.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800175
176 Args:
177 props: the object returned from its.device.get_camera_properties().
Unsuk Jungff9926e2015-09-12 19:50:47 +0000178 req: the input request. filter will be set to OFF or FAST if possible.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800179 available_modes: the key to check available modes.
180 filter: the filter key
181
182 Returns:
Unsuk Jungff9926e2015-09-12 19:50:47 +0000183 Nothing.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800184 """
185 if props.has_key(available_modes):
186 if 0 in props[available_modes]:
187 req[filter] = 0
188 elif 1 in props[available_modes]:
189 req[filter] = 1
190
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800191def turn_slow_filters_off(props, req):
192 """Turn filters that may slow FPS down to OFF or FAST in input request.
193
194 This function modifies the request argument, such that filters that may
195 reduce the frames-per-second throughput of the camera device will be set to
196 OFF or FAST if possible.
197
198 Args:
199 props: the object returned from its.device.get_camera_properties().
200 req: the input request.
201
202 Returns:
203 Nothing.
204 """
205 set_filter_off_or_fast_if_possible(props, req,
206 "android.noiseReduction.availableNoiseReductionModes",
207 "android.noiseReduction.mode")
208 set_filter_off_or_fast_if_possible(props, req,
209 "android.colorCorrection.availableAberrationModes",
210 "android.colorCorrection.aberrationMode")
Itaru Hanada87dcd9c2015-07-28 21:36:31 +0900211 if props.has_key("android.request.availableCharacteristicsKeys"):
212 hot_pixel_modes = 393217 in props["android.request.availableCharacteristicsKeys"]
213 edge_modes = 196610 in props["android.request.availableCharacteristicsKeys"]
214 if props.has_key("android.request.availableRequestKeys"):
215 hot_pixel_mode = 393216 in props["android.request.availableRequestKeys"]
216 edge_mode = 196608 in props["android.request.availableRequestKeys"]
217 if hot_pixel_modes and hot_pixel_mode:
218 set_filter_off_or_fast_if_possible(props, req,
219 "android.hotPixel.availableHotPixelModes",
220 "android.hotPixel.mode")
221 if edge_modes and edge_mode:
222 set_filter_off_or_fast_if_possible(props, req,
223 "android.edge.availableEdgeModes",
224 "android.edge.mode")
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800225
Ruben Brunk370e2432014-10-14 18:33:23 -0700226def get_fastest_manual_capture_settings(props):
227 """Return a capture request and format spec for the fastest capture.
228
229 Args:
230 props: the object returned from its.device.get_camera_properties().
231
232 Returns:
233 Two values, the first is a capture request, and the second is an output
234 format specification, for the fastest possible (legal) capture that
235 can be performed on this device (with the smallest output size).
236 """
237 fmt = "yuv"
238 size = get_available_output_sizes(fmt, props)[-1]
239 out_spec = {"format":fmt, "width":size[0], "height":size[1]}
240 s = min(props['android.sensor.info.sensitivityRange'])
241 e = min(props['android.sensor.info.exposureTimeRange'])
242 req = manual_capture_request(s,e)
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800243
Unsuk Jungff9926e2015-09-12 19:50:47 +0000244 turn_slow_filters_off(props, req)
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800245
Ruben Brunk370e2432014-10-14 18:33:23 -0700246 return req, out_spec
247
Chien-Yu Chen0feea4a2014-10-20 11:04:11 -0700248def get_max_digital_zoom(props):
249 """Returns the maximum amount of zooming possible by the camera device.
250
251 Args:
252 props: the object returned from its.device.get_camera_properties().
253
254 Return:
255 A float indicating the maximum amount of zooming possible by the
256 camera device.
257 """
258
259 maxz = 1.0
260
261 if props.has_key("android.scaler.availableMaxDigitalZoom"):
262 maxz = props["android.scaler.availableMaxDigitalZoom"]
263
264 return maxz
265
266
Ruben Brunk370e2432014-10-14 18:33:23 -0700267class __UnitTest(unittest.TestCase):
268 """Run a suite of unit tests on this module.
269 """
270
271 def test_int_to_rational(self):
272 """Unit test for int_to_rational.
273 """
274 self.assertEqual(int_to_rational(10),
275 {"numerator":10,"denominator":1})
276 self.assertEqual(int_to_rational([1,2]),
277 [{"numerator":1,"denominator":1},
278 {"numerator":2,"denominator":1}])
279
280 def test_float_to_rational(self):
281 """Unit test for float_to_rational.
282 """
283 self.assertEqual(float_to_rational(0.5001, 64),
284 {"numerator":32, "denominator":64})
285
286 def test_rational_to_float(self):
287 """Unit test for rational_to_float.
288 """
289 self.assertTrue(
290 abs(rational_to_float({"numerator":32,"denominator":64})-0.5)
291 < 0.0001)
292
293if __name__ == '__main__':
294 unittest.main()
295