blob: 94ab23b4b41b7bb82bdb223fdd846d23f37a78a5 [file] [log] [blame]
Rucha Katakwarb83e59a2021-02-02 17:16:54 -08001# Copyright 2014 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.
Clemenz Portmanncddf2bf2021-03-19 13:21:16 -070014"""Utility functions to determine what functionality the camera supports."""
Rucha Katakwarb83e59a2021-02-02 17:16:54 -080015
16
17import logging
Clemenz Portmann452eead2021-02-08 13:55:12 -080018import unittest
Rucha Katakwarb83e59a2021-02-02 17:16:54 -080019from mobly import asserts
20import numpy as np
21import capture_request_utils
22
23LENS_FACING_FRONT = 0
24LENS_FACING_BACK = 1
25LENS_FACING_EXTERNAL = 2
26MULTI_CAMERA_SYNC_CALIBRATED = 1
27NUM_DISTORTION_PARAMS = 5 # number of terms in lens.distortion
28NUM_INTRINSIC_CAL_PARAMS = 5 # number of terms in intrinsic calibration
29NUM_POSE_ROTATION_PARAMS = 4 # number of terms in poseRotation
30NUM_POSE_TRANSLATION_PARAMS = 3 # number of terms in poseTranslation
31SKIP_RET_MSG = 'Test skipped'
32SOLID_COLOR_TEST_PATTERN = 1
33
34
35def legacy(props):
36 """Returns whether a device is a LEGACY capability camera2 device.
37
38 Args:
39 props: Camera properties object.
40
41 Returns:
42 Boolean. True if device is a LEGACY camera.
43 """
44 return props.get('android.info.supportedHardwareLevel') == 2
45
46
47def limited(props):
48 """Returns whether a device is a LIMITED capability camera2 device.
49
50 Args:
51 props: Camera properties object.
52
53 Returns:
54 Boolean. True if device is a LIMITED camera.
55 """
56 return props.get('android.info.supportedHardwareLevel') == 0
57
58
59def full_or_better(props):
60 """Returns whether a device is a FULL or better camera2 device.
61
62 Args:
63 props: Camera properties object.
64
65 Returns:
66 Boolean. True if device is FULL or LEVEL3 camera.
67 """
68 return (props.get('android.info.supportedHardwareLevel') >= 1 and
69 props.get('android.info.supportedHardwareLevel') != 2)
70
71
72def level3(props):
73 """Returns whether a device is a LEVEL3 capability camera2 device.
74
75 Args:
76 props: Camera properties object.
77
78 Returns:
79 Boolean. True if device is LEVEL3 camera.
80 """
81 return props.get('android.info.supportedHardwareLevel') == 3
82
83
84def manual_sensor(props):
85 """Returns whether a device supports MANUAL_SENSOR capabilities.
86
87 Args:
88 props: Camera properties object.
89
90 Returns:
91 Boolean. True if devices supports MANUAL_SENSOR capabilities.
92 """
93 return 1 in props.get('android.request.availableCapabilities', [])
94
95
96def manual_post_proc(props):
97 """Returns whether a device supports MANUAL_POST_PROCESSING capabilities.
98
99 Args:
100 props: Camera properties object.
101
102 Returns:
103 Boolean. True if device supports MANUAL_POST_PROCESSING capabilities.
104 """
105 return 2 in props.get('android.request.availableCapabilities', [])
106
107
108def raw(props):
109 """Returns whether a device supports RAW capabilities.
110
111 Args:
112 props: Camera properties object.
113
114 Returns:
115 Boolean. True if device supports RAW capabilities.
116 """
117 return 3 in props.get('android.request.availableCapabilities', [])
118
119
120def sensor_fusion(props):
121 """Checks the camera and motion sensor timestamps.
122
123 Returns whether the camera and motion sensor timestamps for the device
124 are in the same time domain and can be compared directly.
125
126 Args:
127 props: Camera properties object.
128
129 Returns:
130 Boolean. True if camera and motion sensor timestamps in same time domain.
131 """
132 return props.get('android.sensor.info.timestampSource') == 1
133
134
135def logical_multi_camera(props):
136 """Returns whether a device is a logical multi-camera.
137
138 Args:
139 props: Camera properties object.
140
141 Returns:
142 Boolean. True if the device is a logical multi-camera.
143 """
144 return 11 in props.get('android.request.availableCapabilities', [])
145
146
147def logical_multi_camera_physical_ids(props):
148 """Returns a logical multi-camera's underlying physical cameras.
149
150 Args:
151 props: Camera properties object.
152
153 Returns:
154 list of physical cameras backing the logical multi-camera.
155 """
156 physical_ids_list = []
157 if logical_multi_camera(props):
158 physical_ids_list = props['camera.characteristics.physicalCamIds']
159 return physical_ids_list
160
161
162def skip_unless(cond):
163 """Skips the test if the condition is false.
164
165 If a test is skipped, then it is exited and returns the special code
166 of 101 to the calling shell, which can be used by an external test
167 harness to differentiate a skip from a pass or fail.
168
169 Args:
170 cond: Boolean, which must be true for the test to not skip.
171
172 Returns:
173 Nothing.
174 """
175 if not cond:
176 asserts.skip(SKIP_RET_MSG)
177
178
179def backward_compatible(props):
180 """Returns whether a device supports BACKWARD_COMPATIBLE.
181
182 Args:
183 props: Camera properties object.
184
185 Returns:
186 Boolean. True if the devices supports BACKWARD_COMPATIBLE.
187 """
188 return 0 in props.get('android.request.availableCapabilities', [])
189
190
191def lens_calibrated(props):
192 """Returns whether lens position is calibrated or not.
193
194 android.lens.info.focusDistanceCalibration has 3 modes.
195 0: Uncalibrated
196 1: Approximate
197 2: Calibrated
198
199 Args:
200 props: Camera properties objects.
201
202 Returns:
203 Boolean. True if lens is CALIBRATED.
204 """
205 return 'android.lens.info.focusDistanceCalibration' in props and props[
206 'android.lens.info.focusDistanceCalibration'] == 2
207
208
209def lens_approx_calibrated(props):
210 """Returns whether lens position is calibrated or not.
211
212 android.lens.info.focusDistanceCalibration has 3 modes.
213 0: Uncalibrated
214 1: Approximate
215 2: Calibrated
216
217 Args:
218 props: Camera properties objects.
219
220 Returns:
221 Boolean. True if lens is APPROXIMATE or CALIBRATED.
222 """
223 return props.get('android.lens.info.focusDistanceCalibration') in [1, 2]
224
225
226def raw10(props):
227 """Returns whether a device supports RAW10 capabilities.
228
229 Args:
230 props: Camera properties object.
231
232 Returns:
233 Boolean. True if device supports RAW10 capabilities.
234 """
235 if capture_request_utils.get_available_output_sizes('raw10', props):
236 return True
237 return False
238
239
240def raw12(props):
241 """Returns whether a device supports RAW12 capabilities.
242
243 Args:
244 props: Camera properties object.
245
246 Returns:
247 Boolean. True if device supports RAW12 capabilities.
248 """
249 if capture_request_utils.get_available_output_sizes('raw12', props):
250 return True
251 return False
252
253
254def raw16(props):
255 """Returns whether a device supports RAW16 output.
256
257 Args:
258 props: Camera properties object.
259
260 Returns:
261 Boolean. True if device supports RAW16 capabilities.
262 """
263 if capture_request_utils.get_available_output_sizes('raw', props):
264 return True
265 return False
266
267
268def raw_output(props):
269 """Returns whether a device supports any of the RAW output formats.
270
271 Args:
272 props: Camera properties object.
273
274 Returns:
275 Boolean. True if device supports any of the RAW output formats
276 """
277 return raw16(props) or raw10(props) or raw12(props)
278
279
280def per_frame_control(props):
281 """Returns whether a device supports per frame control.
282
283 Args:
284 props: Camera properties object.
285
286 Returns: Boolean. True if devices supports per frame control.
287 """
288 return 'android.sync.maxLatency' in props and props[
289 'android.sync.maxLatency'] == 0
290
291
292def mono_camera(props):
293 """Returns whether a device is monochromatic.
294
295 Args:
296 props: Camera properties object.
297 Returns: Boolean. True if MONO camera.
298 """
299 return 12 in props.get('android.request.availableCapabilities', [])
300
301
302def fixed_focus(props):
303 """Returns whether a device is fixed focus.
304
305 props[android.lens.info.minimumFocusDistance] == 0 is fixed focus
306
307 Args:
308 props: Camera properties objects.
309
310 Returns:
311 Boolean. True if device is a fixed focus camera.
312 """
313 return 'android.lens.info.minimumFocusDistance' in props and props[
314 'android.lens.info.minimumFocusDistance'] == 0
315
316
317def face_detect(props):
318 """Returns whether a device has face detection mode.
319
320 props['android.statistics.info.availableFaceDetectModes'] != 0
321
322 Args:
323 props: Camera properties objects.
324
325 Returns:
326 Boolean. True if device supports face detection.
327 """
328 return 'android.statistics.info.availableFaceDetectModes' in props and props[
329 'android.statistics.info.availableFaceDetectModes'] != [0]
330
331
332def read_3a(props):
333 """Return whether a device supports reading out the below 3A settings.
334
335 sensitivity
336 exposure time
337 awb gain
338 awb cct
339 focus distance
340
341 Args:
342 props: Camera properties object.
343
344 Returns:
345 Boolean. True if device supports reading out 3A settings.
346 """
347 return manual_sensor(props) and manual_post_proc(props)
348
349
350def compute_target_exposure(props):
351 """Return whether a device supports target exposure computation.
352
353 Args:
354 props: Camera properties object.
355
356 Returns:
357 Boolean. True if device supports target exposure computation.
358 """
359 return manual_sensor(props) and manual_post_proc(props)
360
361
362def y8(props):
363 """Returns whether a device supports Y8 output.
364
365 Args:
366 props: Camera properties object.
367
368 Returns:
369 Boolean. True if device suupports Y8 output.
370 """
371 if capture_request_utils.get_available_output_sizes('y8', props):
372 return True
373 return False
374
375
376def jpeg_quality(props):
377 """Returns whether a device supports JPEG quality."""
378 return ('camera.characteristics.requestKeys' in props) and (
379 'android.jpeg.quality' in props['camera.characteristics.requestKeys'])
380
381
382def zoom_ratio_range(props):
383 """Returns whether a device supports zoom capabilities.
384
385 Args:
386 props: Camera properties object.
387
388 Returns:
389 Boolean. True if device supports zoom capabilities.
390 """
391 return 'android.control.zoomRatioRange' in props and props[
392 'android.control.zoomRatioRange'] is not None
393
394
395def sync_latency(props):
396 """Returns sync latency in number of frames.
397
398 If undefined, 8 frames.
399
400 Args:
401 props: Camera properties object.
402
403 Returns:
404 integer number of frames.
405 """
406 latency = props['android.sync.maxLatency']
407 if latency < 0:
408 latency = 8
409 return latency
410
411
412def get_max_digital_zoom(props):
413 """Returns the maximum amount of zooming possible by the camera device.
414
415 Args:
416 props: Camera properties object.
417
418 Returns:
419 A float indicating the maximum amount of zooming possible by the
420 camera device.
421 """
422 z_max = 1.0
423 if 'android.scaler.availableMaxDigitalZoom' in props:
424 z_max = props['android.scaler.availableMaxDigitalZoom']
425 return z_max
426
427
428def ae_lock(props):
429 """Returns whether a device supports AE lock.
430
431 Args:
432 props: Camera properties object.
433
434 Returns:
435 Boolean. True if device supports AE lock.
436 """
437 return 'android.control.aeLockAvailable' in props and props[
438 'android.control.aeLockAvailable'] == 1
439
440
441def awb_lock(props):
442 """Returns whether a device supports AWB lock.
443
444 Args:
445 props: Camera properties object.
446
447 Returns:
448 Boolean. True if device supports AWB lock.
449 """
450 return 'android.control.awbLockAvailable' in props and props[
451 'android.control.awbLockAvailable'] == 1
452
453
454def ev_compensation(props):
455 """Returns whether a device supports ev compensation.
456
457 Args:
458 props: Camera properties object.
459
460 Returns:
461 Boolean. True if device supports EV compensation.
462 """
463 return 'android.control.aeCompensationRange' in props and props[
464 'android.control.aeCompensationRange'] != [0, 0]
465
466
467def flash(props):
468 """Returns whether a device supports flash control.
469
470 Args:
471 props: Camera properties object.
472
473 Returns:
474 Boolean. True if device supports flash control.
475 """
476 return 'android.flash.info.available' in props and props[
477 'android.flash.info.available'] == 1
478
479
480def distortion_correction(props):
481 """Returns whether a device supports android.lens.distortion capabilities.
482
483 Args:
484 props: Camera properties object.
485
486 Returns:
487 Boolean. True if device supports lens distortion correction capabilities.
488 """
489 return 'android.lens.distortion' in props and props[
490 'android.lens.distortion'] is not None
491
492
493def freeform_crop(props):
494 """Returns whether a device supports freefrom cropping.
495
496 Args:
497 props: Camera properties object.
498
499 Returns:
500 Boolean. True if device supports freeform cropping.
501 """
502 return 'android.scaler.croppingType' in props and props[
503 'android.scaler.croppingType'] == 1
504
505
506def noise_reduction_mode(props, mode):
507 """Returns whether a device supports the noise reduction mode.
508
509 Args:
510 props: Camera properties objects.
511 mode: Integer indicating noise reduction mode to check for availability.
512
513 Returns:
514 Boolean. Ture if devices supports noise reduction mode(s).
515 """
516 return ('android.noiseReduction.availableNoiseReductionModes' in props and
517 mode in props['android.noiseReduction.availableNoiseReductionModes'])
518
519
520def lsc_map(props):
521 """Returns whether a device supports lens shading map output.
522
523 Args:
524 props: Camera properties object.
525 Returns: Boolean. True if device supports lens shading map output.
526 """
527 return 1 in props.get('android.statistics.info.availableLensShadingMapModes',
528 [])
529
530
531def lsc_off(props):
532 """Returns whether a device supports disabling lens shading correction.
533
534 Args:
535 props: Camera properties object.
536
537 Returns:
538 Boolean. True if device supports disabling lens shading correction.
539 """
540 return 0 in props.get('android.shading.availableModes', [])
541
542
543def edge_mode(props, mode):
544 """Returns whether a device supports the edge mode.
545
546 Args:
547 props: Camera properties objects.
548 mode: Integer, indicating the edge mode to check for availability.
549
550 Returns:
551 Boolean. True if device supports edge mode(s).
552 """
553 return 'android.edge.availableEdgeModes' in props and mode in props[
554 'android.edge.availableEdgeModes']
555
556
557def yuv_reprocess(props):
558 """Returns whether a device supports YUV reprocessing.
559
560 Args:
561 props: Camera properties object.
562
563 Returns:
564 Boolean. True if device supports YUV reprocessing.
565 """
566 return 'android.request.availableCapabilities' in props and 7 in props[
567 'android.request.availableCapabilities']
568
569
570def private_reprocess(props):
571 """Returns whether a device supports PRIVATE reprocessing.
572
573 Args:
574 props: Camera properties object.
575
576 Returns:
577 Boolean. True if device supports PRIVATE reprocessing.
578 """
579 return 'android.request.availableCapabilities' in props and 4 in props[
580 'android.request.availableCapabilities']
581
582
583def intrinsic_calibration(props):
584 """Returns whether a device supports android.lens.intrinsicCalibration.
585
586 Args:
587 props: Camera properties object.
588
589 Returns:
590 Boolean. True if device supports android.lens.intrinsicCalibratino.
591 """
592 return props.get('android.lens.intrinsicCalibration') is not None
593
594
595def get_intrinsic_calibration(props, debug, fd=None):
596 """Get intrinsicCalibration and create intrisic matrix.
597
598 If intrinsic android.lens.intrinsicCalibration does not exist, return None.
599
600 Args:
601 props: camera properties
602 debug: bool to print more information
603 fd: focal length from capture metadata
604
605 Returns:
606 intrinsic transformation matrix
607 k = [[f_x, s, c_x],
608 [0, f_y, c_y],
609 [0, 0, 1]]
610 """
611 if props.get('android.lens.intrinsicCalibration'):
612 ical = np.array(props['android.lens.intrinsicCalibration'])
613 else:
614 logging.error('Device does not have android.lens.intrinsicCalibration.')
615 return None
616
617 # basic checks for parameter correctness
618 ical_len = len(ical)
619 if ical_len != NUM_INTRINSIC_CAL_PARAMS:
620 raise ValueError(
621 f'instrisicCalibration has wrong number of params: {ical_len}.')
622
623 if fd is not None:
624 # detailed checks for parameter correctness
625 # Intrinsic cal is of format: [f_x, f_y, c_x, c_y, s]
626 # [f_x, f_y] is the horizontal and vertical focal lengths,
627 # [c_x, c_y] is the position of the optical axis,
628 # and s is skew of sensor plane vs lens plane.
629 sensor_h = props['android.sensor.info.physicalSize']['height']
630 sensor_w = props['android.sensor.info.physicalSize']['width']
631 pixel_h = props['android.sensor.info.pixelArraySize']['height']
632 pixel_w = props['android.sensor.info.pixelArraySize']['width']
633 fd_w_pix = pixel_w * fd / sensor_w
634 fd_h_pix = pixel_h * fd / sensor_h
635
636 if not np.isclose(fd_w_pix, ical[0], rtol=0.20):
637 raise ValueError('fd_w(pixels): %.2f\tcal[0](pixels): %.2f\tTOL=20%%' % (
638 fd_w_pix, ical[0]))
639 if not np.isclose(fd_h_pix, ical[1], rtol=0.20):
640 raise ValueError('fd_h(pixels): %.2f\tcal[1](pixels): %.2f\tTOL=20%%' % (
641 fd_h_pix, ical[0]))
642
643 # generate instrinsic matrix
644 k = np.array([[ical[0], ical[4], ical[2]],
645 [0, ical[1], ical[3]],
646 [0, 0, 1]])
647 if debug:
648 logging.debug('k: %s', str(k))
649 return k
650
651
652def get_translation_matrix(props, debug):
653 """Get translation matrix.
654
655 Args:
656 props: dict of camera properties
657 debug: boolean flag to log more info
658
659 Returns:
660 android.lens.poseTranslation matrix if it exists, otherwise None.
661 """
662 if props['android.lens.poseTranslation']:
663 t = np.array(props['android.lens.poseTranslation'])
664 else:
665 logging.error('Device does not have android.lens.poseTranslation.')
666 return None
667
668 if debug:
669 logging.debug('translation: %s', str(t))
670 t_len = len(t)
671 if t_len != NUM_POSE_TRANSLATION_PARAMS:
672 raise ValueError(f'poseTranslation has wrong # of params: {t_len}.')
673 return t
674
675
676def get_rotation_matrix(props, debug):
677 """Convert the rotation parameters to 3-axis data.
678
679 Args:
680 props: camera properties
681 debug: boolean for more information
682
683 Returns:
684 3x3 matrix w/ rotation parameters if poseRotation exists, otherwise None
685 """
686 if props['android.lens.poseRotation']:
687 rotation = np.array(props['android.lens.poseRotation'])
688 else:
689 logging.error('Device does not have android.lens.poseRotation.')
690 return None
691
692 if debug:
693 logging.debug('rotation: %s', str(rotation))
694 rotation_len = len(rotation)
695 if rotation_len != NUM_POSE_ROTATION_PARAMS:
696 raise ValueError(f'poseRotation has wrong # of params: {rotation_len}.')
697 x = rotation[0]
698 y = rotation[1]
699 z = rotation[2]
700 w = rotation[3]
701 return np.array([[1-2*y**2-2*z**2, 2*x*y-2*z*w, 2*x*z+2*y*w],
702 [2*x*y+2*z*w, 1-2*x**2-2*z**2, 2*y*z-2*x*w],
703 [2*x*z-2*y*w, 2*y*z+2*x*w, 1-2*x**2-2*y**2]])
704
705
706def get_distortion_matrix(props):
707 """Get android.lens.distortion matrix and convert to cv2 fmt.
708
709 Args:
710 props: dict of camera properties
711
712 Returns:
713 cv2 reordered android.lens.distortion if it exists, otherwise None.
714 """
715 if props['android.lens.distortion']:
716 dist = np.array(props['android.lens.distortion'])
717 else:
718 logging.error('Device does not have android.lens.distortion.')
719 return None
720
721 dist_len = len(dist)
722 if len(dist) != NUM_DISTORTION_PARAMS:
723 raise ValueError(f'lens.distortion has wrong # of params: {dist_len}.')
724 cv2_distort = np.array([dist[0], dist[1], dist[3], dist[4], dist[2]])
725 logging.debug('cv2 distortion params: %s', str(cv2_distort))
726 return cv2_distort
727
728
729def post_raw_sensitivity_boost(props):
730 """Returns whether a device supports post RAW sensitivity boost.
731
732 Args:
733 props: Camera properties object.
734
735 Returns:
736 Boolean. True if android.control.postRawSensitivityBoost is supported.
737 """
738 return props.get('android.control.postRawSensitivityBoostRange') != [100, 100]
739
740
741def sensor_fusion_capable(props):
742 """Determine if test_sensor_fusion is run."""
743 return all([sensor_fusion(props),
744 manual_sensor(props),
745 props['android.lens.facing'] != LENS_FACING_EXTERNAL])
746
747
748def continuous_picture(props):
749 """Returns whether a device supports CONTINUOUS_PICTURE.
750
751 Args:
752 props: Camera properties object.
753
754 Returns:
755 Boolean. True if CONTINUOUS_PICTURE in android.control.afAvailableModes.
756 """
757 return 4 in props.get('android.control.afAvailableModes', [])
758
759
760def af_scene_change(props):
761 """Returns whether a device supports AF_SCENE_CHANGE.
762
763 Args:
764 props: Camera properties object.
765
766 Returns:
767 Boolean. True if android.control.afSceneChange supported.
768 """
769 return 'android.control.afSceneChange' in props.get(
770 'camera.characteristics.resultKeys')
771
772
773def multi_camera_frame_sync_capable(props):
774 """Determines if test_multi_camera_frame_sync can be run."""
775 return all([
776 read_3a(props),
777 per_frame_control(props),
778 logical_multi_camera(props),
779 sensor_fusion(props),
780 ])
781
782
783def multi_camera_sync_calibrated(props):
784 """Determines if multi-camera sync type is CALIBRATED or APPROXIMATE.
785
786 Args:
787 props: Camera properties object.
788
789 Returns:
790 Boolean. True if android.logicalMultiCamera.sensorSyncType is CALIBRATED.
791 """
792 return props.get('android.logicalMultiCamera.sensorSyncType'
793 ) == MULTI_CAMERA_SYNC_CALIBRATED
794
795
796def solid_color_test_pattern(props):
797 """Determines if camera supports solid color test pattern.
798
799 Args:
800 props: Camera properties object.
801
802 Returns:
803 Boolean. True if android.sensor.availableTestPatternModes has
804 SOLID_COLOR_TEST_PATTERN.
805 """
806 return SOLID_COLOR_TEST_PATTERN in props.get(
807 'android.sensor.availableTestPatternModes')
Clemenz Portmann452eead2021-02-08 13:55:12 -0800808
809
810if __name__ == '__main__':
811 unittest.main()
812