blob: 1269b84f30d517f26241d5b10871604586889396 [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,
Clemenz Portmannad4062c2017-05-25 15:19:09 -0700111 "android.shading.mode": 1,
112 "android.lens.opticalStabilizationMode": 0
Ruben Brunk370e2432014-10-14 18:33:23 -0700113 }
114 if linear_tonemap:
Yin-Chia Yeh9afa7252015-05-08 15:05:50 -0700115 assert(props is not None)
116 #CONTRAST_CURVE mode
117 if 0 in props["android.tonemap.availableToneMapModes"]:
118 req["android.tonemap.mode"] = 0
119 req["android.tonemap.curveRed"] = [0.0,0.0, 1.0,1.0]
120 req["android.tonemap.curveGreen"] = [0.0,0.0, 1.0,1.0]
121 req["android.tonemap.curveBlue"] = [0.0,0.0, 1.0,1.0]
122 #GAMMA_VALUE mode
123 elif 3 in props["android.tonemap.availableToneMapModes"]:
124 req["android.tonemap.mode"] = 3
125 req["android.tonemap.gamma"] = 1.0
126 else:
127 print "Linear tonemap is not supported"
128 assert(False)
Ruben Brunk370e2432014-10-14 18:33:23 -0700129 return req
130
131def auto_capture_request():
132 """Return a capture request with everything set to auto.
133 """
134 return {
135 "android.control.mode": 1,
136 "android.control.aeMode": 1,
137 "android.control.awbMode": 1,
138 "android.control.afMode": 1,
139 "android.colorCorrection.mode": 1,
140 "android.tonemap.mode": 1,
141 }
142
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800143def fastest_auto_capture_request(props):
144 """Return an auto capture request for the fastest capture.
145
146 Args:
147 props: the object returned from its.device.get_camera_properties().
148
149 Returns:
150 A capture request with everything set to auto and all filters that
151 may slow down capture set to OFF or FAST if possible
152 """
153 req = auto_capture_request()
154 turn_slow_filters_off(props, req)
155
156 return req
157
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700158def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
Ruben Brunk370e2432014-10-14 18:33:23 -0700159 """Return a sorted list of available output sizes for a given format.
160
161 Args:
Yin-Chia Yeh76dd1432015-04-27 16:42:03 -0700162 fmt: the output format, as a string in
163 ["jpg", "yuv", "raw", "raw10", "raw12"].
Ruben Brunk370e2432014-10-14 18:33:23 -0700164 props: the object returned from its.device.get_camera_properties().
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700165 max_size: (Optional) A (w,h) tuple.
166 Sizes larger than max_size (either w or h) will be discarded.
167 match_ar_size: (Optional) A (w,h) tuple.
168 Sizes not matching the aspect ratio of match_ar_size will be
169 discarded.
Ruben Brunk370e2432014-10-14 18:33:23 -0700170
171 Returns:
172 A sorted list of (w,h) tuples (sorted large-to-small).
173 """
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700174 AR_TOLERANCE = 0.03
Yin-Chia Yeh8563e4d2015-09-08 16:31:30 -0700175 fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26,"yuv":0x23,
176 "jpg":0x100, "jpeg":0x100}
Ruben Brunk370e2432014-10-14 18:33:23 -0700177 configs = props['android.scaler.streamConfigurationMap']\
178 ['availableStreamConfigurations']
179 fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
180 out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
181 out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700182 if max_size:
183 out_sizes = [s for s in out_sizes if
184 s[0] <= max_size[0] and s[1] <= max_size[1]]
185 if match_ar_size:
186 ar = match_ar_size[0] / float(match_ar_size[1])
187 out_sizes = [s for s in out_sizes if
188 abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
Yin-Chia Yeh4a137512016-08-10 00:02:12 -0700189 out_sizes.sort(reverse=True, key=lambda s: s[0]) # 1st pass, sort by width
190 out_sizes.sort(reverse=True, key=lambda s: s[0]*s[1]) # sort by area
Ruben Brunk370e2432014-10-14 18:33:23 -0700191 return out_sizes
192
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800193def set_filter_off_or_fast_if_possible(props, req, available_modes, filter):
Unsuk Jungff9926e2015-09-12 19:50:47 +0000194 """Check and set controlKey to off or fast in req.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800195
196 Args:
197 props: the object returned from its.device.get_camera_properties().
Unsuk Jungff9926e2015-09-12 19:50:47 +0000198 req: the input request. filter will be set to OFF or FAST if possible.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800199 available_modes: the key to check available modes.
200 filter: the filter key
201
202 Returns:
Unsuk Jungff9926e2015-09-12 19:50:47 +0000203 Nothing.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800204 """
205 if props.has_key(available_modes):
206 if 0 in props[available_modes]:
207 req[filter] = 0
208 elif 1 in props[available_modes]:
209 req[filter] = 1
210
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800211def turn_slow_filters_off(props, req):
212 """Turn filters that may slow FPS down to OFF or FAST in input request.
213
214 This function modifies the request argument, such that filters that may
215 reduce the frames-per-second throughput of the camera device will be set to
216 OFF or FAST if possible.
217
218 Args:
219 props: the object returned from its.device.get_camera_properties().
220 req: the input request.
221
222 Returns:
223 Nothing.
224 """
225 set_filter_off_or_fast_if_possible(props, req,
226 "android.noiseReduction.availableNoiseReductionModes",
227 "android.noiseReduction.mode")
228 set_filter_off_or_fast_if_possible(props, req,
229 "android.colorCorrection.availableAberrationModes",
230 "android.colorCorrection.aberrationMode")
Itaru Hanada87dcd9c2015-07-28 21:36:31 +0900231 if props.has_key("android.request.availableCharacteristicsKeys"):
232 hot_pixel_modes = 393217 in props["android.request.availableCharacteristicsKeys"]
233 edge_modes = 196610 in props["android.request.availableCharacteristicsKeys"]
234 if props.has_key("android.request.availableRequestKeys"):
235 hot_pixel_mode = 393216 in props["android.request.availableRequestKeys"]
236 edge_mode = 196608 in props["android.request.availableRequestKeys"]
237 if hot_pixel_modes and hot_pixel_mode:
238 set_filter_off_or_fast_if_possible(props, req,
239 "android.hotPixel.availableHotPixelModes",
240 "android.hotPixel.mode")
241 if edge_modes and edge_mode:
242 set_filter_off_or_fast_if_possible(props, req,
243 "android.edge.availableEdgeModes",
244 "android.edge.mode")
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800245
Ruben Brunk370e2432014-10-14 18:33:23 -0700246def get_fastest_manual_capture_settings(props):
247 """Return a capture request and format spec for the fastest capture.
248
249 Args:
250 props: the object returned from its.device.get_camera_properties().
251
252 Returns:
253 Two values, the first is a capture request, and the second is an output
254 format specification, for the fastest possible (legal) capture that
255 can be performed on this device (with the smallest output size).
256 """
257 fmt = "yuv"
258 size = get_available_output_sizes(fmt, props)[-1]
259 out_spec = {"format":fmt, "width":size[0], "height":size[1]}
260 s = min(props['android.sensor.info.sensitivityRange'])
261 e = min(props['android.sensor.info.exposureTimeRange'])
262 req = manual_capture_request(s,e)
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800263
Unsuk Jungff9926e2015-09-12 19:50:47 +0000264 turn_slow_filters_off(props, req)
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800265
Ruben Brunk370e2432014-10-14 18:33:23 -0700266 return req, out_spec
267
Clemenz Portmann210b89e2017-01-30 13:46:22 -0800268
Clemenz Portmann7e4f8472017-03-17 09:42:16 -0700269def get_smallest_yuv_format(props, match_ar=None):
Clemenz Portmann210b89e2017-01-30 13:46:22 -0800270 """Return a capture request and format spec for the smallest yuv size.
271
272 Args:
273 props: the object returned from its.device.get_camera_properties().
274
275 Returns:
276 fmt: an output format specification, for the smallest possible yuv
277 format for this device.
278 """
Clemenz Portmann7e4f8472017-03-17 09:42:16 -0700279 size = get_available_output_sizes("yuv", props, match_ar_size=match_ar)[-1]
Clemenz Portmann210b89e2017-01-30 13:46:22 -0800280 fmt = {"format":"yuv", "width":size[0], "height":size[1]}
281
282 return fmt
283
284
285def get_largest_yuv_format(props):
286 """Return a capture request and format spec for the smallest yuv size.
287
288 Args:
289 props: the object returned from its.device.get_camera_properties().
290
291 Returns:
292 fmt: an output format specification, for the smallest possible yuv
293 format for this device.
294 """
295 size = get_available_output_sizes("yuv", props)[0]
296 fmt = {"format":"yuv", "width":size[0], "height":size[1]}
297
298 return fmt
299
300
Chien-Yu Chen0feea4a2014-10-20 11:04:11 -0700301def get_max_digital_zoom(props):
302 """Returns the maximum amount of zooming possible by the camera device.
303
304 Args:
305 props: the object returned from its.device.get_camera_properties().
306
307 Return:
308 A float indicating the maximum amount of zooming possible by the
309 camera device.
310 """
311
312 maxz = 1.0
313
314 if props.has_key("android.scaler.availableMaxDigitalZoom"):
315 maxz = props["android.scaler.availableMaxDigitalZoom"]
316
317 return maxz
318
319
Ruben Brunk370e2432014-10-14 18:33:23 -0700320class __UnitTest(unittest.TestCase):
321 """Run a suite of unit tests on this module.
322 """
323
324 def test_int_to_rational(self):
325 """Unit test for int_to_rational.
326 """
327 self.assertEqual(int_to_rational(10),
328 {"numerator":10,"denominator":1})
329 self.assertEqual(int_to_rational([1,2]),
330 [{"numerator":1,"denominator":1},
331 {"numerator":2,"denominator":1}])
332
333 def test_float_to_rational(self):
334 """Unit test for float_to_rational.
335 """
336 self.assertEqual(float_to_rational(0.5001, 64),
337 {"numerator":32, "denominator":64})
338
339 def test_rational_to_float(self):
340 """Unit test for rational_to_float.
341 """
342 self.assertTrue(
343 abs(rational_to_float({"numerator":32,"denominator":64})-0.5)
344 < 0.0001)
345
346if __name__ == '__main__':
347 unittest.main()
348