blob: a531f3bbe4d1c2ad59cf961c9346f6ed26348f57 [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
126def get_available_output_sizes(fmt, props):
127 """Return a sorted list of available output sizes for a given format.
128
129 Args:
130 fmt: the output format, as a string in ["jpg", "yuv", "raw"].
131 props: the object returned from its.device.get_camera_properties().
132
133 Returns:
134 A sorted list of (w,h) tuples (sorted large-to-small).
135 """
136 fmt_codes = {"raw":0x20, "raw10":0x25, "yuv":0x23, "jpg":0x100, "jpeg":0x100}
137 configs = props['android.scaler.streamConfigurationMap']\
138 ['availableStreamConfigurations']
139 fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
140 out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
141 out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
142 out_sizes.sort(reverse=True)
143 return out_sizes
144
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800145def set_filter_off_or_fast_if_possible(props, req, available_modes, filter):
146 """ Check and set controlKey to off or fast in req
147
148 Args:
149 props: the object returned from its.device.get_camera_properties().
150 req: the input request.
151 available_modes: the key to check available modes.
152 filter: the filter key
153
154 Returns:
155 None. control_key will be set to OFF or FAST if possible.
156 """
157 if props.has_key(available_modes):
158 if 0 in props[available_modes]:
159 req[filter] = 0
160 elif 1 in props[available_modes]:
161 req[filter] = 1
162
Ruben Brunk370e2432014-10-14 18:33:23 -0700163def get_fastest_manual_capture_settings(props):
164 """Return a capture request and format spec for the fastest capture.
165
166 Args:
167 props: the object returned from its.device.get_camera_properties().
168
169 Returns:
170 Two values, the first is a capture request, and the second is an output
171 format specification, for the fastest possible (legal) capture that
172 can be performed on this device (with the smallest output size).
173 """
174 fmt = "yuv"
175 size = get_available_output_sizes(fmt, props)[-1]
176 out_spec = {"format":fmt, "width":size[0], "height":size[1]}
177 s = min(props['android.sensor.info.sensitivityRange'])
178 e = min(props['android.sensor.info.exposureTimeRange'])
179 req = manual_capture_request(s,e)
Yin-Chia Yeh3657ae32014-11-13 15:06:22 -0800180
181 set_filter_off_or_fast_if_possible(props, req,
182 "android.noiseReduction.availableNoiseReductionModes",
183 "android.noiseReduction.mode")
184 set_filter_off_or_fast_if_possible(props, req,
185 "android.colorCorrection.availableAberrationModes",
186 "android.colorCorrection.aberrationMode")
187 set_filter_off_or_fast_if_possible(props, req,
188 "android.hotPixel.availableHotPixelModes",
189 "android.hotPixel.mode")
190 set_filter_off_or_fast_if_possible(props, req,
191 "android.edge.availableEdgeModes",
192 "android.edge.mode")
193
Ruben Brunk370e2432014-10-14 18:33:23 -0700194 return req, out_spec
195
Chien-Yu Chen0feea4a2014-10-20 11:04:11 -0700196def get_max_digital_zoom(props):
197 """Returns the maximum amount of zooming possible by the camera device.
198
199 Args:
200 props: the object returned from its.device.get_camera_properties().
201
202 Return:
203 A float indicating the maximum amount of zooming possible by the
204 camera device.
205 """
206
207 maxz = 1.0
208
209 if props.has_key("android.scaler.availableMaxDigitalZoom"):
210 maxz = props["android.scaler.availableMaxDigitalZoom"]
211
212 return maxz
213
214
Ruben Brunk370e2432014-10-14 18:33:23 -0700215class __UnitTest(unittest.TestCase):
216 """Run a suite of unit tests on this module.
217 """
218
219 def test_int_to_rational(self):
220 """Unit test for int_to_rational.
221 """
222 self.assertEqual(int_to_rational(10),
223 {"numerator":10,"denominator":1})
224 self.assertEqual(int_to_rational([1,2]),
225 [{"numerator":1,"denominator":1},
226 {"numerator":2,"denominator":1}])
227
228 def test_float_to_rational(self):
229 """Unit test for float_to_rational.
230 """
231 self.assertEqual(float_to_rational(0.5001, 64),
232 {"numerator":32, "denominator":64})
233
234 def test_rational_to_float(self):
235 """Unit test for rational_to_float.
236 """
237 self.assertTrue(
238 abs(rational_to_float({"numerator":32,"denominator":64})-0.5)
239 < 0.0001)
240
241if __name__ == '__main__':
242 unittest.main()
243