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