vkit.mechanism.distortion.interface
1# Copyright 2022 vkit-x Administrator. All Rights Reserved. 2# 3# This project (vkit-x/vkit) is dual-licensed under commercial and SSPL licenses. 4# 5# The commercial license gives you the full rights to create and distribute software 6# on your own terms without any SSPL license obligations. For more information, 7# please see the "LICENSE_COMMERCIAL.txt" file. 8# 9# This project is also available under Server Side Public License (SSPL). 10# The SSPL licensing is ideal for use cases such as open source projects with 11# SSPL distribution, student/academic purposes, hobby projects, internal research 12# projects without external distribution, or other projects where all SSPL 13# obligations can be met. For more information, please see the "LICENSE_SSPL.txt" file. 14from typing import ( 15 cast, 16 get_origin, 17 Any, 18 Callable, 19 Mapping, 20 Generic, 21 Iterable, 22 Type, 23 TypeVar, 24 Union, 25 Sequence, 26 Tuple, 27 Optional, 28) 29 30import attrs 31from numpy.random import default_rng, Generator as RandomGenerator 32 33from vkit.element import ( 34 Shapable, 35 Image, 36 Point, 37 PointList, 38 PointTuple, 39 Polygon, 40 Mask, 41 ScoreMap, 42) 43from vkit.utility import ( 44 dyn_structure, 45 get_config_class_snake_case_name, 46) 47 48 49class DistortionConfig: 50 51 _cached_name: str = '' 52 53 @classmethod 54 def get_name(cls): 55 if not cls._cached_name: 56 cls._cached_name = get_config_class_snake_case_name(cls.__name__) 57 return cls._cached_name 58 59 @property 60 def name(self): 61 return self.get_name() 62 63 @property 64 def supports_rng_state(self) -> bool: 65 return False 66 67 @property 68 def rng_state(self) -> Optional[Mapping[str, Any]]: 69 return None 70 71 @rng_state.setter 72 def rng_state(self, val: Mapping[str, Any]): 73 pass 74 75 76_T_CONFIG = TypeVar('_T_CONFIG', bound=DistortionConfig) 77 78 79class DistortionState(Generic[_T_CONFIG]): 80 81 def __init__( 82 self, 83 config: _T_CONFIG, 84 shape: Tuple[int, int], 85 rng: Optional[RandomGenerator], 86 ): 87 raise NotImplementedError() 88 89 @property 90 def result_shape(self) -> Optional[Tuple[int, int]]: 91 return None 92 93 94class DistortionNopState(DistortionState[_T_CONFIG]): 95 96 def __init__( 97 self, 98 config: _T_CONFIG, 99 shape: Tuple[int, int], 100 rng: Optional[RandomGenerator], 101 ): 102 raise NotImplementedError() 103 104 105_T_STATE = TypeVar('_T_STATE', bound=DistortionState) 106 107 108@attrs.define 109class DistortionResult: 110 shape: Tuple[int, int] 111 image: Optional[Image] = None 112 mask: Optional[Mask] = None 113 score_map: Optional[ScoreMap] = None 114 active_mask: Optional[Mask] = None 115 point: Optional[Point] = None 116 points: Optional[PointTuple] = None 117 corner_points: Optional[PointTuple] = None 118 polygon: Optional[Polygon] = None 119 polygons: Optional[Sequence[Polygon]] = None 120 config: Optional[Any] = None 121 state: Optional[Any] = None 122 meta: Optional[Mapping[str, Any]] = None 123 124 125@attrs.define 126class DistortionInternals(Generic[_T_CONFIG, _T_STATE]): 127 config: _T_CONFIG 128 state: Optional[_T_STATE] 129 shape: Tuple[int, int] 130 rng: Optional[RandomGenerator] 131 132 def restore_rng_if_supported(self): 133 if self.rng: 134 assert self.config.supports_rng_state and self.config.rng_state 135 self.rng.bit_generator.state = self.config.rng_state 136 137 138class Distortion(Generic[_T_CONFIG, _T_STATE]): 139 140 # yapf: disable 141 def __init__( 142 self, 143 config_cls: Type[_T_CONFIG], 144 state_cls: Type[_T_STATE], 145 func_image: Callable[ 146 [ 147 _T_CONFIG, 148 Optional[_T_STATE], 149 Image, 150 Optional[RandomGenerator], 151 ], 152 Image, 153 ], 154 func_mask: Optional[ 155 Callable[ 156 [ 157 _T_CONFIG, 158 Optional[_T_STATE], 159 Mask, 160 Optional[RandomGenerator], 161 ], 162 Mask, 163 ] 164 ] = None, 165 func_score_map: Optional[ 166 Callable[ 167 [ 168 _T_CONFIG, 169 Optional[_T_STATE], 170 ScoreMap, 171 Optional[RandomGenerator], 172 ], 173 ScoreMap, 174 ] 175 ] = None, 176 func_active_mask: Optional[ 177 Callable[ 178 [ 179 _T_CONFIG, 180 Optional[_T_STATE], 181 Tuple[int, int], 182 Optional[RandomGenerator], 183 ], 184 Mask, 185 ] 186 ] = None, 187 func_point: Optional[ 188 Callable[ 189 [ 190 _T_CONFIG, 191 Optional[_T_STATE], 192 Tuple[int, int], 193 Point, 194 Optional[RandomGenerator], 195 ], 196 Point, 197 ] 198 ] = None, 199 func_points: Optional[ 200 Callable[ 201 [ 202 _T_CONFIG, 203 Optional[_T_STATE], 204 Tuple[int, int], 205 Union[PointList, PointTuple, Iterable[Point]], 206 Optional[RandomGenerator], 207 ], 208 PointTuple, 209 ] 210 ] = None, 211 func_polygon: Optional[ 212 Callable[ 213 [ 214 _T_CONFIG, 215 Optional[_T_STATE], 216 Tuple[int, int], 217 Polygon, 218 Optional[RandomGenerator], 219 ], 220 Polygon, 221 ] 222 ] = None, 223 func_polygons: Optional[ 224 Callable[ 225 [ 226 _T_CONFIG, 227 Optional[_T_STATE], 228 Tuple[int, int], 229 Iterable[Polygon], 230 Optional[RandomGenerator], 231 ], 232 Sequence[Polygon], 233 ] 234 ] = None, 235 ): 236 # yapf: enable 237 self.config_cls = config_cls 238 self.state_cls = state_cls 239 240 self.func_image = func_image 241 self.func_score_map = func_score_map 242 self.func_mask = func_mask 243 self.func_active_mask = func_active_mask 244 245 self.func_point = func_point 246 self.func_points = func_points 247 self.func_polygon = func_polygon 248 self.func_polygons = func_polygons 249 250 @property 251 def is_geometric(self): 252 return any(( 253 self.func_point, 254 self.func_points, 255 self.func_polygon, 256 self.func_polygons, 257 self.func_active_mask, 258 )) 259 260 # yapf: disable 261 def prepare_config_and_rng( 262 self, 263 config_or_config_generator: Union[ 264 Union[_T_CONFIG, Mapping[str, Any]], 265 Callable[ 266 [Tuple[int, int], RandomGenerator], 267 Union[_T_CONFIG, Mapping[str, Any]], 268 ], 269 ], 270 shape: Tuple[int, int], 271 rng: Optional[RandomGenerator], 272 ) -> Tuple[_T_CONFIG, Optional[RandomGenerator]]: 273 # yapf: enable 274 if callable(config_or_config_generator): 275 config_generator = cast( 276 Callable[[Tuple[int, int], RandomGenerator], Union[_T_CONFIG, Mapping[str, Any]]], 277 config_or_config_generator, 278 ) 279 if not rng: 280 raise RuntimeError('config_generator but rng is None.') 281 config = dyn_structure(config_generator(shape, rng), self.config_cls) 282 283 else: 284 config_or_config_generator = cast( 285 Union[_T_CONFIG, Mapping[str, Any]], 286 config_or_config_generator, 287 ) 288 config = dyn_structure(config_or_config_generator, self.config_cls) 289 290 if config.supports_rng_state: 291 if not config.rng_state: 292 if not rng: 293 raise RuntimeError('both config.rng_state and rng are None.') 294 config.rng_state = rng.bit_generator.state 295 # Move forward rng state to randomize the next run. 296 rng.random() 297 298 # Calling rng methods changes rng's state. We don't want to change the state 299 # of exterior rng, hence making a copy here. 300 rng = default_rng() 301 rng.bit_generator.state = config.rng_state 302 303 else: 304 # Force not passing rng. 305 rng = None 306 307 return config, rng 308 309 @classmethod 310 def get_shape_from_shapable_or_shape(cls, shapable_or_shape: Union[Shapable, Tuple[int, int]]): 311 if isinstance(shapable_or_shape, (list, tuple)): 312 assert len(shapable_or_shape) == 2 313 return shapable_or_shape 314 else: 315 return shapable_or_shape.shape 316 317 # yapf: disable 318 def prepare_internals( 319 self, 320 config_or_config_generator: Union[ 321 Union[_T_CONFIG, Mapping[str, Any]], 322 Callable[ 323 [Tuple[int, int], RandomGenerator], 324 Union[_T_CONFIG, Mapping[str, Any]], 325 ], 326 ], 327 state: Optional[_T_STATE], 328 shapable_or_shape: Union[Shapable, Tuple[int, int]], 329 rng: Optional[RandomGenerator] = None, 330 disable_state_initialization: bool = False, 331 ): 332 # yapf: enable 333 shape = self.get_shape_from_shapable_or_shape(shapable_or_shape) 334 335 config, rng = self.prepare_config_and_rng( 336 config_or_config_generator, 337 shape, 338 rng, 339 ) 340 341 if get_origin(self.state_cls) is not DistortionNopState: 342 if state is None and not disable_state_initialization: 343 state = self.state_cls(config, shape, rng) 344 else: 345 state = None 346 347 return DistortionInternals(config, state, shape, rng) 348 349 # yapf: disable 350 def generate_config_and_state( 351 self, 352 config_or_config_generator: Union[ 353 Union[_T_CONFIG, Mapping[str, Any]], 354 Callable[ 355 [Tuple[int, int], RandomGenerator], 356 Union[_T_CONFIG, Mapping[str, Any]], 357 ], 358 ], 359 state: Optional[_T_STATE], 360 shapable_or_shape: Union[Shapable, Tuple[int, int]], 361 rng: Optional[RandomGenerator] = None, 362 ): 363 # yapf: enable 364 internals = self.prepare_internals( 365 config_or_config_generator=config_or_config_generator, 366 state=state, 367 shapable_or_shape=shapable_or_shape, 368 rng=rng, 369 ) 370 return internals.config, internals.state 371 372 # yapf: disable 373 def generate_config( 374 self, 375 config_or_config_generator: Union[ 376 Union[_T_CONFIG, Mapping[str, Any]], 377 Callable[ 378 [Tuple[int, int], RandomGenerator], 379 Union[_T_CONFIG, Mapping[str, Any]], 380 ], 381 ], 382 shapable_or_shape: Union[Shapable, Tuple[int, int]], 383 rng: Optional[RandomGenerator] = None, 384 ): 385 # yapf: enable 386 internals = self.prepare_internals( 387 config_or_config_generator=config_or_config_generator, 388 state=None, 389 shapable_or_shape=shapable_or_shape, 390 rng=rng, 391 disable_state_initialization=True, 392 ) 393 return internals.config 394 395 # yapf: disable 396 def generate_state( 397 self, 398 config_or_config_generator: Union[ 399 Union[_T_CONFIG, Mapping[str, Any]], 400 Callable[ 401 [Tuple[int, int], RandomGenerator], 402 Union[_T_CONFIG, Mapping[str, Any]], 403 ], 404 ], 405 shapable_or_shape: Union[Shapable, Tuple[int, int]], 406 rng: Optional[RandomGenerator] = None, 407 ): 408 # yapf: enable 409 internals = self.prepare_internals( 410 config_or_config_generator=config_or_config_generator, 411 state=None, 412 shapable_or_shape=shapable_or_shape, 413 rng=rng, 414 ) 415 return internals.state 416 417 def distort_image_based_on_internals( 418 self, 419 internals: DistortionInternals[_T_CONFIG, _T_STATE], 420 image: Image, 421 ): 422 internals.restore_rng_if_supported() 423 424 return self.func_image( 425 internals.config, 426 internals.state, 427 image, 428 internals.rng, 429 ) 430 431 # yapf: disable 432 def distort_image( 433 self, 434 config_or_config_generator: Union[ 435 Union[_T_CONFIG, Mapping[str, Any]], 436 Callable[ 437 [Tuple[int, int], RandomGenerator], 438 Union[_T_CONFIG, Mapping[str, Any]], 439 ], 440 ], 441 image: Image, 442 state: Optional[_T_STATE] = None, 443 rng: Optional[RandomGenerator] = None, 444 ): 445 # yapf: enable 446 internals = self.prepare_internals( 447 config_or_config_generator=config_or_config_generator, 448 state=state, 449 shapable_or_shape=image, 450 rng=rng, 451 ) 452 return self.distort_image_based_on_internals(internals, image) 453 454 def distort_score_map_based_on_internals( 455 self, 456 internals: DistortionInternals[_T_CONFIG, _T_STATE], 457 score_map: ScoreMap, 458 ): 459 internals.restore_rng_if_supported() 460 461 if self.func_score_map: 462 return self.func_score_map( 463 internals.config, 464 internals.state, 465 score_map, 466 internals.rng, 467 ) 468 469 else: 470 # NOP. 471 return score_map 472 473 # yapf: disable 474 def distort_score_map( 475 self, 476 config_or_config_generator: Union[ 477 Union[_T_CONFIG, Mapping[str, Any]], 478 Callable[ 479 [Tuple[int, int], RandomGenerator], 480 Union[_T_CONFIG, Mapping[str, Any]], 481 ], 482 ], 483 score_map: ScoreMap, 484 state: Optional[_T_STATE] = None, 485 rng: Optional[RandomGenerator] = None, 486 ): 487 # yapf: enable 488 internals = self.prepare_internals( 489 config_or_config_generator=config_or_config_generator, 490 state=state, 491 shapable_or_shape=score_map, 492 rng=rng, 493 ) 494 return self.distort_score_map_based_on_internals(internals, score_map) 495 496 def distort_mask_based_on_internals( 497 self, 498 internals: DistortionInternals[_T_CONFIG, _T_STATE], 499 mask: Mask, 500 ): 501 internals.restore_rng_if_supported() 502 503 if self.func_mask: 504 return self.func_mask( 505 internals.config, 506 internals.state, 507 mask, 508 internals.rng, 509 ) 510 511 else: 512 # NOP. 513 return mask 514 515 # yapf: disable 516 def distort_mask( 517 self, 518 config_or_config_generator: Union[ 519 Union[_T_CONFIG, Mapping[str, Any]], 520 Callable[ 521 [Tuple[int, int], RandomGenerator], 522 Union[_T_CONFIG, Mapping[str, Any]], 523 ], 524 ], 525 mask: Mask, 526 state: Optional[_T_STATE] = None, 527 rng: Optional[RandomGenerator] = None, 528 ): 529 # yapf: enable 530 internals = self.prepare_internals( 531 config_or_config_generator=config_or_config_generator, 532 state=state, 533 shapable_or_shape=mask, 534 rng=rng, 535 ) 536 return self.distort_mask_based_on_internals(internals, mask) 537 538 def get_active_mask_based_on_internals( 539 self, 540 internals: DistortionInternals[_T_CONFIG, _T_STATE], 541 ): 542 # TODO: Something is wrong with cv.remap when dealing with border interpolation. 543 # This method could generate a mask "connects" to the transformed border. 544 internals.restore_rng_if_supported() 545 546 if self.func_active_mask: 547 return self.func_active_mask( 548 internals.config, 549 internals.state, 550 internals.shape, 551 internals.rng, 552 ) 553 554 else: 555 mask = Mask.from_shape(internals.shape, value=1) 556 return self.distort_mask_based_on_internals(internals, mask) 557 558 # yapf: disable 559 def get_active_mask( 560 self, 561 config_or_config_generator: Union[ 562 Union[_T_CONFIG, Mapping[str, Any]], 563 Callable[ 564 [Tuple[int, int], RandomGenerator], 565 Union[_T_CONFIG, Mapping[str, Any]], 566 ], 567 ], 568 shapable_or_shape: Union[Shapable, Tuple[int, int]], 569 state: Optional[_T_STATE] = None, 570 rng: Optional[RandomGenerator] = None, 571 ): 572 # yapf: enable 573 internals = self.prepare_internals( 574 config_or_config_generator=config_or_config_generator, 575 state=state, 576 shapable_or_shape=shapable_or_shape, 577 rng=rng, 578 ) 579 return self.get_active_mask_based_on_internals(internals) 580 581 def distort_point_based_on_internals( 582 self, 583 internals: DistortionInternals[_T_CONFIG, _T_STATE], 584 point: Point, 585 ): 586 internals.restore_rng_if_supported() 587 588 if self.func_point: 589 return self.func_point( 590 internals.config, 591 internals.state, 592 internals.shape, 593 point, 594 internals.rng, 595 ) 596 597 elif self.func_points: 598 distorted_points = self.func_points( 599 internals.config, 600 internals.state, 601 internals.shape, 602 [point], 603 internals.rng, 604 ) 605 return distorted_points[0] 606 607 else: 608 if self.is_geometric: 609 raise RuntimeError('Missing self.func_points or self.func_point.') 610 611 # NOP. 612 return point 613 614 # yapf: disable 615 def distort_point( 616 self, 617 config_or_config_generator: Union[ 618 Union[_T_CONFIG, Mapping[str, Any]], 619 Callable[ 620 [Tuple[int, int], RandomGenerator], 621 Union[_T_CONFIG, Mapping[str, Any]], 622 ], 623 ], 624 shapable_or_shape: Union[Shapable, Tuple[int, int]], 625 point: Point, 626 state: Optional[_T_STATE] = None, 627 rng: Optional[RandomGenerator] = None, 628 ): 629 # yapf: enable 630 internals = self.prepare_internals( 631 config_or_config_generator=config_or_config_generator, 632 state=state, 633 shapable_or_shape=shapable_or_shape, 634 rng=rng, 635 ) 636 return self.distort_point_based_on_internals(internals, point) 637 638 def distort_points_based_on_internals( 639 self, 640 internals: DistortionInternals[_T_CONFIG, _T_STATE], 641 points: Union[PointList, PointTuple, Iterable[Point]], 642 ): 643 internals.restore_rng_if_supported() 644 645 points = PointList(points) 646 647 if self.func_points: 648 return self.func_points( 649 internals.config, 650 internals.state, 651 internals.shape, 652 points, 653 internals.rng, 654 ) 655 656 else: 657 new_points = PointList() 658 for point in points: 659 new_point = self.distort_point_based_on_internals(internals, point) 660 new_points.append(new_point) 661 return new_points.to_point_tuple() 662 663 # yapf: disable 664 def distort_points( 665 self, 666 config_or_config_generator: Union[ 667 Union[_T_CONFIG, Mapping[str, Any]], 668 Callable[ 669 [Tuple[int, int], RandomGenerator], 670 Union[_T_CONFIG, Mapping[str, Any]], 671 ], 672 ], 673 shapable_or_shape: Union[Shapable, Tuple[int, int]], 674 points: Union[PointList, PointTuple, Iterable[Point]], 675 state: Optional[_T_STATE] = None, 676 rng: Optional[RandomGenerator] = None, 677 ): 678 # yapf: enable 679 internals = self.prepare_internals( 680 config_or_config_generator=config_or_config_generator, 681 state=state, 682 shapable_or_shape=shapable_or_shape, 683 rng=rng, 684 ) 685 return self.distort_points_based_on_internals(internals, points) 686 687 def distort_polygon_based_on_internals( 688 self, 689 internals: DistortionInternals[_T_CONFIG, _T_STATE], 690 polygon: Polygon, 691 ): 692 internals.restore_rng_if_supported() 693 694 if self.func_polygon: 695 return self.func_polygon( 696 internals.config, 697 internals.state, 698 internals.shape, 699 polygon, 700 internals.rng, 701 ) 702 703 elif self.func_polygons: 704 distorted_polygons = self.func_polygons( 705 internals.config, 706 internals.state, 707 internals.shape, 708 [polygon], 709 internals.rng, 710 ) 711 return distorted_polygons[0] 712 713 else: 714 new_points = self.distort_points_based_on_internals(internals, polygon.points) 715 return Polygon.create(points=new_points) 716 717 # yapf: disable 718 def distort_polygon( 719 self, 720 config_or_config_generator: Union[ 721 Union[_T_CONFIG, Mapping[str, Any]], 722 Callable[ 723 [Tuple[int, int], RandomGenerator], 724 Union[_T_CONFIG, Mapping[str, Any]], 725 ], 726 ], 727 shapable_or_shape: Union[Shapable, Tuple[int, int]], 728 polygon: Polygon, 729 state: Optional[_T_STATE] = None, 730 rng: Optional[RandomGenerator] = None, 731 ): 732 # yapf: enable 733 internals = self.prepare_internals( 734 config_or_config_generator=config_or_config_generator, 735 state=state, 736 shapable_or_shape=shapable_or_shape, 737 rng=rng, 738 ) 739 return self.distort_polygon_based_on_internals(internals, polygon) 740 741 def distort_polygons_based_on_internals( 742 self, 743 internals: DistortionInternals[_T_CONFIG, _T_STATE], 744 polygons: Iterable[Polygon], 745 ): 746 internals.restore_rng_if_supported() 747 748 if self.func_polygons: 749 return self.func_polygons( 750 internals.config, 751 internals.state, 752 internals.shape, 753 polygons, 754 internals.rng, 755 ) 756 757 else: 758 new_polygons = [] 759 for polygon in polygons: 760 new_polygon = self.distort_polygon_based_on_internals(internals, polygon) 761 new_polygons.append(new_polygon) 762 return new_polygons 763 764 # yapf: disable 765 def distort_polygons( 766 self, 767 config_or_config_generator: Union[ 768 Union[_T_CONFIG, Mapping[str, Any]], 769 Callable[ 770 [Tuple[int, int], RandomGenerator], 771 Union[_T_CONFIG, Mapping[str, Any]], 772 ], 773 ], 774 shapable_or_shape: Union[Shapable, Tuple[int, int]], 775 polygons: Iterable[Polygon], 776 state: Optional[_T_STATE] = None, 777 rng: Optional[RandomGenerator] = None, 778 ): 779 # yapf: enable 780 internals = self.prepare_internals( 781 config_or_config_generator=config_or_config_generator, 782 state=state, 783 shapable_or_shape=shapable_or_shape, 784 rng=rng, 785 ) 786 return self.distort_polygons_based_on_internals(internals, polygons) 787 788 @classmethod 789 def get_shape( 790 cls, 791 shapable_or_shape: Optional[Union[Shapable, Tuple[int, int]]] = None, 792 image: Optional[Image] = None, 793 mask: Optional[Mask] = None, 794 score_map: Optional[ScoreMap] = None, 795 ): 796 if shapable_or_shape is None: 797 shapable_or_shape = image or mask or score_map 798 assert shapable_or_shape 799 800 return cls.get_shape_from_shapable_or_shape(shapable_or_shape) 801 802 def clip_result_elements(self, result: DistortionResult): 803 if not self.is_geometric: 804 return 805 806 if result.point: 807 result.point = result.point.to_clipped_point(result.shape) 808 809 if result.points: 810 result.points = result.points.to_clipped_points(result.shape) 811 812 if result.corner_points: 813 result.corner_points = result.corner_points.to_clipped_points(result.shape) 814 815 if result.polygon: 816 result.polygon = result.polygon.to_clipped_polygon(result.shape) 817 818 if result.polygons: 819 result.polygons = [ 820 polygon.to_clipped_polygon(result.shape) for polygon in result.polygons 821 ] 822 823 # yapf: disable 824 def distort( 825 self, 826 config_or_config_generator: Union[ 827 Union[_T_CONFIG, Mapping[str, Any]], 828 Callable[ 829 [Tuple[int, int], RandomGenerator], 830 Union[_T_CONFIG, Mapping[str, Any]], 831 ], 832 ], 833 shapable_or_shape: Optional[Union[Shapable, Tuple[int, int]]] = None, 834 image: Optional[Image] = None, 835 mask: Optional[Mask] = None, 836 score_map: Optional[ScoreMap] = None, 837 point: Optional[Point] = None, 838 points: Optional[Union[PointList, PointTuple, Iterable[Point]]] = None, 839 corner_points: Optional[Union[PointList, PointTuple, Iterable[Point]]] = None, 840 polygon: Optional[Polygon] = None, 841 polygons: Optional[Iterable[Polygon]] = None, 842 get_active_mask: bool = False, 843 get_config: bool = False, 844 get_state: bool = False, 845 disable_clip_result_elements: bool = False, 846 rng: Optional[RandomGenerator] = None, 847 ): 848 # yapf: enable 849 shape = self.get_shape( 850 shapable_or_shape=shapable_or_shape, 851 image=image, 852 mask=mask, 853 score_map=score_map, 854 ) 855 856 internals = self.prepare_internals( 857 config_or_config_generator=config_or_config_generator, 858 state=None, 859 shapable_or_shape=shape, 860 rng=rng, 861 ) 862 863 # If is geometric distortion, the shape will be updated. 864 result = DistortionResult(shape=shape) 865 866 if self.is_geometric: 867 assert internals.state and internals.state.result_shape 868 result.shape = internals.state.result_shape 869 else: 870 result.shape = shape 871 872 if image: 873 result.image = self.distort_image_based_on_internals(internals, image) 874 assert result.shape == result.image.shape 875 876 if mask: 877 result.mask = self.distort_mask_based_on_internals(internals, mask) 878 assert result.shape == result.mask.shape 879 880 if score_map: 881 result.score_map = self.distort_score_map_based_on_internals(internals, score_map) 882 assert result.shape == result.score_map.shape 883 884 if point: 885 result.point = self.distort_point_based_on_internals(internals, point) 886 887 if points: 888 result.points = self.distort_points_based_on_internals(internals, points) 889 890 if corner_points: 891 result.corner_points = self.distort_points_based_on_internals(internals, corner_points) 892 893 if polygon: 894 result.polygon = self.distort_polygon_based_on_internals(internals, polygon) 895 896 if polygons: 897 result.polygons = self.distort_polygons_based_on_internals(internals, polygons) 898 899 if get_active_mask: 900 result.active_mask = self.get_active_mask_based_on_internals(internals) 901 assert result.shape == result.active_mask.shape 902 903 if get_config: 904 result.config = internals.config 905 906 if get_state: 907 result.state = internals.state 908 909 if not disable_clip_result_elements: 910 self.clip_result_elements(result) 911 912 return result
50class DistortionConfig: 51 52 _cached_name: str = '' 53 54 @classmethod 55 def get_name(cls): 56 if not cls._cached_name: 57 cls._cached_name = get_config_class_snake_case_name(cls.__name__) 58 return cls._cached_name 59 60 @property 61 def name(self): 62 return self.get_name() 63 64 @property 65 def supports_rng_state(self) -> bool: 66 return False 67 68 @property 69 def rng_state(self) -> Optional[Mapping[str, Any]]: 70 return None 71 72 @rng_state.setter 73 def rng_state(self, val: Mapping[str, Any]): 74 pass
80class DistortionState(Generic[_T_CONFIG]): 81 82 def __init__( 83 self, 84 config: _T_CONFIG, 85 shape: Tuple[int, int], 86 rng: Optional[RandomGenerator], 87 ): 88 raise NotImplementedError() 89 90 @property 91 def result_shape(self) -> Optional[Tuple[int, int]]: 92 return None
Abstract base class for generic types.
A generic type is typically declared by inheriting from this class parameterized with one or more type variables. For example, a generic mapping type might be defined as::
class Mapping(Generic[KT, VT]): def __getitem__(self, key: KT) -> VT: ... # Etc.
This class can then be used as follows::
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: try: return mapping[key] except KeyError: return default
95class DistortionNopState(DistortionState[_T_CONFIG]): 96 97 def __init__( 98 self, 99 config: _T_CONFIG, 100 shape: Tuple[int, int], 101 rng: Optional[RandomGenerator], 102 ): 103 raise NotImplementedError()
Abstract base class for generic types.
A generic type is typically declared by inheriting from this class parameterized with one or more type variables. For example, a generic mapping type might be defined as::
class Mapping(Generic[KT, VT]): def __getitem__(self, key: KT) -> VT: ... # Etc.
This class can then be used as follows::
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: try: return mapping[key] except KeyError: return default
110class DistortionResult: 111 shape: Tuple[int, int] 112 image: Optional[Image] = None 113 mask: Optional[Mask] = None 114 score_map: Optional[ScoreMap] = None 115 active_mask: Optional[Mask] = None 116 point: Optional[Point] = None 117 points: Optional[PointTuple] = None 118 corner_points: Optional[PointTuple] = None 119 polygon: Optional[Polygon] = None 120 polygons: Optional[Sequence[Polygon]] = None 121 config: Optional[Any] = None 122 state: Optional[Any] = None 123 meta: Optional[Mapping[str, Any]] = None
2def __init__(self, shape, image=attr_dict['image'].default, mask=attr_dict['mask'].default, score_map=attr_dict['score_map'].default, active_mask=attr_dict['active_mask'].default, point=attr_dict['point'].default, points=attr_dict['points'].default, corner_points=attr_dict['corner_points'].default, polygon=attr_dict['polygon'].default, polygons=attr_dict['polygons'].default, config=attr_dict['config'].default, state=attr_dict['state'].default, meta=attr_dict['meta'].default): 3 self.shape = shape 4 self.image = image 5 self.mask = mask 6 self.score_map = score_map 7 self.active_mask = active_mask 8 self.point = point 9 self.points = points 10 self.corner_points = corner_points 11 self.polygon = polygon 12 self.polygons = polygons 13 self.config = config 14 self.state = state 15 self.meta = meta
Method generated by attrs for class DistortionResult.
127class DistortionInternals(Generic[_T_CONFIG, _T_STATE]): 128 config: _T_CONFIG 129 state: Optional[_T_STATE] 130 shape: Tuple[int, int] 131 rng: Optional[RandomGenerator] 132 133 def restore_rng_if_supported(self): 134 if self.rng: 135 assert self.config.supports_rng_state and self.config.rng_state 136 self.rng.bit_generator.state = self.config.rng_state
Abstract base class for generic types.
A generic type is typically declared by inheriting from this class parameterized with one or more type variables. For example, a generic mapping type might be defined as::
class Mapping(Generic[KT, VT]): def __getitem__(self, key: KT) -> VT: ... # Etc.
This class can then be used as follows::
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: try: return mapping[key] except KeyError: return default
2def __init__(self, config, state, shape, rng): 3 self.config = config 4 self.state = state 5 self.shape = shape 6 self.rng = rng
Method generated by attrs for class DistortionInternals.
139class Distortion(Generic[_T_CONFIG, _T_STATE]): 140 141 # yapf: disable 142 def __init__( 143 self, 144 config_cls: Type[_T_CONFIG], 145 state_cls: Type[_T_STATE], 146 func_image: Callable[ 147 [ 148 _T_CONFIG, 149 Optional[_T_STATE], 150 Image, 151 Optional[RandomGenerator], 152 ], 153 Image, 154 ], 155 func_mask: Optional[ 156 Callable[ 157 [ 158 _T_CONFIG, 159 Optional[_T_STATE], 160 Mask, 161 Optional[RandomGenerator], 162 ], 163 Mask, 164 ] 165 ] = None, 166 func_score_map: Optional[ 167 Callable[ 168 [ 169 _T_CONFIG, 170 Optional[_T_STATE], 171 ScoreMap, 172 Optional[RandomGenerator], 173 ], 174 ScoreMap, 175 ] 176 ] = None, 177 func_active_mask: Optional[ 178 Callable[ 179 [ 180 _T_CONFIG, 181 Optional[_T_STATE], 182 Tuple[int, int], 183 Optional[RandomGenerator], 184 ], 185 Mask, 186 ] 187 ] = None, 188 func_point: Optional[ 189 Callable[ 190 [ 191 _T_CONFIG, 192 Optional[_T_STATE], 193 Tuple[int, int], 194 Point, 195 Optional[RandomGenerator], 196 ], 197 Point, 198 ] 199 ] = None, 200 func_points: Optional[ 201 Callable[ 202 [ 203 _T_CONFIG, 204 Optional[_T_STATE], 205 Tuple[int, int], 206 Union[PointList, PointTuple, Iterable[Point]], 207 Optional[RandomGenerator], 208 ], 209 PointTuple, 210 ] 211 ] = None, 212 func_polygon: Optional[ 213 Callable[ 214 [ 215 _T_CONFIG, 216 Optional[_T_STATE], 217 Tuple[int, int], 218 Polygon, 219 Optional[RandomGenerator], 220 ], 221 Polygon, 222 ] 223 ] = None, 224 func_polygons: Optional[ 225 Callable[ 226 [ 227 _T_CONFIG, 228 Optional[_T_STATE], 229 Tuple[int, int], 230 Iterable[Polygon], 231 Optional[RandomGenerator], 232 ], 233 Sequence[Polygon], 234 ] 235 ] = None, 236 ): 237 # yapf: enable 238 self.config_cls = config_cls 239 self.state_cls = state_cls 240 241 self.func_image = func_image 242 self.func_score_map = func_score_map 243 self.func_mask = func_mask 244 self.func_active_mask = func_active_mask 245 246 self.func_point = func_point 247 self.func_points = func_points 248 self.func_polygon = func_polygon 249 self.func_polygons = func_polygons 250 251 @property 252 def is_geometric(self): 253 return any(( 254 self.func_point, 255 self.func_points, 256 self.func_polygon, 257 self.func_polygons, 258 self.func_active_mask, 259 )) 260 261 # yapf: disable 262 def prepare_config_and_rng( 263 self, 264 config_or_config_generator: Union[ 265 Union[_T_CONFIG, Mapping[str, Any]], 266 Callable[ 267 [Tuple[int, int], RandomGenerator], 268 Union[_T_CONFIG, Mapping[str, Any]], 269 ], 270 ], 271 shape: Tuple[int, int], 272 rng: Optional[RandomGenerator], 273 ) -> Tuple[_T_CONFIG, Optional[RandomGenerator]]: 274 # yapf: enable 275 if callable(config_or_config_generator): 276 config_generator = cast( 277 Callable[[Tuple[int, int], RandomGenerator], Union[_T_CONFIG, Mapping[str, Any]]], 278 config_or_config_generator, 279 ) 280 if not rng: 281 raise RuntimeError('config_generator but rng is None.') 282 config = dyn_structure(config_generator(shape, rng), self.config_cls) 283 284 else: 285 config_or_config_generator = cast( 286 Union[_T_CONFIG, Mapping[str, Any]], 287 config_or_config_generator, 288 ) 289 config = dyn_structure(config_or_config_generator, self.config_cls) 290 291 if config.supports_rng_state: 292 if not config.rng_state: 293 if not rng: 294 raise RuntimeError('both config.rng_state and rng are None.') 295 config.rng_state = rng.bit_generator.state 296 # Move forward rng state to randomize the next run. 297 rng.random() 298 299 # Calling rng methods changes rng's state. We don't want to change the state 300 # of exterior rng, hence making a copy here. 301 rng = default_rng() 302 rng.bit_generator.state = config.rng_state 303 304 else: 305 # Force not passing rng. 306 rng = None 307 308 return config, rng 309 310 @classmethod 311 def get_shape_from_shapable_or_shape(cls, shapable_or_shape: Union[Shapable, Tuple[int, int]]): 312 if isinstance(shapable_or_shape, (list, tuple)): 313 assert len(shapable_or_shape) == 2 314 return shapable_or_shape 315 else: 316 return shapable_or_shape.shape 317 318 # yapf: disable 319 def prepare_internals( 320 self, 321 config_or_config_generator: Union[ 322 Union[_T_CONFIG, Mapping[str, Any]], 323 Callable[ 324 [Tuple[int, int], RandomGenerator], 325 Union[_T_CONFIG, Mapping[str, Any]], 326 ], 327 ], 328 state: Optional[_T_STATE], 329 shapable_or_shape: Union[Shapable, Tuple[int, int]], 330 rng: Optional[RandomGenerator] = None, 331 disable_state_initialization: bool = False, 332 ): 333 # yapf: enable 334 shape = self.get_shape_from_shapable_or_shape(shapable_or_shape) 335 336 config, rng = self.prepare_config_and_rng( 337 config_or_config_generator, 338 shape, 339 rng, 340 ) 341 342 if get_origin(self.state_cls) is not DistortionNopState: 343 if state is None and not disable_state_initialization: 344 state = self.state_cls(config, shape, rng) 345 else: 346 state = None 347 348 return DistortionInternals(config, state, shape, rng) 349 350 # yapf: disable 351 def generate_config_and_state( 352 self, 353 config_or_config_generator: Union[ 354 Union[_T_CONFIG, Mapping[str, Any]], 355 Callable[ 356 [Tuple[int, int], RandomGenerator], 357 Union[_T_CONFIG, Mapping[str, Any]], 358 ], 359 ], 360 state: Optional[_T_STATE], 361 shapable_or_shape: Union[Shapable, Tuple[int, int]], 362 rng: Optional[RandomGenerator] = None, 363 ): 364 # yapf: enable 365 internals = self.prepare_internals( 366 config_or_config_generator=config_or_config_generator, 367 state=state, 368 shapable_or_shape=shapable_or_shape, 369 rng=rng, 370 ) 371 return internals.config, internals.state 372 373 # yapf: disable 374 def generate_config( 375 self, 376 config_or_config_generator: Union[ 377 Union[_T_CONFIG, Mapping[str, Any]], 378 Callable[ 379 [Tuple[int, int], RandomGenerator], 380 Union[_T_CONFIG, Mapping[str, Any]], 381 ], 382 ], 383 shapable_or_shape: Union[Shapable, Tuple[int, int]], 384 rng: Optional[RandomGenerator] = None, 385 ): 386 # yapf: enable 387 internals = self.prepare_internals( 388 config_or_config_generator=config_or_config_generator, 389 state=None, 390 shapable_or_shape=shapable_or_shape, 391 rng=rng, 392 disable_state_initialization=True, 393 ) 394 return internals.config 395 396 # yapf: disable 397 def generate_state( 398 self, 399 config_or_config_generator: Union[ 400 Union[_T_CONFIG, Mapping[str, Any]], 401 Callable[ 402 [Tuple[int, int], RandomGenerator], 403 Union[_T_CONFIG, Mapping[str, Any]], 404 ], 405 ], 406 shapable_or_shape: Union[Shapable, Tuple[int, int]], 407 rng: Optional[RandomGenerator] = None, 408 ): 409 # yapf: enable 410 internals = self.prepare_internals( 411 config_or_config_generator=config_or_config_generator, 412 state=None, 413 shapable_or_shape=shapable_or_shape, 414 rng=rng, 415 ) 416 return internals.state 417 418 def distort_image_based_on_internals( 419 self, 420 internals: DistortionInternals[_T_CONFIG, _T_STATE], 421 image: Image, 422 ): 423 internals.restore_rng_if_supported() 424 425 return self.func_image( 426 internals.config, 427 internals.state, 428 image, 429 internals.rng, 430 ) 431 432 # yapf: disable 433 def distort_image( 434 self, 435 config_or_config_generator: Union[ 436 Union[_T_CONFIG, Mapping[str, Any]], 437 Callable[ 438 [Tuple[int, int], RandomGenerator], 439 Union[_T_CONFIG, Mapping[str, Any]], 440 ], 441 ], 442 image: Image, 443 state: Optional[_T_STATE] = None, 444 rng: Optional[RandomGenerator] = None, 445 ): 446 # yapf: enable 447 internals = self.prepare_internals( 448 config_or_config_generator=config_or_config_generator, 449 state=state, 450 shapable_or_shape=image, 451 rng=rng, 452 ) 453 return self.distort_image_based_on_internals(internals, image) 454 455 def distort_score_map_based_on_internals( 456 self, 457 internals: DistortionInternals[_T_CONFIG, _T_STATE], 458 score_map: ScoreMap, 459 ): 460 internals.restore_rng_if_supported() 461 462 if self.func_score_map: 463 return self.func_score_map( 464 internals.config, 465 internals.state, 466 score_map, 467 internals.rng, 468 ) 469 470 else: 471 # NOP. 472 return score_map 473 474 # yapf: disable 475 def distort_score_map( 476 self, 477 config_or_config_generator: Union[ 478 Union[_T_CONFIG, Mapping[str, Any]], 479 Callable[ 480 [Tuple[int, int], RandomGenerator], 481 Union[_T_CONFIG, Mapping[str, Any]], 482 ], 483 ], 484 score_map: ScoreMap, 485 state: Optional[_T_STATE] = None, 486 rng: Optional[RandomGenerator] = None, 487 ): 488 # yapf: enable 489 internals = self.prepare_internals( 490 config_or_config_generator=config_or_config_generator, 491 state=state, 492 shapable_or_shape=score_map, 493 rng=rng, 494 ) 495 return self.distort_score_map_based_on_internals(internals, score_map) 496 497 def distort_mask_based_on_internals( 498 self, 499 internals: DistortionInternals[_T_CONFIG, _T_STATE], 500 mask: Mask, 501 ): 502 internals.restore_rng_if_supported() 503 504 if self.func_mask: 505 return self.func_mask( 506 internals.config, 507 internals.state, 508 mask, 509 internals.rng, 510 ) 511 512 else: 513 # NOP. 514 return mask 515 516 # yapf: disable 517 def distort_mask( 518 self, 519 config_or_config_generator: Union[ 520 Union[_T_CONFIG, Mapping[str, Any]], 521 Callable[ 522 [Tuple[int, int], RandomGenerator], 523 Union[_T_CONFIG, Mapping[str, Any]], 524 ], 525 ], 526 mask: Mask, 527 state: Optional[_T_STATE] = None, 528 rng: Optional[RandomGenerator] = None, 529 ): 530 # yapf: enable 531 internals = self.prepare_internals( 532 config_or_config_generator=config_or_config_generator, 533 state=state, 534 shapable_or_shape=mask, 535 rng=rng, 536 ) 537 return self.distort_mask_based_on_internals(internals, mask) 538 539 def get_active_mask_based_on_internals( 540 self, 541 internals: DistortionInternals[_T_CONFIG, _T_STATE], 542 ): 543 # TODO: Something is wrong with cv.remap when dealing with border interpolation. 544 # This method could generate a mask "connects" to the transformed border. 545 internals.restore_rng_if_supported() 546 547 if self.func_active_mask: 548 return self.func_active_mask( 549 internals.config, 550 internals.state, 551 internals.shape, 552 internals.rng, 553 ) 554 555 else: 556 mask = Mask.from_shape(internals.shape, value=1) 557 return self.distort_mask_based_on_internals(internals, mask) 558 559 # yapf: disable 560 def get_active_mask( 561 self, 562 config_or_config_generator: Union[ 563 Union[_T_CONFIG, Mapping[str, Any]], 564 Callable[ 565 [Tuple[int, int], RandomGenerator], 566 Union[_T_CONFIG, Mapping[str, Any]], 567 ], 568 ], 569 shapable_or_shape: Union[Shapable, Tuple[int, int]], 570 state: Optional[_T_STATE] = None, 571 rng: Optional[RandomGenerator] = None, 572 ): 573 # yapf: enable 574 internals = self.prepare_internals( 575 config_or_config_generator=config_or_config_generator, 576 state=state, 577 shapable_or_shape=shapable_or_shape, 578 rng=rng, 579 ) 580 return self.get_active_mask_based_on_internals(internals) 581 582 def distort_point_based_on_internals( 583 self, 584 internals: DistortionInternals[_T_CONFIG, _T_STATE], 585 point: Point, 586 ): 587 internals.restore_rng_if_supported() 588 589 if self.func_point: 590 return self.func_point( 591 internals.config, 592 internals.state, 593 internals.shape, 594 point, 595 internals.rng, 596 ) 597 598 elif self.func_points: 599 distorted_points = self.func_points( 600 internals.config, 601 internals.state, 602 internals.shape, 603 [point], 604 internals.rng, 605 ) 606 return distorted_points[0] 607 608 else: 609 if self.is_geometric: 610 raise RuntimeError('Missing self.func_points or self.func_point.') 611 612 # NOP. 613 return point 614 615 # yapf: disable 616 def distort_point( 617 self, 618 config_or_config_generator: Union[ 619 Union[_T_CONFIG, Mapping[str, Any]], 620 Callable[ 621 [Tuple[int, int], RandomGenerator], 622 Union[_T_CONFIG, Mapping[str, Any]], 623 ], 624 ], 625 shapable_or_shape: Union[Shapable, Tuple[int, int]], 626 point: Point, 627 state: Optional[_T_STATE] = None, 628 rng: Optional[RandomGenerator] = None, 629 ): 630 # yapf: enable 631 internals = self.prepare_internals( 632 config_or_config_generator=config_or_config_generator, 633 state=state, 634 shapable_or_shape=shapable_or_shape, 635 rng=rng, 636 ) 637 return self.distort_point_based_on_internals(internals, point) 638 639 def distort_points_based_on_internals( 640 self, 641 internals: DistortionInternals[_T_CONFIG, _T_STATE], 642 points: Union[PointList, PointTuple, Iterable[Point]], 643 ): 644 internals.restore_rng_if_supported() 645 646 points = PointList(points) 647 648 if self.func_points: 649 return self.func_points( 650 internals.config, 651 internals.state, 652 internals.shape, 653 points, 654 internals.rng, 655 ) 656 657 else: 658 new_points = PointList() 659 for point in points: 660 new_point = self.distort_point_based_on_internals(internals, point) 661 new_points.append(new_point) 662 return new_points.to_point_tuple() 663 664 # yapf: disable 665 def distort_points( 666 self, 667 config_or_config_generator: Union[ 668 Union[_T_CONFIG, Mapping[str, Any]], 669 Callable[ 670 [Tuple[int, int], RandomGenerator], 671 Union[_T_CONFIG, Mapping[str, Any]], 672 ], 673 ], 674 shapable_or_shape: Union[Shapable, Tuple[int, int]], 675 points: Union[PointList, PointTuple, Iterable[Point]], 676 state: Optional[_T_STATE] = None, 677 rng: Optional[RandomGenerator] = None, 678 ): 679 # yapf: enable 680 internals = self.prepare_internals( 681 config_or_config_generator=config_or_config_generator, 682 state=state, 683 shapable_or_shape=shapable_or_shape, 684 rng=rng, 685 ) 686 return self.distort_points_based_on_internals(internals, points) 687 688 def distort_polygon_based_on_internals( 689 self, 690 internals: DistortionInternals[_T_CONFIG, _T_STATE], 691 polygon: Polygon, 692 ): 693 internals.restore_rng_if_supported() 694 695 if self.func_polygon: 696 return self.func_polygon( 697 internals.config, 698 internals.state, 699 internals.shape, 700 polygon, 701 internals.rng, 702 ) 703 704 elif self.func_polygons: 705 distorted_polygons = self.func_polygons( 706 internals.config, 707 internals.state, 708 internals.shape, 709 [polygon], 710 internals.rng, 711 ) 712 return distorted_polygons[0] 713 714 else: 715 new_points = self.distort_points_based_on_internals(internals, polygon.points) 716 return Polygon.create(points=new_points) 717 718 # yapf: disable 719 def distort_polygon( 720 self, 721 config_or_config_generator: Union[ 722 Union[_T_CONFIG, Mapping[str, Any]], 723 Callable[ 724 [Tuple[int, int], RandomGenerator], 725 Union[_T_CONFIG, Mapping[str, Any]], 726 ], 727 ], 728 shapable_or_shape: Union[Shapable, Tuple[int, int]], 729 polygon: Polygon, 730 state: Optional[_T_STATE] = None, 731 rng: Optional[RandomGenerator] = None, 732 ): 733 # yapf: enable 734 internals = self.prepare_internals( 735 config_or_config_generator=config_or_config_generator, 736 state=state, 737 shapable_or_shape=shapable_or_shape, 738 rng=rng, 739 ) 740 return self.distort_polygon_based_on_internals(internals, polygon) 741 742 def distort_polygons_based_on_internals( 743 self, 744 internals: DistortionInternals[_T_CONFIG, _T_STATE], 745 polygons: Iterable[Polygon], 746 ): 747 internals.restore_rng_if_supported() 748 749 if self.func_polygons: 750 return self.func_polygons( 751 internals.config, 752 internals.state, 753 internals.shape, 754 polygons, 755 internals.rng, 756 ) 757 758 else: 759 new_polygons = [] 760 for polygon in polygons: 761 new_polygon = self.distort_polygon_based_on_internals(internals, polygon) 762 new_polygons.append(new_polygon) 763 return new_polygons 764 765 # yapf: disable 766 def distort_polygons( 767 self, 768 config_or_config_generator: Union[ 769 Union[_T_CONFIG, Mapping[str, Any]], 770 Callable[ 771 [Tuple[int, int], RandomGenerator], 772 Union[_T_CONFIG, Mapping[str, Any]], 773 ], 774 ], 775 shapable_or_shape: Union[Shapable, Tuple[int, int]], 776 polygons: Iterable[Polygon], 777 state: Optional[_T_STATE] = None, 778 rng: Optional[RandomGenerator] = None, 779 ): 780 # yapf: enable 781 internals = self.prepare_internals( 782 config_or_config_generator=config_or_config_generator, 783 state=state, 784 shapable_or_shape=shapable_or_shape, 785 rng=rng, 786 ) 787 return self.distort_polygons_based_on_internals(internals, polygons) 788 789 @classmethod 790 def get_shape( 791 cls, 792 shapable_or_shape: Optional[Union[Shapable, Tuple[int, int]]] = None, 793 image: Optional[Image] = None, 794 mask: Optional[Mask] = None, 795 score_map: Optional[ScoreMap] = None, 796 ): 797 if shapable_or_shape is None: 798 shapable_or_shape = image or mask or score_map 799 assert shapable_or_shape 800 801 return cls.get_shape_from_shapable_or_shape(shapable_or_shape) 802 803 def clip_result_elements(self, result: DistortionResult): 804 if not self.is_geometric: 805 return 806 807 if result.point: 808 result.point = result.point.to_clipped_point(result.shape) 809 810 if result.points: 811 result.points = result.points.to_clipped_points(result.shape) 812 813 if result.corner_points: 814 result.corner_points = result.corner_points.to_clipped_points(result.shape) 815 816 if result.polygon: 817 result.polygon = result.polygon.to_clipped_polygon(result.shape) 818 819 if result.polygons: 820 result.polygons = [ 821 polygon.to_clipped_polygon(result.shape) for polygon in result.polygons 822 ] 823 824 # yapf: disable 825 def distort( 826 self, 827 config_or_config_generator: Union[ 828 Union[_T_CONFIG, Mapping[str, Any]], 829 Callable[ 830 [Tuple[int, int], RandomGenerator], 831 Union[_T_CONFIG, Mapping[str, Any]], 832 ], 833 ], 834 shapable_or_shape: Optional[Union[Shapable, Tuple[int, int]]] = None, 835 image: Optional[Image] = None, 836 mask: Optional[Mask] = None, 837 score_map: Optional[ScoreMap] = None, 838 point: Optional[Point] = None, 839 points: Optional[Union[PointList, PointTuple, Iterable[Point]]] = None, 840 corner_points: Optional[Union[PointList, PointTuple, Iterable[Point]]] = None, 841 polygon: Optional[Polygon] = None, 842 polygons: Optional[Iterable[Polygon]] = None, 843 get_active_mask: bool = False, 844 get_config: bool = False, 845 get_state: bool = False, 846 disable_clip_result_elements: bool = False, 847 rng: Optional[RandomGenerator] = None, 848 ): 849 # yapf: enable 850 shape = self.get_shape( 851 shapable_or_shape=shapable_or_shape, 852 image=image, 853 mask=mask, 854 score_map=score_map, 855 ) 856 857 internals = self.prepare_internals( 858 config_or_config_generator=config_or_config_generator, 859 state=None, 860 shapable_or_shape=shape, 861 rng=rng, 862 ) 863 864 # If is geometric distortion, the shape will be updated. 865 result = DistortionResult(shape=shape) 866 867 if self.is_geometric: 868 assert internals.state and internals.state.result_shape 869 result.shape = internals.state.result_shape 870 else: 871 result.shape = shape 872 873 if image: 874 result.image = self.distort_image_based_on_internals(internals, image) 875 assert result.shape == result.image.shape 876 877 if mask: 878 result.mask = self.distort_mask_based_on_internals(internals, mask) 879 assert result.shape == result.mask.shape 880 881 if score_map: 882 result.score_map = self.distort_score_map_based_on_internals(internals, score_map) 883 assert result.shape == result.score_map.shape 884 885 if point: 886 result.point = self.distort_point_based_on_internals(internals, point) 887 888 if points: 889 result.points = self.distort_points_based_on_internals(internals, points) 890 891 if corner_points: 892 result.corner_points = self.distort_points_based_on_internals(internals, corner_points) 893 894 if polygon: 895 result.polygon = self.distort_polygon_based_on_internals(internals, polygon) 896 897 if polygons: 898 result.polygons = self.distort_polygons_based_on_internals(internals, polygons) 899 900 if get_active_mask: 901 result.active_mask = self.get_active_mask_based_on_internals(internals) 902 assert result.shape == result.active_mask.shape 903 904 if get_config: 905 result.config = internals.config 906 907 if get_state: 908 result.state = internals.state 909 910 if not disable_clip_result_elements: 911 self.clip_result_elements(result) 912 913 return result
Abstract base class for generic types.
A generic type is typically declared by inheriting from this class parameterized with one or more type variables. For example, a generic mapping type might be defined as::
class Mapping(Generic[KT, VT]): def __getitem__(self, key: KT) -> VT: ... # Etc.
This class can then be used as follows::
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: try: return mapping[key] except KeyError: return default
142 def __init__( 143 self, 144 config_cls: Type[_T_CONFIG], 145 state_cls: Type[_T_STATE], 146 func_image: Callable[ 147 [ 148 _T_CONFIG, 149 Optional[_T_STATE], 150 Image, 151 Optional[RandomGenerator], 152 ], 153 Image, 154 ], 155 func_mask: Optional[ 156 Callable[ 157 [ 158 _T_CONFIG, 159 Optional[_T_STATE], 160 Mask, 161 Optional[RandomGenerator], 162 ], 163 Mask, 164 ] 165 ] = None, 166 func_score_map: Optional[ 167 Callable[ 168 [ 169 _T_CONFIG, 170 Optional[_T_STATE], 171 ScoreMap, 172 Optional[RandomGenerator], 173 ], 174 ScoreMap, 175 ] 176 ] = None, 177 func_active_mask: Optional[ 178 Callable[ 179 [ 180 _T_CONFIG, 181 Optional[_T_STATE], 182 Tuple[int, int], 183 Optional[RandomGenerator], 184 ], 185 Mask, 186 ] 187 ] = None, 188 func_point: Optional[ 189 Callable[ 190 [ 191 _T_CONFIG, 192 Optional[_T_STATE], 193 Tuple[int, int], 194 Point, 195 Optional[RandomGenerator], 196 ], 197 Point, 198 ] 199 ] = None, 200 func_points: Optional[ 201 Callable[ 202 [ 203 _T_CONFIG, 204 Optional[_T_STATE], 205 Tuple[int, int], 206 Union[PointList, PointTuple, Iterable[Point]], 207 Optional[RandomGenerator], 208 ], 209 PointTuple, 210 ] 211 ] = None, 212 func_polygon: Optional[ 213 Callable[ 214 [ 215 _T_CONFIG, 216 Optional[_T_STATE], 217 Tuple[int, int], 218 Polygon, 219 Optional[RandomGenerator], 220 ], 221 Polygon, 222 ] 223 ] = None, 224 func_polygons: Optional[ 225 Callable[ 226 [ 227 _T_CONFIG, 228 Optional[_T_STATE], 229 Tuple[int, int], 230 Iterable[Polygon], 231 Optional[RandomGenerator], 232 ], 233 Sequence[Polygon], 234 ] 235 ] = None, 236 ): 237 # yapf: enable 238 self.config_cls = config_cls 239 self.state_cls = state_cls 240 241 self.func_image = func_image 242 self.func_score_map = func_score_map 243 self.func_mask = func_mask 244 self.func_active_mask = func_active_mask 245 246 self.func_point = func_point 247 self.func_points = func_points 248 self.func_polygon = func_polygon 249 self.func_polygons = func_polygons
262 def prepare_config_and_rng( 263 self, 264 config_or_config_generator: Union[ 265 Union[_T_CONFIG, Mapping[str, Any]], 266 Callable[ 267 [Tuple[int, int], RandomGenerator], 268 Union[_T_CONFIG, Mapping[str, Any]], 269 ], 270 ], 271 shape: Tuple[int, int], 272 rng: Optional[RandomGenerator], 273 ) -> Tuple[_T_CONFIG, Optional[RandomGenerator]]: 274 # yapf: enable 275 if callable(config_or_config_generator): 276 config_generator = cast( 277 Callable[[Tuple[int, int], RandomGenerator], Union[_T_CONFIG, Mapping[str, Any]]], 278 config_or_config_generator, 279 ) 280 if not rng: 281 raise RuntimeError('config_generator but rng is None.') 282 config = dyn_structure(config_generator(shape, rng), self.config_cls) 283 284 else: 285 config_or_config_generator = cast( 286 Union[_T_CONFIG, Mapping[str, Any]], 287 config_or_config_generator, 288 ) 289 config = dyn_structure(config_or_config_generator, self.config_cls) 290 291 if config.supports_rng_state: 292 if not config.rng_state: 293 if not rng: 294 raise RuntimeError('both config.rng_state and rng are None.') 295 config.rng_state = rng.bit_generator.state 296 # Move forward rng state to randomize the next run. 297 rng.random() 298 299 # Calling rng methods changes rng's state. We don't want to change the state 300 # of exterior rng, hence making a copy here. 301 rng = default_rng() 302 rng.bit_generator.state = config.rng_state 303 304 else: 305 # Force not passing rng. 306 rng = None 307 308 return config, rng
319 def prepare_internals( 320 self, 321 config_or_config_generator: Union[ 322 Union[_T_CONFIG, Mapping[str, Any]], 323 Callable[ 324 [Tuple[int, int], RandomGenerator], 325 Union[_T_CONFIG, Mapping[str, Any]], 326 ], 327 ], 328 state: Optional[_T_STATE], 329 shapable_or_shape: Union[Shapable, Tuple[int, int]], 330 rng: Optional[RandomGenerator] = None, 331 disable_state_initialization: bool = False, 332 ): 333 # yapf: enable 334 shape = self.get_shape_from_shapable_or_shape(shapable_or_shape) 335 336 config, rng = self.prepare_config_and_rng( 337 config_or_config_generator, 338 shape, 339 rng, 340 ) 341 342 if get_origin(self.state_cls) is not DistortionNopState: 343 if state is None and not disable_state_initialization: 344 state = self.state_cls(config, shape, rng) 345 else: 346 state = None 347 348 return DistortionInternals(config, state, shape, rng)
351 def generate_config_and_state( 352 self, 353 config_or_config_generator: Union[ 354 Union[_T_CONFIG, Mapping[str, Any]], 355 Callable[ 356 [Tuple[int, int], RandomGenerator], 357 Union[_T_CONFIG, Mapping[str, Any]], 358 ], 359 ], 360 state: Optional[_T_STATE], 361 shapable_or_shape: Union[Shapable, Tuple[int, int]], 362 rng: Optional[RandomGenerator] = None, 363 ): 364 # yapf: enable 365 internals = self.prepare_internals( 366 config_or_config_generator=config_or_config_generator, 367 state=state, 368 shapable_or_shape=shapable_or_shape, 369 rng=rng, 370 ) 371 return internals.config, internals.state
374 def generate_config( 375 self, 376 config_or_config_generator: Union[ 377 Union[_T_CONFIG, Mapping[str, Any]], 378 Callable[ 379 [Tuple[int, int], RandomGenerator], 380 Union[_T_CONFIG, Mapping[str, Any]], 381 ], 382 ], 383 shapable_or_shape: Union[Shapable, Tuple[int, int]], 384 rng: Optional[RandomGenerator] = None, 385 ): 386 # yapf: enable 387 internals = self.prepare_internals( 388 config_or_config_generator=config_or_config_generator, 389 state=None, 390 shapable_or_shape=shapable_or_shape, 391 rng=rng, 392 disable_state_initialization=True, 393 ) 394 return internals.config
397 def generate_state( 398 self, 399 config_or_config_generator: Union[ 400 Union[_T_CONFIG, Mapping[str, Any]], 401 Callable[ 402 [Tuple[int, int], RandomGenerator], 403 Union[_T_CONFIG, Mapping[str, Any]], 404 ], 405 ], 406 shapable_or_shape: Union[Shapable, Tuple[int, int]], 407 rng: Optional[RandomGenerator] = None, 408 ): 409 # yapf: enable 410 internals = self.prepare_internals( 411 config_or_config_generator=config_or_config_generator, 412 state=None, 413 shapable_or_shape=shapable_or_shape, 414 rng=rng, 415 ) 416 return internals.state
433 def distort_image( 434 self, 435 config_or_config_generator: Union[ 436 Union[_T_CONFIG, Mapping[str, Any]], 437 Callable[ 438 [Tuple[int, int], RandomGenerator], 439 Union[_T_CONFIG, Mapping[str, Any]], 440 ], 441 ], 442 image: Image, 443 state: Optional[_T_STATE] = None, 444 rng: Optional[RandomGenerator] = None, 445 ): 446 # yapf: enable 447 internals = self.prepare_internals( 448 config_or_config_generator=config_or_config_generator, 449 state=state, 450 shapable_or_shape=image, 451 rng=rng, 452 ) 453 return self.distort_image_based_on_internals(internals, image)
455 def distort_score_map_based_on_internals( 456 self, 457 internals: DistortionInternals[_T_CONFIG, _T_STATE], 458 score_map: ScoreMap, 459 ): 460 internals.restore_rng_if_supported() 461 462 if self.func_score_map: 463 return self.func_score_map( 464 internals.config, 465 internals.state, 466 score_map, 467 internals.rng, 468 ) 469 470 else: 471 # NOP. 472 return score_map
475 def distort_score_map( 476 self, 477 config_or_config_generator: Union[ 478 Union[_T_CONFIG, Mapping[str, Any]], 479 Callable[ 480 [Tuple[int, int], RandomGenerator], 481 Union[_T_CONFIG, Mapping[str, Any]], 482 ], 483 ], 484 score_map: ScoreMap, 485 state: Optional[_T_STATE] = None, 486 rng: Optional[RandomGenerator] = None, 487 ): 488 # yapf: enable 489 internals = self.prepare_internals( 490 config_or_config_generator=config_or_config_generator, 491 state=state, 492 shapable_or_shape=score_map, 493 rng=rng, 494 ) 495 return self.distort_score_map_based_on_internals(internals, score_map)
497 def distort_mask_based_on_internals( 498 self, 499 internals: DistortionInternals[_T_CONFIG, _T_STATE], 500 mask: Mask, 501 ): 502 internals.restore_rng_if_supported() 503 504 if self.func_mask: 505 return self.func_mask( 506 internals.config, 507 internals.state, 508 mask, 509 internals.rng, 510 ) 511 512 else: 513 # NOP. 514 return mask
517 def distort_mask( 518 self, 519 config_or_config_generator: Union[ 520 Union[_T_CONFIG, Mapping[str, Any]], 521 Callable[ 522 [Tuple[int, int], RandomGenerator], 523 Union[_T_CONFIG, Mapping[str, Any]], 524 ], 525 ], 526 mask: Mask, 527 state: Optional[_T_STATE] = None, 528 rng: Optional[RandomGenerator] = None, 529 ): 530 # yapf: enable 531 internals = self.prepare_internals( 532 config_or_config_generator=config_or_config_generator, 533 state=state, 534 shapable_or_shape=mask, 535 rng=rng, 536 ) 537 return self.distort_mask_based_on_internals(internals, mask)
539 def get_active_mask_based_on_internals( 540 self, 541 internals: DistortionInternals[_T_CONFIG, _T_STATE], 542 ): 543 # TODO: Something is wrong with cv.remap when dealing with border interpolation. 544 # This method could generate a mask "connects" to the transformed border. 545 internals.restore_rng_if_supported() 546 547 if self.func_active_mask: 548 return self.func_active_mask( 549 internals.config, 550 internals.state, 551 internals.shape, 552 internals.rng, 553 ) 554 555 else: 556 mask = Mask.from_shape(internals.shape, value=1) 557 return self.distort_mask_based_on_internals(internals, mask)
560 def get_active_mask( 561 self, 562 config_or_config_generator: Union[ 563 Union[_T_CONFIG, Mapping[str, Any]], 564 Callable[ 565 [Tuple[int, int], RandomGenerator], 566 Union[_T_CONFIG, Mapping[str, Any]], 567 ], 568 ], 569 shapable_or_shape: Union[Shapable, Tuple[int, int]], 570 state: Optional[_T_STATE] = None, 571 rng: Optional[RandomGenerator] = None, 572 ): 573 # yapf: enable 574 internals = self.prepare_internals( 575 config_or_config_generator=config_or_config_generator, 576 state=state, 577 shapable_or_shape=shapable_or_shape, 578 rng=rng, 579 ) 580 return self.get_active_mask_based_on_internals(internals)
582 def distort_point_based_on_internals( 583 self, 584 internals: DistortionInternals[_T_CONFIG, _T_STATE], 585 point: Point, 586 ): 587 internals.restore_rng_if_supported() 588 589 if self.func_point: 590 return self.func_point( 591 internals.config, 592 internals.state, 593 internals.shape, 594 point, 595 internals.rng, 596 ) 597 598 elif self.func_points: 599 distorted_points = self.func_points( 600 internals.config, 601 internals.state, 602 internals.shape, 603 [point], 604 internals.rng, 605 ) 606 return distorted_points[0] 607 608 else: 609 if self.is_geometric: 610 raise RuntimeError('Missing self.func_points or self.func_point.') 611 612 # NOP. 613 return point
616 def distort_point( 617 self, 618 config_or_config_generator: Union[ 619 Union[_T_CONFIG, Mapping[str, Any]], 620 Callable[ 621 [Tuple[int, int], RandomGenerator], 622 Union[_T_CONFIG, Mapping[str, Any]], 623 ], 624 ], 625 shapable_or_shape: Union[Shapable, Tuple[int, int]], 626 point: Point, 627 state: Optional[_T_STATE] = None, 628 rng: Optional[RandomGenerator] = None, 629 ): 630 # yapf: enable 631 internals = self.prepare_internals( 632 config_or_config_generator=config_or_config_generator, 633 state=state, 634 shapable_or_shape=shapable_or_shape, 635 rng=rng, 636 ) 637 return self.distort_point_based_on_internals(internals, point)
639 def distort_points_based_on_internals( 640 self, 641 internals: DistortionInternals[_T_CONFIG, _T_STATE], 642 points: Union[PointList, PointTuple, Iterable[Point]], 643 ): 644 internals.restore_rng_if_supported() 645 646 points = PointList(points) 647 648 if self.func_points: 649 return self.func_points( 650 internals.config, 651 internals.state, 652 internals.shape, 653 points, 654 internals.rng, 655 ) 656 657 else: 658 new_points = PointList() 659 for point in points: 660 new_point = self.distort_point_based_on_internals(internals, point) 661 new_points.append(new_point) 662 return new_points.to_point_tuple()
665 def distort_points( 666 self, 667 config_or_config_generator: Union[ 668 Union[_T_CONFIG, Mapping[str, Any]], 669 Callable[ 670 [Tuple[int, int], RandomGenerator], 671 Union[_T_CONFIG, Mapping[str, Any]], 672 ], 673 ], 674 shapable_or_shape: Union[Shapable, Tuple[int, int]], 675 points: Union[PointList, PointTuple, Iterable[Point]], 676 state: Optional[_T_STATE] = None, 677 rng: Optional[RandomGenerator] = None, 678 ): 679 # yapf: enable 680 internals = self.prepare_internals( 681 config_or_config_generator=config_or_config_generator, 682 state=state, 683 shapable_or_shape=shapable_or_shape, 684 rng=rng, 685 ) 686 return self.distort_points_based_on_internals(internals, points)
688 def distort_polygon_based_on_internals( 689 self, 690 internals: DistortionInternals[_T_CONFIG, _T_STATE], 691 polygon: Polygon, 692 ): 693 internals.restore_rng_if_supported() 694 695 if self.func_polygon: 696 return self.func_polygon( 697 internals.config, 698 internals.state, 699 internals.shape, 700 polygon, 701 internals.rng, 702 ) 703 704 elif self.func_polygons: 705 distorted_polygons = self.func_polygons( 706 internals.config, 707 internals.state, 708 internals.shape, 709 [polygon], 710 internals.rng, 711 ) 712 return distorted_polygons[0] 713 714 else: 715 new_points = self.distort_points_based_on_internals(internals, polygon.points) 716 return Polygon.create(points=new_points)
719 def distort_polygon( 720 self, 721 config_or_config_generator: Union[ 722 Union[_T_CONFIG, Mapping[str, Any]], 723 Callable[ 724 [Tuple[int, int], RandomGenerator], 725 Union[_T_CONFIG, Mapping[str, Any]], 726 ], 727 ], 728 shapable_or_shape: Union[Shapable, Tuple[int, int]], 729 polygon: Polygon, 730 state: Optional[_T_STATE] = None, 731 rng: Optional[RandomGenerator] = None, 732 ): 733 # yapf: enable 734 internals = self.prepare_internals( 735 config_or_config_generator=config_or_config_generator, 736 state=state, 737 shapable_or_shape=shapable_or_shape, 738 rng=rng, 739 ) 740 return self.distort_polygon_based_on_internals(internals, polygon)
742 def distort_polygons_based_on_internals( 743 self, 744 internals: DistortionInternals[_T_CONFIG, _T_STATE], 745 polygons: Iterable[Polygon], 746 ): 747 internals.restore_rng_if_supported() 748 749 if self.func_polygons: 750 return self.func_polygons( 751 internals.config, 752 internals.state, 753 internals.shape, 754 polygons, 755 internals.rng, 756 ) 757 758 else: 759 new_polygons = [] 760 for polygon in polygons: 761 new_polygon = self.distort_polygon_based_on_internals(internals, polygon) 762 new_polygons.append(new_polygon) 763 return new_polygons
766 def distort_polygons( 767 self, 768 config_or_config_generator: Union[ 769 Union[_T_CONFIG, Mapping[str, Any]], 770 Callable[ 771 [Tuple[int, int], RandomGenerator], 772 Union[_T_CONFIG, Mapping[str, Any]], 773 ], 774 ], 775 shapable_or_shape: Union[Shapable, Tuple[int, int]], 776 polygons: Iterable[Polygon], 777 state: Optional[_T_STATE] = None, 778 rng: Optional[RandomGenerator] = None, 779 ): 780 # yapf: enable 781 internals = self.prepare_internals( 782 config_or_config_generator=config_or_config_generator, 783 state=state, 784 shapable_or_shape=shapable_or_shape, 785 rng=rng, 786 ) 787 return self.distort_polygons_based_on_internals(internals, polygons)
789 @classmethod 790 def get_shape( 791 cls, 792 shapable_or_shape: Optional[Union[Shapable, Tuple[int, int]]] = None, 793 image: Optional[Image] = None, 794 mask: Optional[Mask] = None, 795 score_map: Optional[ScoreMap] = None, 796 ): 797 if shapable_or_shape is None: 798 shapable_or_shape = image or mask or score_map 799 assert shapable_or_shape 800 801 return cls.get_shape_from_shapable_or_shape(shapable_or_shape)
803 def clip_result_elements(self, result: DistortionResult): 804 if not self.is_geometric: 805 return 806 807 if result.point: 808 result.point = result.point.to_clipped_point(result.shape) 809 810 if result.points: 811 result.points = result.points.to_clipped_points(result.shape) 812 813 if result.corner_points: 814 result.corner_points = result.corner_points.to_clipped_points(result.shape) 815 816 if result.polygon: 817 result.polygon = result.polygon.to_clipped_polygon(result.shape) 818 819 if result.polygons: 820 result.polygons = [ 821 polygon.to_clipped_polygon(result.shape) for polygon in result.polygons 822 ]
825 def distort( 826 self, 827 config_or_config_generator: Union[ 828 Union[_T_CONFIG, Mapping[str, Any]], 829 Callable[ 830 [Tuple[int, int], RandomGenerator], 831 Union[_T_CONFIG, Mapping[str, Any]], 832 ], 833 ], 834 shapable_or_shape: Optional[Union[Shapable, Tuple[int, int]]] = None, 835 image: Optional[Image] = None, 836 mask: Optional[Mask] = None, 837 score_map: Optional[ScoreMap] = None, 838 point: Optional[Point] = None, 839 points: Optional[Union[PointList, PointTuple, Iterable[Point]]] = None, 840 corner_points: Optional[Union[PointList, PointTuple, Iterable[Point]]] = None, 841 polygon: Optional[Polygon] = None, 842 polygons: Optional[Iterable[Polygon]] = None, 843 get_active_mask: bool = False, 844 get_config: bool = False, 845 get_state: bool = False, 846 disable_clip_result_elements: bool = False, 847 rng: Optional[RandomGenerator] = None, 848 ): 849 # yapf: enable 850 shape = self.get_shape( 851 shapable_or_shape=shapable_or_shape, 852 image=image, 853 mask=mask, 854 score_map=score_map, 855 ) 856 857 internals = self.prepare_internals( 858 config_or_config_generator=config_or_config_generator, 859 state=None, 860 shapable_or_shape=shape, 861 rng=rng, 862 ) 863 864 # If is geometric distortion, the shape will be updated. 865 result = DistortionResult(shape=shape) 866 867 if self.is_geometric: 868 assert internals.state and internals.state.result_shape 869 result.shape = internals.state.result_shape 870 else: 871 result.shape = shape 872 873 if image: 874 result.image = self.distort_image_based_on_internals(internals, image) 875 assert result.shape == result.image.shape 876 877 if mask: 878 result.mask = self.distort_mask_based_on_internals(internals, mask) 879 assert result.shape == result.mask.shape 880 881 if score_map: 882 result.score_map = self.distort_score_map_based_on_internals(internals, score_map) 883 assert result.shape == result.score_map.shape 884 885 if point: 886 result.point = self.distort_point_based_on_internals(internals, point) 887 888 if points: 889 result.points = self.distort_points_based_on_internals(internals, points) 890 891 if corner_points: 892 result.corner_points = self.distort_points_based_on_internals(internals, corner_points) 893 894 if polygon: 895 result.polygon = self.distort_polygon_based_on_internals(internals, polygon) 896 897 if polygons: 898 result.polygons = self.distort_polygons_based_on_internals(internals, polygons) 899 900 if get_active_mask: 901 result.active_mask = self.get_active_mask_based_on_internals(internals) 902 assert result.shape == result.active_mask.shape 903 904 if get_config: 905 result.config = internals.config 906 907 if get_state: 908 result.state = internals.state 909 910 if not disable_clip_result_elements: 911 self.clip_result_elements(result) 912 913 return result