1# 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.
14"""Utility functions to determine what functionality the camera supports."""
15
16
17import logging
18import math
19import types
20
21from mobly import asserts
22import numpy as np
23
24import capture_request_utils
25
26FD_CAL_RTOL = 0.20
27LENS_FACING = types.MappingProxyType({'FRONT': 0, 'BACK': 1, 'EXTERNAL': 2})
28MULTI_CAMERA_SYNC_CALIBRATED = 1
29NUM_DISTORTION_PARAMS = 5  # number of terms in lens.distortion
30NUM_INTRINSIC_CAL_PARAMS = 5  # number of terms in intrinsic calibration
31NUM_POSE_ROTATION_PARAMS = 4  # number of terms in poseRotation
32NUM_POSE_TRANSLATION_PARAMS = 3  # number of terms in poseTranslation
33SKIP_RET_MSG = 'Test skipped'
34SOLID_COLOR_TEST_PATTERN = 1
35COLOR_BARS_TEST_PATTERN = 2
36USE_CASE_STILL_CAPTURE = 2
37DEFAULT_AE_TARGET_FPS_RANGE = (15, 30)
38COLOR_SPACES = [
39    'SRGB', 'LINEAR_SRGB', 'EXTENDED_SRGB',
40    'LINEAR_EXTENDED_SRGB', 'BT709', 'BT2020',
41    'DCI_P3', 'DISPLAY_P3', 'NTSC_1953', 'SMPTE_C',
42    'ADOBE_RGB', 'PRO_PHOTO_RGB', 'ACES', 'ACESCG',
43    'CIE_XYZ', 'CIE_LAB', 'BT2020_HLG', 'BT2020_PQ'
44]
45SETTINGS_OVERRIDE_ZOOM = 1
46STABILIZATION_MODE_OFF = 0
47STABILIZATION_MODE_PREVIEW = 2
48LENS_OPTICAL_STABILIZATION_MODE_ON = 1
49
50
51def check_front_or_rear_camera(props):
52  """Raises an error if not LENS_FACING FRONT or BACK.
53
54  Args:
55    props: Camera properties object.
56
57  Raises:
58    assertionError if not front or rear camera.
59  """
60  facing = props['android.lens.facing']
61  if not (facing == LENS_FACING['BACK'] or facing == LENS_FACING['FRONT']):
62    raise AssertionError('Unknown lens facing: {facing}.')
63
64
65def legacy(props):
66  """Returns whether a device is a LEGACY capability camera2 device.
67
68  Args:
69    props: Camera properties object.
70
71  Returns:
72    Boolean. True if device is a LEGACY camera.
73  """
74  return props.get('android.info.supportedHardwareLevel') == 2
75
76
77def limited(props):
78  """Returns whether a device is a LIMITED capability camera2 device.
79
80  Args:
81    props: Camera properties object.
82
83  Returns:
84     Boolean. True if device is a LIMITED camera.
85  """
86  return props.get('android.info.supportedHardwareLevel') == 0
87
88
89def full_or_better(props):
90  """Returns whether a device is a FULL or better camera2 device.
91
92  Args:
93    props: Camera properties object.
94
95  Returns:
96     Boolean. True if device is FULL or LEVEL3 camera.
97  """
98  return (props.get('android.info.supportedHardwareLevel') >= 1 and
99          props.get('android.info.supportedHardwareLevel') != 2)
100
101
102def level3(props):
103  """Returns whether a device is a LEVEL3 capability camera2 device.
104
105  Args:
106    props: Camera properties object.
107
108  Returns:
109    Boolean. True if device is LEVEL3 camera.
110  """
111  return props.get('android.info.supportedHardwareLevel') == 3
112
113
114def manual_sensor(props):
115  """Returns whether a device supports MANUAL_SENSOR capabilities.
116
117  Args:
118    props: Camera properties object.
119
120  Returns:
121    Boolean. True if devices supports MANUAL_SENSOR capabilities.
122  """
123  return 1 in props.get('android.request.availableCapabilities', [])
124
125
126def manual_post_proc(props):
127  """Returns whether a device supports MANUAL_POST_PROCESSING capabilities.
128
129  Args:
130    props: Camera properties object.
131
132  Returns:
133    Boolean. True if device supports MANUAL_POST_PROCESSING capabilities.
134  """
135  return 2 in props.get('android.request.availableCapabilities', [])
136
137
138def raw(props):
139  """Returns whether a device supports RAW capabilities.
140
141  Args:
142    props: Camera properties object.
143
144  Returns:
145    Boolean. True if device supports RAW capabilities.
146  """
147  return 3 in props.get('android.request.availableCapabilities', [])
148
149
150def sensor_fusion(props):
151  """Checks the camera and motion sensor timestamps.
152
153  Returns whether the camera and motion sensor timestamps for the device
154  are in the same time domain and can be compared directly.
155
156  Args:
157    props: Camera properties object.
158
159  Returns:
160     Boolean. True if camera and motion sensor timestamps in same time domain.
161  """
162  return props.get('android.sensor.info.timestampSource') == 1
163
164
165def burst_capture_capable(props):
166  """Returns whether a device supports burst capture.
167
168  Args:
169    props: Camera properties object.
170
171  Returns:
172    Boolean. True if the device supports burst capture.
173  """
174  return 6 in props.get('android.request.availableCapabilities', [])
175
176
177def logical_multi_camera(props):
178  """Returns whether a device is a logical multi-camera.
179
180  Args:
181    props: Camera properties object.
182
183  Returns:
184     Boolean. True if the device is a logical multi-camera.
185  """
186  return 11 in props.get('android.request.availableCapabilities', [])
187
188
189def logical_multi_camera_physical_ids(props):
190  """Returns a logical multi-camera's underlying physical cameras.
191
192  Args:
193    props: Camera properties object.
194
195  Returns:
196    list of physical cameras backing the logical multi-camera.
197  """
198  physical_ids_list = []
199  if logical_multi_camera(props):
200    physical_ids_list = props['camera.characteristics.physicalCamIds']
201  return physical_ids_list
202
203
204def skip_unless(cond, msg=None):
205  """Skips the test if the condition is false.
206
207  If a test is skipped, then it is exited and returns the special code
208  of 101 to the calling shell, which can be used by an external test
209  harness to differentiate a skip from a pass or fail.
210
211  Args:
212    cond: Boolean, which must be true for the test to not skip.
213    msg: String, reason for test to skip
214
215  Returns:
216     Nothing.
217  """
218  if not cond:
219    skip_msg = SKIP_RET_MSG if not msg else f'{SKIP_RET_MSG}: {msg}'
220    asserts.skip(skip_msg)
221
222
223def backward_compatible(props):
224  """Returns whether a device supports BACKWARD_COMPATIBLE.
225
226  Args:
227    props: Camera properties object.
228
229  Returns:
230    Boolean. True if the devices supports BACKWARD_COMPATIBLE.
231  """
232  return 0 in props.get('android.request.availableCapabilities', [])
233
234
235def lens_calibrated(props):
236  """Returns whether lens position is calibrated or not.
237
238  android.lens.info.focusDistanceCalibration has 3 modes.
239  0: Uncalibrated
240  1: Approximate
241  2: Calibrated
242
243  Args:
244    props: Camera properties objects.
245
246  Returns:
247    Boolean. True if lens is CALIBRATED.
248  """
249  return 'android.lens.info.focusDistanceCalibration' in props and props[
250      'android.lens.info.focusDistanceCalibration'] == 2
251
252
253def lens_approx_calibrated(props):
254  """Returns whether lens position is calibrated or not.
255
256  android.lens.info.focusDistanceCalibration has 3 modes.
257  0: Uncalibrated
258  1: Approximate
259  2: Calibrated
260
261  Args:
262   props: Camera properties objects.
263
264  Returns:
265    Boolean. True if lens is APPROXIMATE or CALIBRATED.
266  """
267  return props.get('android.lens.info.focusDistanceCalibration') in [1, 2]
268
269
270def raw10(props):
271  """Returns whether a device supports RAW10 capabilities.
272
273  Args:
274    props: Camera properties object.
275
276  Returns:
277    Boolean. True if device supports RAW10 capabilities.
278  """
279  if capture_request_utils.get_available_output_sizes('raw10', props):
280    return True
281  return False
282
283
284def raw12(props):
285  """Returns whether a device supports RAW12 capabilities.
286
287  Args:
288    props: Camera properties object.
289
290  Returns:
291    Boolean. True if device supports RAW12 capabilities.
292  """
293  if capture_request_utils.get_available_output_sizes('raw12', props):
294    return True
295  return False
296
297
298def raw16(props):
299  """Returns whether a device supports RAW16 output.
300
301  Args:
302    props: Camera properties object.
303
304  Returns:
305    Boolean. True if device supports RAW16 capabilities.
306  """
307  if capture_request_utils.get_available_output_sizes('raw', props):
308    return True
309  return False
310
311
312def raw_output(props):
313  """Returns whether a device supports any of the RAW output formats.
314
315  Args:
316    props: Camera properties object.
317
318  Returns:
319    Boolean. True if device supports any of the RAW output formats
320  """
321  return raw16(props) or raw10(props) or raw12(props)
322
323
324def per_frame_control(props):
325  """Returns whether a device supports per frame control.
326
327  Args:
328    props: Camera properties object.
329
330  Returns: Boolean. True if devices supports per frame control.
331  """
332  return 'android.sync.maxLatency' in props and props[
333      'android.sync.maxLatency'] == 0
334
335
336def mono_camera(props):
337  """Returns whether a device is monochromatic.
338
339  Args:
340    props: Camera properties object.
341  Returns: Boolean. True if MONO camera.
342  """
343  return 12 in props.get('android.request.availableCapabilities', [])
344
345
346def fixed_focus(props):
347  """Returns whether a device is fixed focus.
348
349  props[android.lens.info.minimumFocusDistance] == 0 is fixed focus
350
351  Args:
352    props: Camera properties objects.
353
354  Returns:
355    Boolean. True if device is a fixed focus camera.
356  """
357  return 'android.lens.info.minimumFocusDistance' in props and props[
358      'android.lens.info.minimumFocusDistance'] == 0
359
360
361def face_detect(props):
362  """Returns whether a device has face detection mode.
363
364  props['android.statistics.info.availableFaceDetectModes'] != 0
365
366  Args:
367    props: Camera properties objects.
368
369  Returns:
370    Boolean. True if device supports face detection.
371  """
372  return 'android.statistics.info.availableFaceDetectModes' in props and props[
373      'android.statistics.info.availableFaceDetectModes'] != [0]
374
375
376def read_3a(props):
377  """Return whether a device supports reading out the below 3A settings.
378
379  sensitivity
380  exposure time
381  awb gain
382  awb cct
383  focus distance
384
385  Args:
386    props: Camera properties object.
387
388  Returns:
389     Boolean. True if device supports reading out 3A settings.
390  """
391  return manual_sensor(props) and manual_post_proc(props)
392
393
394def compute_target_exposure(props):
395  """Return whether a device supports target exposure computation.
396
397  Args:
398    props: Camera properties object.
399
400  Returns:
401    Boolean. True if device supports target exposure computation.
402  """
403  return manual_sensor(props) and manual_post_proc(props)
404
405
406def y8(props):
407  """Returns whether a device supports Y8 output.
408
409  Args:
410    props: Camera properties object.
411
412  Returns:
413     Boolean. True if device suupports Y8 output.
414  """
415  if capture_request_utils.get_available_output_sizes('y8', props):
416    return True
417  return False
418
419
420def jpeg_quality(props):
421  """Returns whether a device supports JPEG quality."""
422  return ('camera.characteristics.requestKeys' in props) and (
423      'android.jpeg.quality' in props['camera.characteristics.requestKeys'])
424
425
426def jpeg_orientation(props):
427  """Returns whether a device supports JPEG orientation."""
428  return ('camera.characteristics.requestKeys' in props) and (
429      'android.jpeg.orientation' in props['camera.characteristics.requestKeys'])
430
431
432def sensor_orientation(props):
433  """Returns the sensor orientation of the camera."""
434  return props['android.sensor.orientation']
435
436
437def zoom_ratio_range(props):
438  """Returns whether a device supports zoom capabilities.
439
440  Args:
441    props: Camera properties object.
442
443  Returns:
444    Boolean. True if device supports zoom capabilities.
445  """
446  return 'android.control.zoomRatioRange' in props and props[
447      'android.control.zoomRatioRange'] is not None
448
449
450def low_latency_zoom(props):
451  """Returns whether a device supports low latency zoom via settings override.
452
453  Args:
454    props: Camera properties object.
455
456  Returns:
457    Boolean. True if device supports SETTINGS_OVERRIDE_ZOOM.
458  """
459  return ('android.control.availableSettingsOverrides') in props and (
460      SETTINGS_OVERRIDE_ZOOM in props[
461          'android.control.availableSettingsOverrides'])
462
463
464def sync_latency(props):
465  """Returns sync latency in number of frames.
466
467  If undefined, 8 frames.
468
469  Args:
470    props: Camera properties object.
471
472  Returns:
473    integer number of frames.
474  """
475  latency = props['android.sync.maxLatency']
476  if latency < 0:
477    latency = 8
478  return latency
479
480
481def get_max_digital_zoom(props):
482  """Returns the maximum amount of zooming possible by the camera device.
483
484  Args:
485    props: Camera properties object.
486
487  Returns:
488    A float indicating the maximum amount of zooming possible by the
489    camera device.
490  """
491  z_max = 1.0
492  if 'android.scaler.availableMaxDigitalZoom' in props:
493    z_max = props['android.scaler.availableMaxDigitalZoom']
494  return z_max
495
496
497def get_ae_target_fps_ranges(props):
498  """Returns the AE target FPS ranges supported by the camera device.
499
500  Args:
501    props: Camera properties object.
502
503  Returns:
504    A list of AE target FPS ranges supported by the camera device.
505  """
506  ranges = []  # return empty list instead of Boolean if no FPS range in props
507  if 'android.control.aeAvailableTargetFpsRanges' in props:
508    ranges = props['android.control.aeAvailableTargetFpsRanges']
509  return ranges
510
511
512def get_fps_range_to_test(fps_ranges):
513  """Returns an AE target FPS range to test based on camera device properties.
514
515  Args:
516    fps_ranges: list of AE target FPS ranges supported by camera.
517      e.g. [[7, 30], [24, 30], [30, 30]]
518  Returns:
519    An AE target FPS range for testing.
520  """
521  default_range_min, default_range_max = DEFAULT_AE_TARGET_FPS_RANGE
522  default_range_size = default_range_max - default_range_min
523  logging.debug('AE target FPS ranges: %s', fps_ranges)
524  widest_fps_range = max(fps_ranges, key=lambda r: r[1] - r[0])
525  if widest_fps_range[1] - widest_fps_range[0] < default_range_size:
526    logging.debug('Default range %s is wider than widest '
527                  'available AE target FPS range %s.',
528                  DEFAULT_AE_TARGET_FPS_RANGE,
529                  widest_fps_range)
530  logging.debug('Accepted AE target FPS range: %s', widest_fps_range)
531  return widest_fps_range
532
533
534def ae_lock(props):
535  """Returns whether a device supports AE lock.
536
537  Args:
538    props: Camera properties object.
539
540  Returns:
541    Boolean. True if device supports AE lock.
542  """
543  return 'android.control.aeLockAvailable' in props and props[
544      'android.control.aeLockAvailable'] == 1
545
546
547def awb_lock(props):
548  """Returns whether a device supports AWB lock.
549
550  Args:
551    props: Camera properties object.
552
553  Returns:
554    Boolean. True if device supports AWB lock.
555  """
556  return 'android.control.awbLockAvailable' in props and props[
557      'android.control.awbLockAvailable'] == 1
558
559
560def ev_compensation(props):
561  """Returns whether a device supports ev compensation.
562
563  Args:
564    props: Camera properties object.
565
566  Returns:
567    Boolean. True if device supports EV compensation.
568  """
569  return 'android.control.aeCompensationRange' in props and props[
570      'android.control.aeCompensationRange'] != [0, 0]
571
572
573def flash(props):
574  """Returns whether a device supports flash control.
575
576  Args:
577    props: Camera properties object.
578
579  Returns:
580    Boolean. True if device supports flash control.
581  """
582  return 'android.flash.info.available' in props and props[
583      'android.flash.info.available'] == 1
584
585
586def distortion_correction(props):
587  """Returns whether a device supports android.lens.distortion capabilities.
588
589  Args:
590    props: Camera properties object.
591
592  Returns:
593    Boolean. True if device supports lens distortion correction capabilities.
594  """
595  return 'android.lens.distortion' in props and props[
596      'android.lens.distortion'] is not None
597
598
599def distortion_correction_mode(props, mode):
600  """Returns whether a device supports a distortionCorrection mode.
601
602  Args:
603    props: Camera properties object
604    mode: Integer indicating distortion correction mode
605
606  Returns:
607    Boolean. True if device supports distortion correction mode(s).
608  """
609  if 'android.distortionCorrection.availableModes' in props:
610    logging.debug('distortionCorrection.availableModes: %s',
611                  props['android.distortionCorrection.availableModes'])
612  else:
613    logging.debug('distortionCorrection.availableModes not in props!')
614  return ('android.distortionCorrection.availableModes' in props and
615          mode in props['android.distortionCorrection.availableModes'])
616
617
618def freeform_crop(props):
619  """Returns whether a device supports freefrom cropping.
620
621  Args:
622    props: Camera properties object.
623
624  Returns:
625    Boolean. True if device supports freeform cropping.
626  """
627  return 'android.scaler.croppingType' in props and props[
628      'android.scaler.croppingType'] == 1
629
630
631def noise_reduction_mode(props, mode):
632  """Returns whether a device supports the noise reduction mode.
633
634  Args:
635    props: Camera properties objects.
636    mode: Integer indicating noise reduction mode to check for availability.
637
638  Returns:
639    Boolean. True if devices supports noise reduction mode(s).
640  """
641  return ('android.noiseReduction.availableNoiseReductionModes' in props and
642          mode in props['android.noiseReduction.availableNoiseReductionModes'])
643
644
645def lsc_map(props):
646  """Returns whether a device supports lens shading map output.
647
648  Args:
649    props: Camera properties object.
650  Returns: Boolean. True if device supports lens shading map output.
651  """
652  return 1 in props.get('android.statistics.info.availableLensShadingMapModes',
653                        [])
654
655
656def lsc_off(props):
657  """Returns whether a device supports disabling lens shading correction.
658
659  Args:
660    props: Camera properties object.
661
662  Returns:
663    Boolean. True if device supports disabling lens shading correction.
664  """
665  return 0 in props.get('android.shading.availableModes', [])
666
667
668def edge_mode(props, mode):
669  """Returns whether a device supports the edge mode.
670
671  Args:
672    props: Camera properties objects.
673    mode: Integer, indicating the edge mode to check for availability.
674
675  Returns:
676    Boolean. True if device supports edge mode(s).
677  """
678  return 'android.edge.availableEdgeModes' in props and mode in props[
679      'android.edge.availableEdgeModes']
680
681
682def tonemap_mode(props, mode):
683  """Returns whether a device supports the tonemap mode.
684
685  Args:
686    props: Camera properties object.
687    mode: Integer, indicating the tonemap mode to check for availability.
688
689  Return:
690    Boolean.
691  """
692  return 'android.tonemap.availableToneMapModes' in props and mode in props[
693      'android.tonemap.availableToneMapModes']
694
695
696def yuv_reprocess(props):
697  """Returns whether a device supports YUV reprocessing.
698
699  Args:
700    props: Camera properties object.
701
702  Returns:
703    Boolean. True if device supports YUV reprocessing.
704  """
705  return 'android.request.availableCapabilities' in props and 7 in props[
706      'android.request.availableCapabilities']
707
708
709def private_reprocess(props):
710  """Returns whether a device supports PRIVATE reprocessing.
711
712  Args:
713    props: Camera properties object.
714
715  Returns:
716    Boolean. True if device supports PRIVATE reprocessing.
717  """
718  return 'android.request.availableCapabilities' in props and 4 in props[
719      'android.request.availableCapabilities']
720
721
722def stream_use_case(props):
723  """Returns whether a device has stream use case capability.
724
725  Args:
726    props: Camera properties object.
727
728  Returns:
729     Boolean. True if the device has stream use case capability.
730  """
731  return 'android.request.availableCapabilities' in props and 19 in props[
732      'android.request.availableCapabilities']
733
734
735def cropped_raw_stream_use_case(props):
736  """Returns whether a device supports the CROPPED_RAW stream use case.
737
738  Args:
739    props: Camera properties object.
740
741  Returns:
742     Boolean. True if the device supports the CROPPED_RAW stream use case.
743  """
744  return stream_use_case(props) and 6 in props[
745      'android.scaler.availableStreamUseCases']
746
747
748def dynamic_range_ten_bit(props):
749  """Returns whether a device supports the DYNAMIC_RANGE_TEN_BIT capability.
750
751  Args:
752    props: Camera properties object.
753
754  Returns:
755     Boolean. True if the device supports the DYNAMIC_RANGE_TEN_BIT capability.
756  """
757  return 'android.request.availableCapabilities' in props and 18 in props[
758      'android.request.availableCapabilities']
759
760
761def intrinsic_calibration(props):
762  """Returns whether a device supports android.lens.intrinsicCalibration.
763
764  Args:
765    props: Camera properties object.
766
767  Returns:
768    Boolean. True if device supports android.lens.intrinsicCalibratino.
769  """
770  return props.get('android.lens.intrinsicCalibration') is not None
771
772
773def get_intrinsic_calibration(props, metadata, debug, fd=None):
774  """Get intrinsicCalibration and create intrisic matrix.
775
776  If intrinsic android.lens.intrinsicCalibration does not exist, return None.
777
778  Args:
779    props: camera properties.
780    metadata: dict; camera capture metadata.
781    debug: boolean; enable printing more information.
782    fd: float; focal length from capture metadata.
783
784  Returns:
785    numpy array for intrinsic transformation matrix or None
786    k = [[f_x, s, c_x],
787         [0, f_y, c_y],
788         [0,   0,   1]]
789  """
790  if metadata.get('android.lens.intrinsicCalibration'):
791    ical = np.array(metadata['android.lens.intrinsicCalibration'])
792    logging.debug('Using capture metadata android.lens.intrinsicCalibration')
793  elif props.get('android.lens.intrinsicCalibration'):
794    ical = np.array(props['android.lens.intrinsicCalibration'])
795    logging.debug('Using camera property android.lens.intrinsicCalibration')
796  else:
797    logging.error('Camera does not have android.lens.intrinsicCalibration.')
798    return None
799
800  # basic checks for parameter correctness
801  ical_len = len(ical)
802  if ical_len != NUM_INTRINSIC_CAL_PARAMS:
803    raise ValueError(
804        f'instrisicCalibration has wrong number of params: {ical_len}.')
805
806  if fd is not None:
807    # detailed checks for parameter correctness
808    # Intrinsic cal is of format: [f_x, f_y, c_x, c_y, s]
809    # [f_x, f_y] is the horizontal and vertical focal lengths,
810    # [c_x, c_y] is the position of the optical axis,
811    # and s is skew of sensor plane vs lens plane.
812    sensor_h = props['android.sensor.info.physicalSize']['height']
813    sensor_w = props['android.sensor.info.physicalSize']['width']
814    pixel_h = props['android.sensor.info.pixelArraySize']['height']
815    pixel_w = props['android.sensor.info.pixelArraySize']['width']
816    fd_w_pix = pixel_w * fd / sensor_w
817    fd_h_pix = pixel_h * fd / sensor_h
818
819    if not math.isclose(fd_w_pix, ical[0], rel_tol=FD_CAL_RTOL):
820      raise ValueError(f'fd_w(pixels): {fd_w_pix:.2f}\tcal[0](pixels): '
821                       f'{ical[0]:.2f}\tTOL=20%')
822    if not math.isclose(fd_h_pix, ical[1], rel_tol=FD_CAL_RTOL):
823      raise ValueError(f'fd_h(pixels): {fd_h_pix:.2f}\tcal[1](pixels): '
824                       f'{ical[1]:.2f}\tTOL=20%')
825
826  # generate instrinsic matrix
827  k = np.array([[ical[0], ical[4], ical[2]],
828                [0, ical[1], ical[3]],
829                [0, 0, 1]])
830  if debug:
831    logging.debug('k: %s', str(k))
832  return k
833
834
835def get_translation_matrix(props, debug):
836  """Get translation matrix.
837
838  Args:
839    props: dict of camera properties
840    debug: boolean flag to log more info
841
842  Returns:
843    android.lens.poseTranslation matrix if it exists, otherwise None.
844  """
845  if props['android.lens.poseTranslation']:
846    t = np.array(props['android.lens.poseTranslation'])
847  else:
848    logging.error('Device does not have android.lens.poseTranslation.')
849    return None
850
851  if debug:
852    logging.debug('translation: %s', str(t))
853  t_len = len(t)
854  if t_len != NUM_POSE_TRANSLATION_PARAMS:
855    raise ValueError(f'poseTranslation has wrong # of params: {t_len}.')
856  return t
857
858
859def get_rotation_matrix(props, debug):
860  """Convert the rotation parameters to 3-axis data.
861
862  Args:
863    props: camera properties
864    debug: boolean for more information
865
866  Returns:
867    3x3 matrix w/ rotation parameters if poseRotation exists, otherwise None
868  """
869  if props['android.lens.poseRotation']:
870    rotation = np.array(props['android.lens.poseRotation'])
871  else:
872    logging.error('Device does not have android.lens.poseRotation.')
873    return None
874
875  if debug:
876    logging.debug('rotation: %s', str(rotation))
877    rotation_len = len(rotation)
878    if rotation_len != NUM_POSE_ROTATION_PARAMS:
879      raise ValueError(f'poseRotation has wrong # of params: {rotation_len}.')
880  x = rotation[0]
881  y = rotation[1]
882  z = rotation[2]
883  w = rotation[3]
884  return np.array([[1-2*y**2-2*z**2, 2*x*y-2*z*w, 2*x*z+2*y*w],
885                   [2*x*y+2*z*w, 1-2*x**2-2*z**2, 2*y*z-2*x*w],
886                   [2*x*z-2*y*w, 2*y*z+2*x*w, 1-2*x**2-2*y**2]])
887
888
889def get_distortion_matrix(props):
890  """Get android.lens.distortion matrix and convert to cv2 fmt.
891
892  Args:
893    props: dict of camera properties
894
895  Returns:
896    cv2 reordered android.lens.distortion if it exists, otherwise None.
897  """
898  if props['android.lens.distortion']:
899    dist = np.array(props['android.lens.distortion'])
900  else:
901    logging.error('Device does not have android.lens.distortion.')
902    return None
903
904  dist_len = len(dist)
905  if len(dist) != NUM_DISTORTION_PARAMS:
906    raise ValueError(f'lens.distortion has wrong # of params: {dist_len}.')
907  cv2_distort = np.array([dist[0], dist[1], dist[3], dist[4], dist[2]])
908  logging.debug('cv2 distortion params: %s', str(cv2_distort))
909  return cv2_distort
910
911
912def post_raw_sensitivity_boost(props):
913  """Returns whether a device supports post RAW sensitivity boost.
914
915  Args:
916    props: Camera properties object.
917
918  Returns:
919    Boolean. True if android.control.postRawSensitivityBoost is supported.
920  """
921  return (
922      'android.control.postRawSensitivityBoostRange' in
923      props['camera.characteristics.keys'] and
924      props.get('android.control.postRawSensitivityBoostRange') != [100, 100])
925
926
927def sensor_fusion_capable(props):
928  """Determine if test_sensor_fusion is run."""
929  return all([sensor_fusion(props),
930              manual_sensor(props),
931              props['android.lens.facing'] != LENS_FACING['EXTERNAL']])
932
933
934def continuous_picture(props):
935  """Returns whether a device supports CONTINUOUS_PICTURE.
936
937  Args:
938    props: Camera properties object.
939
940  Returns:
941    Boolean. True if CONTINUOUS_PICTURE in android.control.afAvailableModes.
942  """
943  return 4 in props.get('android.control.afAvailableModes', [])
944
945
946def af_scene_change(props):
947  """Returns whether a device supports AF_SCENE_CHANGE.
948
949  Args:
950    props: Camera properties object.
951
952  Returns:
953    Boolean. True if android.control.afSceneChange supported.
954  """
955  return 'android.control.afSceneChange' in props.get(
956      'camera.characteristics.resultKeys')
957
958
959def multi_camera_frame_sync_capable(props):
960  """Determines if test_multi_camera_frame_sync can be run."""
961  return all([
962      read_3a(props),
963      per_frame_control(props),
964      logical_multi_camera(props),
965      sensor_fusion(props),
966  ])
967
968
969def multi_camera_sync_calibrated(props):
970  """Determines if multi-camera sync type is CALIBRATED or APPROXIMATE.
971
972  Args:
973    props: Camera properties object.
974
975  Returns:
976    Boolean. True if android.logicalMultiCamera.sensorSyncType is CALIBRATED.
977  """
978  return props.get('android.logicalMultiCamera.sensorSyncType'
979                  ) == MULTI_CAMERA_SYNC_CALIBRATED
980
981
982def solid_color_test_pattern(props):
983  """Determines if camera supports solid color test pattern.
984
985  Args:
986    props: Camera properties object.
987
988  Returns:
989    Boolean. True if android.sensor.availableTestPatternModes has
990             SOLID_COLOR_TEST_PATTERN.
991  """
992  return SOLID_COLOR_TEST_PATTERN in props.get(
993      'android.sensor.availableTestPatternModes')
994
995
996def color_bars_test_pattern(props):
997  """Determines if camera supports color bars test pattern.
998
999  Args:
1000    props: Camera properties object.
1001
1002  Returns:
1003    Boolean. True if android.sensor.availableTestPatternModes has
1004             COLOR_BARS_TEST_PATTERN.
1005  """
1006  return COLOR_BARS_TEST_PATTERN in props.get(
1007      'android.sensor.availableTestPatternModes')
1008
1009
1010def linear_tonemap(props):
1011  """Determines if camera supports CONTRAST_CURVE or GAMMA_VALUE in tonemap.
1012
1013  Args:
1014    props: Camera properties object.
1015
1016  Returns:
1017    Boolean. True if android.tonemap.availableToneMapModes has
1018             CONTRAST_CURVE (0) or GAMMA_VALUE (3).
1019  """
1020  return ('android.tonemap.availableToneMapModes' in props and
1021          (0 in props.get('android.tonemap.availableToneMapModes') or
1022           3 in props.get('android.tonemap.availableToneMapModes')))
1023
1024
1025def get_reprocess_formats(props):
1026  """Retrieve the list of supported reprocess formats.
1027
1028  Args:
1029    props: The camera properties.
1030
1031  Returns:
1032    A list of supported reprocess formats.
1033  """
1034  reprocess_formats = []
1035  if yuv_reprocess(props):
1036    reprocess_formats.append('yuv')
1037  if private_reprocess(props):
1038    reprocess_formats.append('private')
1039  return reprocess_formats
1040
1041
1042def color_space_to_int(color_space):
1043  """Returns the integer ordinal of a named color space.
1044
1045  Args:
1046    color_space: The color space string.
1047
1048  Returns:
1049    Int. Ordinal of the color space.
1050  """
1051  if color_space == 'UNSPECIFIED':
1052    return -1
1053
1054  return COLOR_SPACES.index(color_space)
1055
1056
1057def autoframing(props):
1058  """Returns whether a device supports autoframing.
1059
1060  Args:
1061    props: Camera properties object.
1062
1063  Returns:
1064    Boolean. True if android.control.autoframing is supported.
1065  """
1066  return 'android.control.autoframingAvailable' in props and props[
1067      'android.control.autoframingAvailable'] == 1
1068
1069
1070def ae_regions(props):
1071  """Returns whether a device supports CONTROL_AE_REGIONS.
1072
1073  Args:
1074    props: Camera properties object.
1075
1076  Returns:
1077    Boolean. True if android.control.aeRegions is supported.
1078  """
1079  return 'android.control.maxRegionsAe' in props and props[
1080      'android.control.maxRegionsAe'] != 0
1081
1082
1083def awb_regions(props):
1084  """Returns whether a device supports CONTROL_AWB_REGIONS.
1085
1086  Args:
1087    props: Camera properties object.
1088
1089  Returns:
1090    Boolean. True if android.control.awbRegions is supported.
1091  """
1092  return 'android.control.maxRegionsAwb' in props and props[
1093      'android.control.maxRegionsAwb'] != 0
1094
1095
1096def preview_stabilization_supported(props):
1097  """Returns whether preview stabilization is supported.
1098
1099  Args:
1100    props: Camera properties object.
1101
1102  Returns:
1103    Boolean. True if preview stabilization is supported.
1104  """
1105  supported_stabilization_modes = props[
1106      'android.control.availableVideoStabilizationModes'
1107  ]
1108  supported = (
1109      supported_stabilization_modes is not None and
1110      STABILIZATION_MODE_PREVIEW in supported_stabilization_modes
1111  )
1112  return supported
1113
1114
1115def optical_stabilization_supported(props):
1116  """Returns whether optical image stabilization is supported.
1117
1118  Args:
1119    props: Camera properties object.
1120
1121  Returns:
1122    Boolean. True if optical image stabilization is supported.
1123  """
1124  optical_stabilization_modes = props[
1125      'android.lens.info.availableOpticalStabilization'
1126    ]
1127  logging.debug('optical_stabilization_modes = %s',
1128                str(optical_stabilization_modes))
1129
1130  # Check if OIS supported
1131  ois_supported = (optical_stabilization_modes is not None and
1132                   LENS_OPTICAL_STABILIZATION_MODE_ON in
1133                   optical_stabilization_modes)
1134  return ois_supported
1135