blob: dbddb8e60bb4c57c5795ab90770b6419206304e6 [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,
Clemenz Portmann5ba79d02017-06-14 15:27:47 -0700141 "android.lens.opticalStabilizationMode": 0
Ruben Brunk370e2432014-10-14 18:33:23 -0700142 }
143
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800144def fastest_auto_capture_request(props):
145 """Return an auto capture request for the fastest capture.
146
147 Args:
148 props: the object returned from its.device.get_camera_properties().
149
150 Returns:
151 A capture request with everything set to auto and all filters that
152 may slow down capture set to OFF or FAST if possible
153 """
154 req = auto_capture_request()
155 turn_slow_filters_off(props, req)
156
157 return req
158
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700159def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
Ruben Brunk370e2432014-10-14 18:33:23 -0700160 """Return a sorted list of available output sizes for a given format.
161
162 Args:
Yin-Chia Yeh76dd1432015-04-27 16:42:03 -0700163 fmt: the output format, as a string in
164 ["jpg", "yuv", "raw", "raw10", "raw12"].
Ruben Brunk370e2432014-10-14 18:33:23 -0700165 props: the object returned from its.device.get_camera_properties().
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700166 max_size: (Optional) A (w,h) tuple.
167 Sizes larger than max_size (either w or h) will be discarded.
168 match_ar_size: (Optional) A (w,h) tuple.
169 Sizes not matching the aspect ratio of match_ar_size will be
170 discarded.
Ruben Brunk370e2432014-10-14 18:33:23 -0700171
172 Returns:
173 A sorted list of (w,h) tuples (sorted large-to-small).
174 """
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700175 AR_TOLERANCE = 0.03
Yin-Chia Yeh8563e4d2015-09-08 16:31:30 -0700176 fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26,"yuv":0x23,
177 "jpg":0x100, "jpeg":0x100}
Ruben Brunk370e2432014-10-14 18:33:23 -0700178 configs = props['android.scaler.streamConfigurationMap']\
179 ['availableStreamConfigurations']
180 fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
181 out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
182 out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
Yin-Chia Yehd511ded2015-09-03 14:46:57 -0700183 if max_size:
184 out_sizes = [s for s in out_sizes if
185 s[0] <= max_size[0] and s[1] <= max_size[1]]
186 if match_ar_size:
187 ar = match_ar_size[0] / float(match_ar_size[1])
188 out_sizes = [s for s in out_sizes if
189 abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
Yin-Chia Yeh4a137512016-08-10 00:02:12 -0700190 out_sizes.sort(reverse=True, key=lambda s: s[0]) # 1st pass, sort by width
191 out_sizes.sort(reverse=True, key=lambda s: s[0]*s[1]) # sort by area
Ruben Brunk370e2432014-10-14 18:33:23 -0700192 return out_sizes
193
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800194def set_filter_off_or_fast_if_possible(props, req, available_modes, filter):
Unsuk Jungff9926e2015-09-12 19:50:47 +0000195 """Check and set controlKey to off or fast in req.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800196
197 Args:
198 props: the object returned from its.device.get_camera_properties().
Unsuk Jungff9926e2015-09-12 19:50:47 +0000199 req: the input request. filter will be set to OFF or FAST if possible.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800200 available_modes: the key to check available modes.
201 filter: the filter key
202
203 Returns:
Unsuk Jungff9926e2015-09-12 19:50:47 +0000204 Nothing.
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800205 """
206 if props.has_key(available_modes):
207 if 0 in props[available_modes]:
208 req[filter] = 0
209 elif 1 in props[available_modes]:
210 req[filter] = 1
211
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800212def turn_slow_filters_off(props, req):
213 """Turn filters that may slow FPS down to OFF or FAST in input request.
214
215 This function modifies the request argument, such that filters that may
216 reduce the frames-per-second throughput of the camera device will be set to
217 OFF or FAST if possible.
218
219 Args:
220 props: the object returned from its.device.get_camera_properties().
221 req: the input request.
222
223 Returns:
224 Nothing.
225 """
226 set_filter_off_or_fast_if_possible(props, req,
227 "android.noiseReduction.availableNoiseReductionModes",
228 "android.noiseReduction.mode")
229 set_filter_off_or_fast_if_possible(props, req,
230 "android.colorCorrection.availableAberrationModes",
231 "android.colorCorrection.aberrationMode")
Itaru Hanada87dcd9c2015-07-28 21:36:31 +0900232 if props.has_key("android.request.availableCharacteristicsKeys"):
233 hot_pixel_modes = 393217 in props["android.request.availableCharacteristicsKeys"]
234 edge_modes = 196610 in props["android.request.availableCharacteristicsKeys"]
235 if props.has_key("android.request.availableRequestKeys"):
236 hot_pixel_mode = 393216 in props["android.request.availableRequestKeys"]
237 edge_mode = 196608 in props["android.request.availableRequestKeys"]
238 if hot_pixel_modes and hot_pixel_mode:
239 set_filter_off_or_fast_if_possible(props, req,
240 "android.hotPixel.availableHotPixelModes",
241 "android.hotPixel.mode")
242 if edge_modes and edge_mode:
243 set_filter_off_or_fast_if_possible(props, req,
244 "android.edge.availableEdgeModes",
245 "android.edge.mode")
Yin-Chia Yehd0776022014-11-24 13:14:07 -0800246
Ruben Brunk370e2432014-10-14 18:33:23 -0700247def get_fastest_manual_capture_settings(props):
248 """Return a capture request and format spec for the fastest capture.
249
250 Args:
251 props: the object returned from its.device.get_camera_properties().
252
253 Returns:
254 Two values, the first is a capture request, and the second is an output
255 format specification, for the fastest possible (legal) capture that
256 can be performed on this device (with the smallest output size).
257 """
258 fmt = "yuv"
259 size = get_available_output_sizes(fmt, props)[-1]
260 out_spec = {"format":fmt, "width":size[0], "height":size[1]}
261 s = min(props['android.sensor.info.sensitivityRange'])
262 e = min(props['android.sensor.info.exposureTimeRange'])
263 req = manual_capture_request(s,e)
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800264
Unsuk Jungff9926e2015-09-12 19:50:47 +0000265 turn_slow_filters_off(props, req)
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800266
Ruben Brunk370e2432014-10-14 18:33:23 -0700267 return req, out_spec
268
Clemenz Portmann210b89e2017-01-30 13:46:22 -0800269
Clemenz Portmann7e4f8472017-03-17 09:42:16 -0700270def get_smallest_yuv_format(props, match_ar=None):
Clemenz Portmann210b89e2017-01-30 13:46:22 -0800271 """Return a capture request and format spec for the smallest yuv size.
272
273 Args:
274 props: the object returned from its.device.get_camera_properties().
275
276 Returns:
277 fmt: an output format specification, for the smallest possible yuv
278 format for this device.
279 """
Clemenz Portmann7e4f8472017-03-17 09:42:16 -0700280 size = get_available_output_sizes("yuv", props, match_ar_size=match_ar)[-1]
Clemenz Portmann210b89e2017-01-30 13:46:22 -0800281 fmt = {"format":"yuv", "width":size[0], "height":size[1]}
282
283 return fmt
284
285
286def get_largest_yuv_format(props):
287 """Return a capture request and format spec for the smallest yuv size.
288
289 Args:
290 props: the object returned from its.device.get_camera_properties().
291
292 Returns:
293 fmt: an output format specification, for the smallest possible yuv
294 format for this device.
295 """
296 size = get_available_output_sizes("yuv", props)[0]
297 fmt = {"format":"yuv", "width":size[0], "height":size[1]}
298
299 return fmt
300
301
Chien-Yu Chen0feea4a2014-10-20 11:04:11 -0700302def get_max_digital_zoom(props):
303 """Returns the maximum amount of zooming possible by the camera device.
304
305 Args:
306 props: the object returned from its.device.get_camera_properties().
307
308 Return:
309 A float indicating the maximum amount of zooming possible by the
310 camera device.
311 """
312
313 maxz = 1.0
314
315 if props.has_key("android.scaler.availableMaxDigitalZoom"):
316 maxz = props["android.scaler.availableMaxDigitalZoom"]
317
318 return maxz
319
320
Ruben Brunk370e2432014-10-14 18:33:23 -0700321class __UnitTest(unittest.TestCase):
322 """Run a suite of unit tests on this module.
323 """
324
325 def test_int_to_rational(self):
326 """Unit test for int_to_rational.
327 """
328 self.assertEqual(int_to_rational(10),
329 {"numerator":10,"denominator":1})
330 self.assertEqual(int_to_rational([1,2]),
331 [{"numerator":1,"denominator":1},
332 {"numerator":2,"denominator":1}])
333
334 def test_float_to_rational(self):
335 """Unit test for float_to_rational.
336 """
337 self.assertEqual(float_to_rational(0.5001, 64),
338 {"numerator":32, "denominator":64})
339
340 def test_rational_to_float(self):
341 """Unit test for rational_to_float.
342 """
343 self.assertTrue(
344 abs(rational_to_float({"numerator":32,"denominator":64})-0.5)
345 < 0.0001)
346
347if __name__ == '__main__':
348 unittest.main()
349