blob: ac384fb8b256a9ceb489152ad005b8d2e8098849 [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
Yin-Chia Yeh9afa7252015-05-08 15:05:50 -070073def manual_capture_request(
74 sensitivity, exp_time, linear_tonemap=False, props=None):
Ruben Brunk370e2432014-10-14 18:33:23 -070075 """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.
Yin-Chia Yeh9afa7252015-05-08 15:05:50 -070086 props: [Optional] the object returned from
87 its.device.get_camera_properties(). Must present when
88 linear_tonemap is True.
Ruben Brunk370e2432014-10-14 18:33:23 -070089
90 Returns:
91 The default manual capture request, ready to be passed to the
92 its.device.do_capture function.
93 """
94 req = {
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -080095 "android.control.captureIntent": 6,
Ruben Brunk370e2432014-10-14 18:33:23 -070096 "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,
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800109 "android.shading.mode": 1
Ruben Brunk370e2432014-10-14 18:33:23 -0700110 }
111 if linear_tonemap:
Yin-Chia Yeh9afa7252015-05-08 15:05:50 -0700112 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)
Ruben Brunk370e2432014-10-14 18:33:23 -0700126 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
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800140def 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
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700155def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
Ruben Brunk370e2432014-10-14 18:33:23 -0700156 """Return a sorted list of available output sizes for a given format.
157
158 Args:
Yin-Chia Yeh76dd1432015-04-27 16:42:03 -0700159 fmt: the output format, as a string in
160 ["jpg", "yuv", "raw", "raw10", "raw12"].
Ruben Brunk370e2432014-10-14 18:33:23 -0700161 props: the object returned from its.device.get_camera_properties().
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700162 max_size: (Optional) A (w,h) tuple.
163 Sizes larger than max_size (either w or h) will be discarded.
164 match_ar_size: (Optional) A (w,h) tuple.
165 Sizes not matching the aspect ratio of match_ar_size will be
166 discarded.
Ruben Brunk370e2432014-10-14 18:33:23 -0700167
168 Returns:
169 A sorted list of (w,h) tuples (sorted large-to-small).
170 """
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700171 AR_TOLERANCE = 0.03
Yin-Chia Yeh8563e4d2015-09-08 16:31:30 -0700172 fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26,"yuv":0x23,
173 "jpg":0x100, "jpeg":0x100}
Ruben Brunk370e2432014-10-14 18:33:23 -0700174 configs = props['android.scaler.streamConfigurationMap']\
175 ['availableStreamConfigurations']
176 fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
177 out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
178 out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700179 if max_size:
180 out_sizes = [s for s in out_sizes if
181 s[0] <= max_size[0] and s[1] <= max_size[1]]
182 if match_ar_size:
183 ar = match_ar_size[0] / float(match_ar_size[1])
184 out_sizes = [s for s in out_sizes if
185 abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
Ruben Brunk370e2432014-10-14 18:33:23 -0700186 out_sizes.sort(reverse=True)
187 return out_sizes
188
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800189def set_filter_off_or_fast_if_possible(props, req, available_modes, filter):
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800190 """Check and set controlKey to off or fast in req.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800191
192 Args:
193 props: the object returned from its.device.get_camera_properties().
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800194 req: the input request. filter will be set to OFF or FAST if possible.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800195 available_modes: the key to check available modes.
196 filter: the filter key
197
198 Returns:
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800199 Nothing.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800200 """
201 if props.has_key(available_modes):
202 if 0 in props[available_modes]:
203 req[filter] = 0
204 elif 1 in props[available_modes]:
205 req[filter] = 1
206
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800207def turn_slow_filters_off(props, req):
208 """Turn filters that may slow FPS down to OFF or FAST in input request.
209
210 This function modifies the request argument, such that filters that may
211 reduce the frames-per-second throughput of the camera device will be set to
212 OFF or FAST if possible.
213
214 Args:
215 props: the object returned from its.device.get_camera_properties().
216 req: the input request.
217
218 Returns:
219 Nothing.
220 """
221 set_filter_off_or_fast_if_possible(props, req,
222 "android.noiseReduction.availableNoiseReductionModes",
223 "android.noiseReduction.mode")
224 set_filter_off_or_fast_if_possible(props, req,
225 "android.colorCorrection.availableAberrationModes",
226 "android.colorCorrection.aberrationMode")
Itaru Hanada87dcd9c2015-07-28 21:36:31 +0900227 if props.has_key("android.request.availableCharacteristicsKeys"):
228 hot_pixel_modes = 393217 in props["android.request.availableCharacteristicsKeys"]
229 edge_modes = 196610 in props["android.request.availableCharacteristicsKeys"]
230 if props.has_key("android.request.availableRequestKeys"):
231 hot_pixel_mode = 393216 in props["android.request.availableRequestKeys"]
232 edge_mode = 196608 in props["android.request.availableRequestKeys"]
233 if hot_pixel_modes and hot_pixel_mode:
234 set_filter_off_or_fast_if_possible(props, req,
235 "android.hotPixel.availableHotPixelModes",
236 "android.hotPixel.mode")
237 if edge_modes and edge_mode:
238 set_filter_off_or_fast_if_possible(props, req,
239 "android.edge.availableEdgeModes",
240 "android.edge.mode")
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800241
Ruben Brunk370e2432014-10-14 18:33:23 -0700242def get_fastest_manual_capture_settings(props):
243 """Return a capture request and format spec for the fastest capture.
244
245 Args:
246 props: the object returned from its.device.get_camera_properties().
247
248 Returns:
249 Two values, the first is a capture request, and the second is an output
250 format specification, for the fastest possible (legal) capture that
251 can be performed on this device (with the smallest output size).
252 """
253 fmt = "yuv"
254 size = get_available_output_sizes(fmt, props)[-1]
255 out_spec = {"format":fmt, "width":size[0], "height":size[1]}
256 s = min(props['android.sensor.info.sensitivityRange'])
257 e = min(props['android.sensor.info.exposureTimeRange'])
258 req = manual_capture_request(s,e)
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800259
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800260 turn_slow_filters_off(props, req)
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800261
Ruben Brunk370e2432014-10-14 18:33:23 -0700262 return req, out_spec
263
Chien-Yu Chen0feea4a2014-10-20 11:04:11 -0700264def get_max_digital_zoom(props):
265 """Returns the maximum amount of zooming possible by the camera device.
266
267 Args:
268 props: the object returned from its.device.get_camera_properties().
269
270 Return:
271 A float indicating the maximum amount of zooming possible by the
272 camera device.
273 """
274
275 maxz = 1.0
276
277 if props.has_key("android.scaler.availableMaxDigitalZoom"):
278 maxz = props["android.scaler.availableMaxDigitalZoom"]
279
280 return maxz
281
282
Ruben Brunk370e2432014-10-14 18:33:23 -0700283class __UnitTest(unittest.TestCase):
284 """Run a suite of unit tests on this module.
285 """
286
287 def test_int_to_rational(self):
288 """Unit test for int_to_rational.
289 """
290 self.assertEqual(int_to_rational(10),
291 {"numerator":10,"denominator":1})
292 self.assertEqual(int_to_rational([1,2]),
293 [{"numerator":1,"denominator":1},
294 {"numerator":2,"denominator":1}])
295
296 def test_float_to_rational(self):
297 """Unit test for float_to_rational.
298 """
299 self.assertEqual(float_to_rational(0.5001, 64),
300 {"numerator":32, "denominator":64})
301
302 def test_rational_to_float(self):
303 """Unit test for rational_to_float.
304 """
305 self.assertTrue(
306 abs(rational_to_float({"numerator":32,"denominator":64})-0.5)
307 < 0.0001)
308
309if __name__ == '__main__':
310 unittest.main()
311