blob: a8b1535504402687e1984057d75fc8de8b8b1391 [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(
Shuzhen Wang18b330d2016-06-27 11:49:38 -070074 sensitivity, exp_time, f_distance = 0.0, 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.
Shuzhen Wang18b330d2016-06-27 11:49:38 -070084 f_distance: The focus distance to populate the request with.
Ruben Brunk370e2432014-10-14 18:33:23 -070085 linear_tonemap: [Optional] whether a linear tonemap should be used
86 in this request.
Yin-Chia Yeh9afa7252015-05-08 15:05:50 -070087 props: [Optional] the object returned from
88 its.device.get_camera_properties(). Must present when
89 linear_tonemap is True.
Ruben Brunk370e2432014-10-14 18:33:23 -070090
91 Returns:
92 The default manual capture request, ready to be passed to the
93 its.device.do_capture function.
94 """
95 req = {
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -080096 "android.control.captureIntent": 6,
Ruben Brunk370e2432014-10-14 18:33:23 -070097 "android.control.mode": 0,
98 "android.control.aeMode": 0,
99 "android.control.awbMode": 0,
100 "android.control.afMode": 0,
101 "android.control.effectMode": 0,
102 "android.sensor.frameDuration": 0,
103 "android.sensor.sensitivity": sensitivity,
104 "android.sensor.exposureTime": exp_time,
105 "android.colorCorrection.mode": 0,
106 "android.colorCorrection.transform":
107 int_to_rational([1,0,0, 0,1,0, 0,0,1]),
108 "android.colorCorrection.gains": [1,1,1,1],
Shuzhen Wang18b330d2016-06-27 11:49:38 -0700109 "android.lens.focusDistance" : f_distance,
Ruben Brunk370e2432014-10-14 18:33:23 -0700110 "android.tonemap.mode": 1,
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800111 "android.shading.mode": 1
Ruben Brunk370e2432014-10-14 18:33:23 -0700112 }
113 if linear_tonemap:
Yin-Chia Yeh9afa7252015-05-08 15:05:50 -0700114 assert(props is not None)
115 #CONTRAST_CURVE mode
116 if 0 in props["android.tonemap.availableToneMapModes"]:
117 req["android.tonemap.mode"] = 0
118 req["android.tonemap.curveRed"] = [0.0,0.0, 1.0,1.0]
119 req["android.tonemap.curveGreen"] = [0.0,0.0, 1.0,1.0]
120 req["android.tonemap.curveBlue"] = [0.0,0.0, 1.0,1.0]
121 #GAMMA_VALUE mode
122 elif 3 in props["android.tonemap.availableToneMapModes"]:
123 req["android.tonemap.mode"] = 3
124 req["android.tonemap.gamma"] = 1.0
125 else:
126 print "Linear tonemap is not supported"
127 assert(False)
Ruben Brunk370e2432014-10-14 18:33:23 -0700128 return req
129
130def auto_capture_request():
131 """Return a capture request with everything set to auto.
132 """
133 return {
134 "android.control.mode": 1,
135 "android.control.aeMode": 1,
136 "android.control.awbMode": 1,
137 "android.control.afMode": 1,
138 "android.colorCorrection.mode": 1,
139 "android.tonemap.mode": 1,
140 }
141
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800142def fastest_auto_capture_request(props):
143 """Return an auto capture request for the fastest capture.
144
145 Args:
146 props: the object returned from its.device.get_camera_properties().
147
148 Returns:
149 A capture request with everything set to auto and all filters that
150 may slow down capture set to OFF or FAST if possible
151 """
152 req = auto_capture_request()
153 turn_slow_filters_off(props, req)
154
155 return req
156
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700157def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
Ruben Brunk370e2432014-10-14 18:33:23 -0700158 """Return a sorted list of available output sizes for a given format.
159
160 Args:
Yin-Chia Yeh76dd1432015-04-27 16:42:03 -0700161 fmt: the output format, as a string in
162 ["jpg", "yuv", "raw", "raw10", "raw12"].
Ruben Brunk370e2432014-10-14 18:33:23 -0700163 props: the object returned from its.device.get_camera_properties().
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700164 max_size: (Optional) A (w,h) tuple.
165 Sizes larger than max_size (either w or h) will be discarded.
166 match_ar_size: (Optional) A (w,h) tuple.
167 Sizes not matching the aspect ratio of match_ar_size will be
168 discarded.
Ruben Brunk370e2432014-10-14 18:33:23 -0700169
170 Returns:
171 A sorted list of (w,h) tuples (sorted large-to-small).
172 """
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700173 AR_TOLERANCE = 0.03
Yin-Chia Yeh8563e4d2015-09-08 16:31:30 -0700174 fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26,"yuv":0x23,
175 "jpg":0x100, "jpeg":0x100}
Ruben Brunk370e2432014-10-14 18:33:23 -0700176 configs = props['android.scaler.streamConfigurationMap']\
177 ['availableStreamConfigurations']
178 fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
179 out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
180 out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700181 if max_size:
182 out_sizes = [s for s in out_sizes if
183 s[0] <= max_size[0] and s[1] <= max_size[1]]
184 if match_ar_size:
185 ar = match_ar_size[0] / float(match_ar_size[1])
186 out_sizes = [s for s in out_sizes if
187 abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
Yin-Chia Yeh4a137512016-08-10 00:02:12 -0700188 out_sizes.sort(reverse=True, key=lambda s: s[0]) # 1st pass, sort by width
189 out_sizes.sort(reverse=True, key=lambda s: s[0]*s[1]) # sort by area
Ruben Brunk370e2432014-10-14 18:33:23 -0700190 return out_sizes
191
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800192def set_filter_off_or_fast_if_possible(props, req, available_modes, filter):
Unsuk Jungff9926e2015-09-12 19:50:47 +0000193 """Check and set controlKey to off or fast in req.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800194
195 Args:
196 props: the object returned from its.device.get_camera_properties().
Unsuk Jungff9926e2015-09-12 19:50:47 +0000197 req: the input request. filter will be set to OFF or FAST if possible.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800198 available_modes: the key to check available modes.
199 filter: the filter key
200
201 Returns:
Unsuk Jungff9926e2015-09-12 19:50:47 +0000202 Nothing.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800203 """
204 if props.has_key(available_modes):
205 if 0 in props[available_modes]:
206 req[filter] = 0
207 elif 1 in props[available_modes]:
208 req[filter] = 1
209
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800210def turn_slow_filters_off(props, req):
211 """Turn filters that may slow FPS down to OFF or FAST in input request.
212
213 This function modifies the request argument, such that filters that may
214 reduce the frames-per-second throughput of the camera device will be set to
215 OFF or FAST if possible.
216
217 Args:
218 props: the object returned from its.device.get_camera_properties().
219 req: the input request.
220
221 Returns:
222 Nothing.
223 """
224 set_filter_off_or_fast_if_possible(props, req,
225 "android.noiseReduction.availableNoiseReductionModes",
226 "android.noiseReduction.mode")
227 set_filter_off_or_fast_if_possible(props, req,
228 "android.colorCorrection.availableAberrationModes",
229 "android.colorCorrection.aberrationMode")
Itaru Hanada87dcd9c2015-07-28 21:36:31 +0900230 if props.has_key("android.request.availableCharacteristicsKeys"):
231 hot_pixel_modes = 393217 in props["android.request.availableCharacteristicsKeys"]
232 edge_modes = 196610 in props["android.request.availableCharacteristicsKeys"]
233 if props.has_key("android.request.availableRequestKeys"):
234 hot_pixel_mode = 393216 in props["android.request.availableRequestKeys"]
235 edge_mode = 196608 in props["android.request.availableRequestKeys"]
236 if hot_pixel_modes and hot_pixel_mode:
237 set_filter_off_or_fast_if_possible(props, req,
238 "android.hotPixel.availableHotPixelModes",
239 "android.hotPixel.mode")
240 if edge_modes and edge_mode:
241 set_filter_off_or_fast_if_possible(props, req,
242 "android.edge.availableEdgeModes",
243 "android.edge.mode")
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800244
Ruben Brunk370e2432014-10-14 18:33:23 -0700245def get_fastest_manual_capture_settings(props):
246 """Return a capture request and format spec for the fastest capture.
247
248 Args:
249 props: the object returned from its.device.get_camera_properties().
250
251 Returns:
252 Two values, the first is a capture request, and the second is an output
253 format specification, for the fastest possible (legal) capture that
254 can be performed on this device (with the smallest output size).
255 """
256 fmt = "yuv"
257 size = get_available_output_sizes(fmt, props)[-1]
258 out_spec = {"format":fmt, "width":size[0], "height":size[1]}
259 s = min(props['android.sensor.info.sensitivityRange'])
260 e = min(props['android.sensor.info.exposureTimeRange'])
261 req = manual_capture_request(s,e)
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800262
Unsuk Jungff9926e2015-09-12 19:50:47 +0000263 turn_slow_filters_off(props, req)
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800264
Ruben Brunk370e2432014-10-14 18:33:23 -0700265 return req, out_spec
266
Clemenz Portmann210b89e2017-01-30 13:46:22 -0800267
268def get_smallest_yuv_format(props):
269 """Return a capture request and format spec for the smallest yuv size.
270
271 Args:
272 props: the object returned from its.device.get_camera_properties().
273
274 Returns:
275 fmt: an output format specification, for the smallest possible yuv
276 format for this device.
277 """
278 size = get_available_output_sizes("yuv", props)[-1]
279 fmt = {"format":"yuv", "width":size[0], "height":size[1]}
280
281 return fmt
282
283
284def get_largest_yuv_format(props):
285 """Return a capture request and format spec for the smallest yuv size.
286
287 Args:
288 props: the object returned from its.device.get_camera_properties().
289
290 Returns:
291 fmt: an output format specification, for the smallest possible yuv
292 format for this device.
293 """
294 size = get_available_output_sizes("yuv", props)[0]
295 fmt = {"format":"yuv", "width":size[0], "height":size[1]}
296
297 return fmt
298
299
Chien-Yu Chen0feea4a2014-10-20 11:04:11 -0700300def get_max_digital_zoom(props):
301 """Returns the maximum amount of zooming possible by the camera device.
302
303 Args:
304 props: the object returned from its.device.get_camera_properties().
305
306 Return:
307 A float indicating the maximum amount of zooming possible by the
308 camera device.
309 """
310
311 maxz = 1.0
312
313 if props.has_key("android.scaler.availableMaxDigitalZoom"):
314 maxz = props["android.scaler.availableMaxDigitalZoom"]
315
316 return maxz
317
318
Ruben Brunk370e2432014-10-14 18:33:23 -0700319class __UnitTest(unittest.TestCase):
320 """Run a suite of unit tests on this module.
321 """
322
323 def test_int_to_rational(self):
324 """Unit test for int_to_rational.
325 """
326 self.assertEqual(int_to_rational(10),
327 {"numerator":10,"denominator":1})
328 self.assertEqual(int_to_rational([1,2]),
329 [{"numerator":1,"denominator":1},
330 {"numerator":2,"denominator":1}])
331
332 def test_float_to_rational(self):
333 """Unit test for float_to_rational.
334 """
335 self.assertEqual(float_to_rational(0.5001, 64),
336 {"numerator":32, "denominator":64})
337
338 def test_rational_to_float(self):
339 """Unit test for rational_to_float.
340 """
341 self.assertTrue(
342 abs(rational_to_float({"numerator":32,"denominator":64})-0.5)
343 < 0.0001)
344
345if __name__ == '__main__':
346 unittest.main()
347