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