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 296 # Calling rng methods changes rng's state. We don't want to change the state 297 # of exterior rng, hence making a copy here. 298 rng = default_rng() 299 rng.bit_generator.state = config.rng_state 300 301 else: 302 # Force not passing rng. 303 rng = None 304 305 return config, rng 306 307 @classmethod 308 def get_shape_from_shapable_or_shape(cls, shapable_or_shape: Union[Shapable, Tuple[int, int]]): 309 if isinstance(shapable_or_shape, (list, tuple)): 310 assert len(shapable_or_shape) == 2 311 return shapable_or_shape 312 else: 313 return shapable_or_shape.shape 314 315 # yapf: disable 316 def prepare_internals( 317 self, 318 config_or_config_generator: Union[ 319 Union[_T_CONFIG, Mapping[str, Any]], 320 Callable[ 321 [Tuple[int, int], RandomGenerator], 322 Union[_T_CONFIG, Mapping[str, Any]], 323 ], 324 ], 325 state: Optional[_T_STATE], 326 shapable_or_shape: Union[Shapable, Tuple[int, int]], 327 rng: Optional[RandomGenerator] = None, 328 disable_state_initialization: bool = False, 329 ): 330 # yapf: enable 331 shape = self.get_shape_from_shapable_or_shape(shapable_or_shape) 332 333 config, rng = self.prepare_config_and_rng( 334 config_or_config_generator, 335 shape, 336 rng, 337 ) 338 339 if get_origin(self.state_cls) is not DistortionNopState: 340 if state is None and not disable_state_initialization: 341 state = self.state_cls(config, shape, rng) 342 else: 343 state = None 344 345 return DistortionInternals(config, state, shape, rng) 346 347 # yapf: disable 348 def generate_config_and_state( 349 self, 350 config_or_config_generator: Union[ 351 Union[_T_CONFIG, Mapping[str, Any]], 352 Callable[ 353 [Tuple[int, int], RandomGenerator], 354 Union[_T_CONFIG, Mapping[str, Any]], 355 ], 356 ], 357 state: Optional[_T_STATE], 358 shapable_or_shape: Union[Shapable, Tuple[int, int]], 359 rng: Optional[RandomGenerator] = None, 360 ): 361 # yapf: enable 362 internals = self.prepare_internals( 363 config_or_config_generator=config_or_config_generator, 364 state=state, 365 shapable_or_shape=shapable_or_shape, 366 rng=rng, 367 ) 368 return internals.config, internals.state 369 370 # yapf: disable 371 def generate_config( 372 self, 373 config_or_config_generator: Union[ 374 Union[_T_CONFIG, Mapping[str, Any]], 375 Callable[ 376 [Tuple[int, int], RandomGenerator], 377 Union[_T_CONFIG, Mapping[str, Any]], 378 ], 379 ], 380 shapable_or_shape: Union[Shapable, Tuple[int, int]], 381 rng: Optional[RandomGenerator] = None, 382 ): 383 # yapf: enable 384 internals = self.prepare_internals( 385 config_or_config_generator=config_or_config_generator, 386 state=None, 387 shapable_or_shape=shapable_or_shape, 388 rng=rng, 389 disable_state_initialization=True, 390 ) 391 return internals.config 392 393 # yapf: disable 394 def generate_state( 395 self, 396 config_or_config_generator: Union[ 397 Union[_T_CONFIG, Mapping[str, Any]], 398 Callable[ 399 [Tuple[int, int], RandomGenerator], 400 Union[_T_CONFIG, Mapping[str, Any]], 401 ], 402 ], 403 shapable_or_shape: Union[Shapable, Tuple[int, int]], 404 rng: Optional[RandomGenerator] = None, 405 ): 406 # yapf: enable 407 internals = self.prepare_internals( 408 config_or_config_generator=config_or_config_generator, 409 state=None, 410 shapable_or_shape=shapable_or_shape, 411 rng=rng, 412 ) 413 return internals.state 414 415 def distort_image_based_on_internals( 416 self, 417 internals: DistortionInternals[_T_CONFIG, _T_STATE], 418 image: Image, 419 ): 420 internals.restore_rng_if_supported() 421 422 return self.func_image( 423 internals.config, 424 internals.state, 425 image, 426 internals.rng, 427 ) 428 429 # yapf: disable 430 def distort_image( 431 self, 432 config_or_config_generator: Union[ 433 Union[_T_CONFIG, Mapping[str, Any]], 434 Callable[ 435 [Tuple[int, int], RandomGenerator], 436 Union[_T_CONFIG, Mapping[str, Any]], 437 ], 438 ], 439 image: Image, 440 state: Optional[_T_STATE] = None, 441 rng: Optional[RandomGenerator] = None, 442 ): 443 # yapf: enable 444 internals = self.prepare_internals( 445 config_or_config_generator=config_or_config_generator, 446 state=state, 447 shapable_or_shape=image, 448 rng=rng, 449 ) 450 return self.distort_image_based_on_internals(internals, image) 451 452 def distort_score_map_based_on_internals( 453 self, 454 internals: DistortionInternals[_T_CONFIG, _T_STATE], 455 score_map: ScoreMap, 456 ): 457 internals.restore_rng_if_supported() 458 459 if self.func_score_map: 460 return self.func_score_map( 461 internals.config, 462 internals.state, 463 score_map, 464 internals.rng, 465 ) 466 467 else: 468 # NOP. 469 return score_map 470 471 # yapf: disable 472 def distort_score_map( 473 self, 474 config_or_config_generator: Union[ 475 Union[_T_CONFIG, Mapping[str, Any]], 476 Callable[ 477 [Tuple[int, int], RandomGenerator], 478 Union[_T_CONFIG, Mapping[str, Any]], 479 ], 480 ], 481 score_map: ScoreMap, 482 state: Optional[_T_STATE] = None, 483 rng: Optional[RandomGenerator] = None, 484 ): 485 # yapf: enable 486 internals = self.prepare_internals( 487 config_or_config_generator=config_or_config_generator, 488 state=state, 489 shapable_or_shape=score_map, 490 rng=rng, 491 ) 492 return self.distort_score_map_based_on_internals(internals, score_map) 493 494 def distort_mask_based_on_internals( 495 self, 496 internals: DistortionInternals[_T_CONFIG, _T_STATE], 497 mask: Mask, 498 ): 499 internals.restore_rng_if_supported() 500 501 if self.func_mask: 502 return self.func_mask( 503 internals.config, 504 internals.state, 505 mask, 506 internals.rng, 507 ) 508 509 else: 510 # NOP. 511 return mask 512 513 # yapf: disable 514 def distort_mask( 515 self, 516 config_or_config_generator: Union[ 517 Union[_T_CONFIG, Mapping[str, Any]], 518 Callable[ 519 [Tuple[int, int], RandomGenerator], 520 Union[_T_CONFIG, Mapping[str, Any]], 521 ], 522 ], 523 mask: Mask, 524 state: Optional[_T_STATE] = None, 525 rng: Optional[RandomGenerator] = None, 526 ): 527 # yapf: enable 528 internals = self.prepare_internals( 529 config_or_config_generator=config_or_config_generator, 530 state=state, 531 shapable_or_shape=mask, 532 rng=rng, 533 ) 534 return self.distort_mask_based_on_internals(internals, mask) 535 536 def get_active_mask_based_on_internals( 537 self, 538 internals: DistortionInternals[_T_CONFIG, _T_STATE], 539 ): 540 # TODO: Something is wrong with cv.remap when dealing with border interpolation. 541 # This method could generate a mask "connects" to the transformed border. 542 internals.restore_rng_if_supported() 543 544 if self.func_active_mask: 545 return self.func_active_mask( 546 internals.config, 547 internals.state, 548 internals.shape, 549 internals.rng, 550 ) 551 552 else: 553 mask = Mask.from_shape(internals.shape, value=1) 554 return self.distort_mask_based_on_internals(internals, mask) 555 556 # yapf: disable 557 def get_active_mask( 558 self, 559 config_or_config_generator: Union[ 560 Union[_T_CONFIG, Mapping[str, Any]], 561 Callable[ 562 [Tuple[int, int], RandomGenerator], 563 Union[_T_CONFIG, Mapping[str, Any]], 564 ], 565 ], 566 shapable_or_shape: Union[Shapable, Tuple[int, int]], 567 state: Optional[_T_STATE] = None, 568 rng: Optional[RandomGenerator] = None, 569 ): 570 # yapf: enable 571 internals = self.prepare_internals( 572 config_or_config_generator=config_or_config_generator, 573 state=state, 574 shapable_or_shape=shapable_or_shape, 575 rng=rng, 576 ) 577 return self.get_active_mask_based_on_internals(internals) 578 579 def distort_point_based_on_internals( 580 self, 581 internals: DistortionInternals[_T_CONFIG, _T_STATE], 582 point: Point, 583 ): 584 internals.restore_rng_if_supported() 585 586 if self.func_point: 587 return self.func_point( 588 internals.config, 589 internals.state, 590 internals.shape, 591 point, 592 internals.rng, 593 ) 594 595 elif self.func_points: 596 distorted_points = self.func_points( 597 internals.config, 598 internals.state, 599 internals.shape, 600 [point], 601 internals.rng, 602 ) 603 return distorted_points[0] 604 605 else: 606 if self.is_geometric: 607 raise RuntimeError('Missing self.func_points or self.func_point.') 608 609 # NOP. 610 return point 611 612 # yapf: disable 613 def distort_point( 614 self, 615 config_or_config_generator: Union[ 616 Union[_T_CONFIG, Mapping[str, Any]], 617 Callable[ 618 [Tuple[int, int], RandomGenerator], 619 Union[_T_CONFIG, Mapping[str, Any]], 620 ], 621 ], 622 shapable_or_shape: Union[Shapable, Tuple[int, int]], 623 point: Point, 624 state: Optional[_T_STATE] = None, 625 rng: Optional[RandomGenerator] = None, 626 ): 627 # yapf: enable 628 internals = self.prepare_internals( 629 config_or_config_generator=config_or_config_generator, 630 state=state, 631 shapable_or_shape=shapable_or_shape, 632 rng=rng, 633 ) 634 return self.distort_point_based_on_internals(internals, point) 635 636 def distort_points_based_on_internals( 637 self, 638 internals: DistortionInternals[_T_CONFIG, _T_STATE], 639 points: Union[PointList, PointTuple, Iterable[Point]], 640 ): 641 internals.restore_rng_if_supported() 642 643 points = PointList(points) 644 645 if self.func_points: 646 return self.func_points( 647 internals.config, 648 internals.state, 649 internals.shape, 650 points, 651 internals.rng, 652 ) 653 654 else: 655 new_points = PointList() 656 for point in points: 657 new_point = self.distort_point_based_on_internals(internals, point) 658 new_points.append(new_point) 659 return new_points.to_point_tuple() 660 661 # yapf: disable 662 def distort_points( 663 self, 664 config_or_config_generator: Union[ 665 Union[_T_CONFIG, Mapping[str, Any]], 666 Callable[ 667 [Tuple[int, int], RandomGenerator], 668 Union[_T_CONFIG, Mapping[str, Any]], 669 ], 670 ], 671 shapable_or_shape: Union[Shapable, Tuple[int, int]], 672 points: Union[PointList, PointTuple, Iterable[Point]], 673 state: Optional[_T_STATE] = None, 674 rng: Optional[RandomGenerator] = None, 675 ): 676 # yapf: enable 677 internals = self.prepare_internals( 678 config_or_config_generator=config_or_config_generator, 679 state=state, 680 shapable_or_shape=shapable_or_shape, 681 rng=rng, 682 ) 683 return self.distort_points_based_on_internals(internals, points) 684 685 def distort_polygon_based_on_internals( 686 self, 687 internals: DistortionInternals[_T_CONFIG, _T_STATE], 688 polygon: Polygon, 689 ): 690 internals.restore_rng_if_supported() 691 692 if self.func_polygon: 693 return self.func_polygon( 694 internals.config, 695 internals.state, 696 internals.shape, 697 polygon, 698 internals.rng, 699 ) 700 701 elif self.func_polygons: 702 distorted_polygons = self.func_polygons( 703 internals.config, 704 internals.state, 705 internals.shape, 706 [polygon], 707 internals.rng, 708 ) 709 return distorted_polygons[0] 710 711 else: 712 new_points = self.distort_points_based_on_internals(internals, polygon.points) 713 return Polygon.create(points=new_points) 714 715 # yapf: disable 716 def distort_polygon( 717 self, 718 config_or_config_generator: Union[ 719 Union[_T_CONFIG, Mapping[str, Any]], 720 Callable[ 721 [Tuple[int, int], RandomGenerator], 722 Union[_T_CONFIG, Mapping[str, Any]], 723 ], 724 ], 725 shapable_or_shape: Union[Shapable, Tuple[int, int]], 726 polygon: Polygon, 727 state: Optional[_T_STATE] = None, 728 rng: Optional[RandomGenerator] = None, 729 ): 730 # yapf: enable 731 internals = self.prepare_internals( 732 config_or_config_generator=config_or_config_generator, 733 state=state, 734 shapable_or_shape=shapable_or_shape, 735 rng=rng, 736 ) 737 return self.distort_polygon_based_on_internals(internals, polygon) 738 739 def distort_polygons_based_on_internals( 740 self, 741 internals: DistortionInternals[_T_CONFIG, _T_STATE], 742 polygons: Iterable[Polygon], 743 ): 744 internals.restore_rng_if_supported() 745 746 if self.func_polygons: 747 return self.func_polygons( 748 internals.config, 749 internals.state, 750 internals.shape, 751 polygons, 752 internals.rng, 753 ) 754 755 else: 756 new_polygons = [] 757 for polygon in polygons: 758 new_polygon = self.distort_polygon_based_on_internals(internals, polygon) 759 new_polygons.append(new_polygon) 760 return new_polygons 761 762 # yapf: disable 763 def distort_polygons( 764 self, 765 config_or_config_generator: Union[ 766 Union[_T_CONFIG, Mapping[str, Any]], 767 Callable[ 768 [Tuple[int, int], RandomGenerator], 769 Union[_T_CONFIG, Mapping[str, Any]], 770 ], 771 ], 772 shapable_or_shape: Union[Shapable, Tuple[int, int]], 773 polygons: Iterable[Polygon], 774 state: Optional[_T_STATE] = None, 775 rng: Optional[RandomGenerator] = None, 776 ): 777 # yapf: enable 778 internals = self.prepare_internals( 779 config_or_config_generator=config_or_config_generator, 780 state=state, 781 shapable_or_shape=shapable_or_shape, 782 rng=rng, 783 ) 784 return self.distort_polygons_based_on_internals(internals, polygons) 785 786 @classmethod 787 def get_shape( 788 cls, 789 shapable_or_shape: Optional[Union[Shapable, Tuple[int, int]]] = None, 790 image: Optional[Image] = None, 791 mask: Optional[Mask] = None, 792 score_map: Optional[ScoreMap] = None, 793 ): 794 if shapable_or_shape is None: 795 shapable_or_shape = image or mask or score_map 796 assert shapable_or_shape 797 798 return cls.get_shape_from_shapable_or_shape(shapable_or_shape) 799 800 def clip_result_elements(self, result: DistortionResult): 801 if not self.is_geometric: 802 return 803 804 if result.point: 805 result.point = result.point.to_clipped_point(result.shape) 806 807 if result.points: 808 result.points = result.points.to_clipped_points(result.shape) 809 810 if result.corner_points: 811 result.corner_points = result.corner_points.to_clipped_points(result.shape) 812 813 if result.polygon: 814 result.polygon = result.polygon.to_clipped_polygon(result.shape) 815 816 if result.polygons: 817 result.polygons = [ 818 polygon.to_clipped_polygon(result.shape) for polygon in result.polygons 819 ] 820 821 # yapf: disable 822 def distort( 823 self, 824 config_or_config_generator: Union[ 825 Union[_T_CONFIG, Mapping[str, Any]], 826 Callable[ 827 [Tuple[int, int], RandomGenerator], 828 Union[_T_CONFIG, Mapping[str, Any]], 829 ], 830 ], 831 shapable_or_shape: Optional[Union[Shapable, Tuple[int, int]]] = None, 832 image: Optional[Image] = None, 833 mask: Optional[Mask] = None, 834 score_map: Optional[ScoreMap] = None, 835 point: Optional[Point] = None, 836 points: Optional[Union[PointList, PointTuple, Iterable[Point]]] = None, 837 corner_points: Optional[Union[PointList, PointTuple, Iterable[Point]]] = None, 838 polygon: Optional[Polygon] = None, 839 polygons: Optional[Iterable[Polygon]] = None, 840 get_active_mask: bool = False, 841 get_config: bool = False, 842 get_state: bool = False, 843 rng: Optional[RandomGenerator] = None, 844 ): 845 # yapf: enable 846 shape = self.get_shape( 847 shapable_or_shape=shapable_or_shape, 848 image=image, 849 mask=mask, 850 score_map=score_map, 851 ) 852 853 internals = self.prepare_internals( 854 config_or_config_generator=config_or_config_generator, 855 state=None, 856 shapable_or_shape=shape, 857 rng=rng, 858 ) 859 860 # If is geometric distortion, the shape will be updated. 861 result = DistortionResult(shape=shape) 862 863 if self.is_geometric: 864 assert internals.state and internals.state.result_shape 865 result.shape = internals.state.result_shape 866 else: 867 result.shape = shape 868 869 if image: 870 result.image = self.distort_image_based_on_internals(internals, image) 871 assert result.shape == result.image.shape 872 873 if mask: 874 result.mask = self.distort_mask_based_on_internals(internals, mask) 875 assert result.shape == result.mask.shape 876 877 if score_map: 878 result.score_map = self.distort_score_map_based_on_internals(internals, score_map) 879 assert result.shape == result.score_map.shape 880 881 if point: 882 result.point = self.distort_point_based_on_internals(internals, point) 883 884 if points: 885 result.points = self.distort_points_based_on_internals(internals, points) 886 887 if corner_points: 888 result.corner_points = self.distort_points_based_on_internals(internals, corner_points) 889 890 if polygon: 891 result.polygon = self.distort_polygon_based_on_internals(internals, polygon) 892 893 if polygons: 894 result.polygons = self.distort_polygons_based_on_internals(internals, polygons) 895 896 if get_active_mask: 897 result.active_mask = self.get_active_mask_based_on_internals(internals) 898 assert result.shape == result.active_mask.shape 899 900 if get_config: 901 result.config = internals.config 902 903 if get_state: 904 result.state = internals.state 905 906 self.clip_result_elements(result) 907 908 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 297 # Calling rng methods changes rng's state. We don't want to change the state 298 # of exterior rng, hence making a copy here. 299 rng = default_rng() 300 rng.bit_generator.state = config.rng_state 301 302 else: 303 # Force not passing rng. 304 rng = None 305 306 return config, rng 307 308 @classmethod 309 def get_shape_from_shapable_or_shape(cls, shapable_or_shape: Union[Shapable, Tuple[int, int]]): 310 if isinstance(shapable_or_shape, (list, tuple)): 311 assert len(shapable_or_shape) == 2 312 return shapable_or_shape 313 else: 314 return shapable_or_shape.shape 315 316 # yapf: disable 317 def prepare_internals( 318 self, 319 config_or_config_generator: Union[ 320 Union[_T_CONFIG, Mapping[str, Any]], 321 Callable[ 322 [Tuple[int, int], RandomGenerator], 323 Union[_T_CONFIG, Mapping[str, Any]], 324 ], 325 ], 326 state: Optional[_T_STATE], 327 shapable_or_shape: Union[Shapable, Tuple[int, int]], 328 rng: Optional[RandomGenerator] = None, 329 disable_state_initialization: bool = False, 330 ): 331 # yapf: enable 332 shape = self.get_shape_from_shapable_or_shape(shapable_or_shape) 333 334 config, rng = self.prepare_config_and_rng( 335 config_or_config_generator, 336 shape, 337 rng, 338 ) 339 340 if get_origin(self.state_cls) is not DistortionNopState: 341 if state is None and not disable_state_initialization: 342 state = self.state_cls(config, shape, rng) 343 else: 344 state = None 345 346 return DistortionInternals(config, state, shape, rng) 347 348 # yapf: disable 349 def generate_config_and_state( 350 self, 351 config_or_config_generator: Union[ 352 Union[_T_CONFIG, Mapping[str, Any]], 353 Callable[ 354 [Tuple[int, int], RandomGenerator], 355 Union[_T_CONFIG, Mapping[str, Any]], 356 ], 357 ], 358 state: Optional[_T_STATE], 359 shapable_or_shape: Union[Shapable, Tuple[int, int]], 360 rng: Optional[RandomGenerator] = None, 361 ): 362 # yapf: enable 363 internals = self.prepare_internals( 364 config_or_config_generator=config_or_config_generator, 365 state=state, 366 shapable_or_shape=shapable_or_shape, 367 rng=rng, 368 ) 369 return internals.config, internals.state 370 371 # yapf: disable 372 def generate_config( 373 self, 374 config_or_config_generator: Union[ 375 Union[_T_CONFIG, Mapping[str, Any]], 376 Callable[ 377 [Tuple[int, int], RandomGenerator], 378 Union[_T_CONFIG, Mapping[str, Any]], 379 ], 380 ], 381 shapable_or_shape: Union[Shapable, Tuple[int, int]], 382 rng: Optional[RandomGenerator] = None, 383 ): 384 # yapf: enable 385 internals = self.prepare_internals( 386 config_or_config_generator=config_or_config_generator, 387 state=None, 388 shapable_or_shape=shapable_or_shape, 389 rng=rng, 390 disable_state_initialization=True, 391 ) 392 return internals.config 393 394 # yapf: disable 395 def generate_state( 396 self, 397 config_or_config_generator: Union[ 398 Union[_T_CONFIG, Mapping[str, Any]], 399 Callable[ 400 [Tuple[int, int], RandomGenerator], 401 Union[_T_CONFIG, Mapping[str, Any]], 402 ], 403 ], 404 shapable_or_shape: Union[Shapable, Tuple[int, int]], 405 rng: Optional[RandomGenerator] = None, 406 ): 407 # yapf: enable 408 internals = self.prepare_internals( 409 config_or_config_generator=config_or_config_generator, 410 state=None, 411 shapable_or_shape=shapable_or_shape, 412 rng=rng, 413 ) 414 return internals.state 415 416 def distort_image_based_on_internals( 417 self, 418 internals: DistortionInternals[_T_CONFIG, _T_STATE], 419 image: Image, 420 ): 421 internals.restore_rng_if_supported() 422 423 return self.func_image( 424 internals.config, 425 internals.state, 426 image, 427 internals.rng, 428 ) 429 430 # yapf: disable 431 def distort_image( 432 self, 433 config_or_config_generator: Union[ 434 Union[_T_CONFIG, Mapping[str, Any]], 435 Callable[ 436 [Tuple[int, int], RandomGenerator], 437 Union[_T_CONFIG, Mapping[str, Any]], 438 ], 439 ], 440 image: Image, 441 state: Optional[_T_STATE] = None, 442 rng: Optional[RandomGenerator] = None, 443 ): 444 # yapf: enable 445 internals = self.prepare_internals( 446 config_or_config_generator=config_or_config_generator, 447 state=state, 448 shapable_or_shape=image, 449 rng=rng, 450 ) 451 return self.distort_image_based_on_internals(internals, image) 452 453 def distort_score_map_based_on_internals( 454 self, 455 internals: DistortionInternals[_T_CONFIG, _T_STATE], 456 score_map: ScoreMap, 457 ): 458 internals.restore_rng_if_supported() 459 460 if self.func_score_map: 461 return self.func_score_map( 462 internals.config, 463 internals.state, 464 score_map, 465 internals.rng, 466 ) 467 468 else: 469 # NOP. 470 return score_map 471 472 # yapf: disable 473 def distort_score_map( 474 self, 475 config_or_config_generator: Union[ 476 Union[_T_CONFIG, Mapping[str, Any]], 477 Callable[ 478 [Tuple[int, int], RandomGenerator], 479 Union[_T_CONFIG, Mapping[str, Any]], 480 ], 481 ], 482 score_map: ScoreMap, 483 state: Optional[_T_STATE] = None, 484 rng: Optional[RandomGenerator] = None, 485 ): 486 # yapf: enable 487 internals = self.prepare_internals( 488 config_or_config_generator=config_or_config_generator, 489 state=state, 490 shapable_or_shape=score_map, 491 rng=rng, 492 ) 493 return self.distort_score_map_based_on_internals(internals, score_map) 494 495 def distort_mask_based_on_internals( 496 self, 497 internals: DistortionInternals[_T_CONFIG, _T_STATE], 498 mask: Mask, 499 ): 500 internals.restore_rng_if_supported() 501 502 if self.func_mask: 503 return self.func_mask( 504 internals.config, 505 internals.state, 506 mask, 507 internals.rng, 508 ) 509 510 else: 511 # NOP. 512 return mask 513 514 # yapf: disable 515 def distort_mask( 516 self, 517 config_or_config_generator: Union[ 518 Union[_T_CONFIG, Mapping[str, Any]], 519 Callable[ 520 [Tuple[int, int], RandomGenerator], 521 Union[_T_CONFIG, Mapping[str, Any]], 522 ], 523 ], 524 mask: Mask, 525 state: Optional[_T_STATE] = None, 526 rng: Optional[RandomGenerator] = None, 527 ): 528 # yapf: enable 529 internals = self.prepare_internals( 530 config_or_config_generator=config_or_config_generator, 531 state=state, 532 shapable_or_shape=mask, 533 rng=rng, 534 ) 535 return self.distort_mask_based_on_internals(internals, mask) 536 537 def get_active_mask_based_on_internals( 538 self, 539 internals: DistortionInternals[_T_CONFIG, _T_STATE], 540 ): 541 # TODO: Something is wrong with cv.remap when dealing with border interpolation. 542 # This method could generate a mask "connects" to the transformed border. 543 internals.restore_rng_if_supported() 544 545 if self.func_active_mask: 546 return self.func_active_mask( 547 internals.config, 548 internals.state, 549 internals.shape, 550 internals.rng, 551 ) 552 553 else: 554 mask = Mask.from_shape(internals.shape, value=1) 555 return self.distort_mask_based_on_internals(internals, mask) 556 557 # yapf: disable 558 def get_active_mask( 559 self, 560 config_or_config_generator: Union[ 561 Union[_T_CONFIG, Mapping[str, Any]], 562 Callable[ 563 [Tuple[int, int], RandomGenerator], 564 Union[_T_CONFIG, Mapping[str, Any]], 565 ], 566 ], 567 shapable_or_shape: Union[Shapable, Tuple[int, int]], 568 state: Optional[_T_STATE] = None, 569 rng: Optional[RandomGenerator] = None, 570 ): 571 # yapf: enable 572 internals = self.prepare_internals( 573 config_or_config_generator=config_or_config_generator, 574 state=state, 575 shapable_or_shape=shapable_or_shape, 576 rng=rng, 577 ) 578 return self.get_active_mask_based_on_internals(internals) 579 580 def distort_point_based_on_internals( 581 self, 582 internals: DistortionInternals[_T_CONFIG, _T_STATE], 583 point: Point, 584 ): 585 internals.restore_rng_if_supported() 586 587 if self.func_point: 588 return self.func_point( 589 internals.config, 590 internals.state, 591 internals.shape, 592 point, 593 internals.rng, 594 ) 595 596 elif self.func_points: 597 distorted_points = self.func_points( 598 internals.config, 599 internals.state, 600 internals.shape, 601 [point], 602 internals.rng, 603 ) 604 return distorted_points[0] 605 606 else: 607 if self.is_geometric: 608 raise RuntimeError('Missing self.func_points or self.func_point.') 609 610 # NOP. 611 return point 612 613 # yapf: disable 614 def distort_point( 615 self, 616 config_or_config_generator: Union[ 617 Union[_T_CONFIG, Mapping[str, Any]], 618 Callable[ 619 [Tuple[int, int], RandomGenerator], 620 Union[_T_CONFIG, Mapping[str, Any]], 621 ], 622 ], 623 shapable_or_shape: Union[Shapable, Tuple[int, int]], 624 point: Point, 625 state: Optional[_T_STATE] = None, 626 rng: Optional[RandomGenerator] = None, 627 ): 628 # yapf: enable 629 internals = self.prepare_internals( 630 config_or_config_generator=config_or_config_generator, 631 state=state, 632 shapable_or_shape=shapable_or_shape, 633 rng=rng, 634 ) 635 return self.distort_point_based_on_internals(internals, point) 636 637 def distort_points_based_on_internals( 638 self, 639 internals: DistortionInternals[_T_CONFIG, _T_STATE], 640 points: Union[PointList, PointTuple, Iterable[Point]], 641 ): 642 internals.restore_rng_if_supported() 643 644 points = PointList(points) 645 646 if self.func_points: 647 return self.func_points( 648 internals.config, 649 internals.state, 650 internals.shape, 651 points, 652 internals.rng, 653 ) 654 655 else: 656 new_points = PointList() 657 for point in points: 658 new_point = self.distort_point_based_on_internals(internals, point) 659 new_points.append(new_point) 660 return new_points.to_point_tuple() 661 662 # yapf: disable 663 def distort_points( 664 self, 665 config_or_config_generator: Union[ 666 Union[_T_CONFIG, Mapping[str, Any]], 667 Callable[ 668 [Tuple[int, int], RandomGenerator], 669 Union[_T_CONFIG, Mapping[str, Any]], 670 ], 671 ], 672 shapable_or_shape: Union[Shapable, Tuple[int, int]], 673 points: Union[PointList, PointTuple, Iterable[Point]], 674 state: Optional[_T_STATE] = None, 675 rng: Optional[RandomGenerator] = None, 676 ): 677 # yapf: enable 678 internals = self.prepare_internals( 679 config_or_config_generator=config_or_config_generator, 680 state=state, 681 shapable_or_shape=shapable_or_shape, 682 rng=rng, 683 ) 684 return self.distort_points_based_on_internals(internals, points) 685 686 def distort_polygon_based_on_internals( 687 self, 688 internals: DistortionInternals[_T_CONFIG, _T_STATE], 689 polygon: Polygon, 690 ): 691 internals.restore_rng_if_supported() 692 693 if self.func_polygon: 694 return self.func_polygon( 695 internals.config, 696 internals.state, 697 internals.shape, 698 polygon, 699 internals.rng, 700 ) 701 702 elif self.func_polygons: 703 distorted_polygons = self.func_polygons( 704 internals.config, 705 internals.state, 706 internals.shape, 707 [polygon], 708 internals.rng, 709 ) 710 return distorted_polygons[0] 711 712 else: 713 new_points = self.distort_points_based_on_internals(internals, polygon.points) 714 return Polygon.create(points=new_points) 715 716 # yapf: disable 717 def distort_polygon( 718 self, 719 config_or_config_generator: Union[ 720 Union[_T_CONFIG, Mapping[str, Any]], 721 Callable[ 722 [Tuple[int, int], RandomGenerator], 723 Union[_T_CONFIG, Mapping[str, Any]], 724 ], 725 ], 726 shapable_or_shape: Union[Shapable, Tuple[int, int]], 727 polygon: Polygon, 728 state: Optional[_T_STATE] = None, 729 rng: Optional[RandomGenerator] = None, 730 ): 731 # yapf: enable 732 internals = self.prepare_internals( 733 config_or_config_generator=config_or_config_generator, 734 state=state, 735 shapable_or_shape=shapable_or_shape, 736 rng=rng, 737 ) 738 return self.distort_polygon_based_on_internals(internals, polygon) 739 740 def distort_polygons_based_on_internals( 741 self, 742 internals: DistortionInternals[_T_CONFIG, _T_STATE], 743 polygons: Iterable[Polygon], 744 ): 745 internals.restore_rng_if_supported() 746 747 if self.func_polygons: 748 return self.func_polygons( 749 internals.config, 750 internals.state, 751 internals.shape, 752 polygons, 753 internals.rng, 754 ) 755 756 else: 757 new_polygons = [] 758 for polygon in polygons: 759 new_polygon = self.distort_polygon_based_on_internals(internals, polygon) 760 new_polygons.append(new_polygon) 761 return new_polygons 762 763 # yapf: disable 764 def distort_polygons( 765 self, 766 config_or_config_generator: Union[ 767 Union[_T_CONFIG, Mapping[str, Any]], 768 Callable[ 769 [Tuple[int, int], RandomGenerator], 770 Union[_T_CONFIG, Mapping[str, Any]], 771 ], 772 ], 773 shapable_or_shape: Union[Shapable, Tuple[int, int]], 774 polygons: Iterable[Polygon], 775 state: Optional[_T_STATE] = None, 776 rng: Optional[RandomGenerator] = None, 777 ): 778 # yapf: enable 779 internals = self.prepare_internals( 780 config_or_config_generator=config_or_config_generator, 781 state=state, 782 shapable_or_shape=shapable_or_shape, 783 rng=rng, 784 ) 785 return self.distort_polygons_based_on_internals(internals, polygons) 786 787 @classmethod 788 def get_shape( 789 cls, 790 shapable_or_shape: Optional[Union[Shapable, Tuple[int, int]]] = None, 791 image: Optional[Image] = None, 792 mask: Optional[Mask] = None, 793 score_map: Optional[ScoreMap] = None, 794 ): 795 if shapable_or_shape is None: 796 shapable_or_shape = image or mask or score_map 797 assert shapable_or_shape 798 799 return cls.get_shape_from_shapable_or_shape(shapable_or_shape) 800 801 def clip_result_elements(self, result: DistortionResult): 802 if not self.is_geometric: 803 return 804 805 if result.point: 806 result.point = result.point.to_clipped_point(result.shape) 807 808 if result.points: 809 result.points = result.points.to_clipped_points(result.shape) 810 811 if result.corner_points: 812 result.corner_points = result.corner_points.to_clipped_points(result.shape) 813 814 if result.polygon: 815 result.polygon = result.polygon.to_clipped_polygon(result.shape) 816 817 if result.polygons: 818 result.polygons = [ 819 polygon.to_clipped_polygon(result.shape) for polygon in result.polygons 820 ] 821 822 # yapf: disable 823 def distort( 824 self, 825 config_or_config_generator: Union[ 826 Union[_T_CONFIG, Mapping[str, Any]], 827 Callable[ 828 [Tuple[int, int], RandomGenerator], 829 Union[_T_CONFIG, Mapping[str, Any]], 830 ], 831 ], 832 shapable_or_shape: Optional[Union[Shapable, Tuple[int, int]]] = None, 833 image: Optional[Image] = None, 834 mask: Optional[Mask] = None, 835 score_map: Optional[ScoreMap] = None, 836 point: Optional[Point] = None, 837 points: Optional[Union[PointList, PointTuple, Iterable[Point]]] = None, 838 corner_points: Optional[Union[PointList, PointTuple, Iterable[Point]]] = None, 839 polygon: Optional[Polygon] = None, 840 polygons: Optional[Iterable[Polygon]] = None, 841 get_active_mask: bool = False, 842 get_config: bool = False, 843 get_state: bool = False, 844 rng: Optional[RandomGenerator] = None, 845 ): 846 # yapf: enable 847 shape = self.get_shape( 848 shapable_or_shape=shapable_or_shape, 849 image=image, 850 mask=mask, 851 score_map=score_map, 852 ) 853 854 internals = self.prepare_internals( 855 config_or_config_generator=config_or_config_generator, 856 state=None, 857 shapable_or_shape=shape, 858 rng=rng, 859 ) 860 861 # If is geometric distortion, the shape will be updated. 862 result = DistortionResult(shape=shape) 863 864 if self.is_geometric: 865 assert internals.state and internals.state.result_shape 866 result.shape = internals.state.result_shape 867 else: 868 result.shape = shape 869 870 if image: 871 result.image = self.distort_image_based_on_internals(internals, image) 872 assert result.shape == result.image.shape 873 874 if mask: 875 result.mask = self.distort_mask_based_on_internals(internals, mask) 876 assert result.shape == result.mask.shape 877 878 if score_map: 879 result.score_map = self.distort_score_map_based_on_internals(internals, score_map) 880 assert result.shape == result.score_map.shape 881 882 if point: 883 result.point = self.distort_point_based_on_internals(internals, point) 884 885 if points: 886 result.points = self.distort_points_based_on_internals(internals, points) 887 888 if corner_points: 889 result.corner_points = self.distort_points_based_on_internals(internals, corner_points) 890 891 if polygon: 892 result.polygon = self.distort_polygon_based_on_internals(internals, polygon) 893 894 if polygons: 895 result.polygons = self.distort_polygons_based_on_internals(internals, polygons) 896 897 if get_active_mask: 898 result.active_mask = self.get_active_mask_based_on_internals(internals) 899 assert result.shape == result.active_mask.shape 900 901 if get_config: 902 result.config = internals.config 903 904 if get_state: 905 result.state = internals.state 906 907 self.clip_result_elements(result) 908 909 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 297 # Calling rng methods changes rng's state. We don't want to change the state 298 # of exterior rng, hence making a copy here. 299 rng = default_rng() 300 rng.bit_generator.state = config.rng_state 301 302 else: 303 # Force not passing rng. 304 rng = None 305 306 return config, rng
317 def prepare_internals( 318 self, 319 config_or_config_generator: Union[ 320 Union[_T_CONFIG, Mapping[str, Any]], 321 Callable[ 322 [Tuple[int, int], RandomGenerator], 323 Union[_T_CONFIG, Mapping[str, Any]], 324 ], 325 ], 326 state: Optional[_T_STATE], 327 shapable_or_shape: Union[Shapable, Tuple[int, int]], 328 rng: Optional[RandomGenerator] = None, 329 disable_state_initialization: bool = False, 330 ): 331 # yapf: enable 332 shape = self.get_shape_from_shapable_or_shape(shapable_or_shape) 333 334 config, rng = self.prepare_config_and_rng( 335 config_or_config_generator, 336 shape, 337 rng, 338 ) 339 340 if get_origin(self.state_cls) is not DistortionNopState: 341 if state is None and not disable_state_initialization: 342 state = self.state_cls(config, shape, rng) 343 else: 344 state = None 345 346 return DistortionInternals(config, state, shape, rng)
349 def generate_config_and_state( 350 self, 351 config_or_config_generator: Union[ 352 Union[_T_CONFIG, Mapping[str, Any]], 353 Callable[ 354 [Tuple[int, int], RandomGenerator], 355 Union[_T_CONFIG, Mapping[str, Any]], 356 ], 357 ], 358 state: Optional[_T_STATE], 359 shapable_or_shape: Union[Shapable, Tuple[int, int]], 360 rng: Optional[RandomGenerator] = None, 361 ): 362 # yapf: enable 363 internals = self.prepare_internals( 364 config_or_config_generator=config_or_config_generator, 365 state=state, 366 shapable_or_shape=shapable_or_shape, 367 rng=rng, 368 ) 369 return internals.config, internals.state
372 def generate_config( 373 self, 374 config_or_config_generator: Union[ 375 Union[_T_CONFIG, Mapping[str, Any]], 376 Callable[ 377 [Tuple[int, int], RandomGenerator], 378 Union[_T_CONFIG, Mapping[str, Any]], 379 ], 380 ], 381 shapable_or_shape: Union[Shapable, Tuple[int, int]], 382 rng: Optional[RandomGenerator] = None, 383 ): 384 # yapf: enable 385 internals = self.prepare_internals( 386 config_or_config_generator=config_or_config_generator, 387 state=None, 388 shapable_or_shape=shapable_or_shape, 389 rng=rng, 390 disable_state_initialization=True, 391 ) 392 return internals.config
395 def generate_state( 396 self, 397 config_or_config_generator: Union[ 398 Union[_T_CONFIG, Mapping[str, Any]], 399 Callable[ 400 [Tuple[int, int], RandomGenerator], 401 Union[_T_CONFIG, Mapping[str, Any]], 402 ], 403 ], 404 shapable_or_shape: Union[Shapable, Tuple[int, int]], 405 rng: Optional[RandomGenerator] = None, 406 ): 407 # yapf: enable 408 internals = self.prepare_internals( 409 config_or_config_generator=config_or_config_generator, 410 state=None, 411 shapable_or_shape=shapable_or_shape, 412 rng=rng, 413 ) 414 return internals.state
431 def distort_image( 432 self, 433 config_or_config_generator: Union[ 434 Union[_T_CONFIG, Mapping[str, Any]], 435 Callable[ 436 [Tuple[int, int], RandomGenerator], 437 Union[_T_CONFIG, Mapping[str, Any]], 438 ], 439 ], 440 image: Image, 441 state: Optional[_T_STATE] = None, 442 rng: Optional[RandomGenerator] = None, 443 ): 444 # yapf: enable 445 internals = self.prepare_internals( 446 config_or_config_generator=config_or_config_generator, 447 state=state, 448 shapable_or_shape=image, 449 rng=rng, 450 ) 451 return self.distort_image_based_on_internals(internals, image)
453 def distort_score_map_based_on_internals( 454 self, 455 internals: DistortionInternals[_T_CONFIG, _T_STATE], 456 score_map: ScoreMap, 457 ): 458 internals.restore_rng_if_supported() 459 460 if self.func_score_map: 461 return self.func_score_map( 462 internals.config, 463 internals.state, 464 score_map, 465 internals.rng, 466 ) 467 468 else: 469 # NOP. 470 return score_map
473 def distort_score_map( 474 self, 475 config_or_config_generator: Union[ 476 Union[_T_CONFIG, Mapping[str, Any]], 477 Callable[ 478 [Tuple[int, int], RandomGenerator], 479 Union[_T_CONFIG, Mapping[str, Any]], 480 ], 481 ], 482 score_map: ScoreMap, 483 state: Optional[_T_STATE] = None, 484 rng: Optional[RandomGenerator] = None, 485 ): 486 # yapf: enable 487 internals = self.prepare_internals( 488 config_or_config_generator=config_or_config_generator, 489 state=state, 490 shapable_or_shape=score_map, 491 rng=rng, 492 ) 493 return self.distort_score_map_based_on_internals(internals, score_map)
495 def distort_mask_based_on_internals( 496 self, 497 internals: DistortionInternals[_T_CONFIG, _T_STATE], 498 mask: Mask, 499 ): 500 internals.restore_rng_if_supported() 501 502 if self.func_mask: 503 return self.func_mask( 504 internals.config, 505 internals.state, 506 mask, 507 internals.rng, 508 ) 509 510 else: 511 # NOP. 512 return mask
515 def distort_mask( 516 self, 517 config_or_config_generator: Union[ 518 Union[_T_CONFIG, Mapping[str, Any]], 519 Callable[ 520 [Tuple[int, int], RandomGenerator], 521 Union[_T_CONFIG, Mapping[str, Any]], 522 ], 523 ], 524 mask: Mask, 525 state: Optional[_T_STATE] = None, 526 rng: Optional[RandomGenerator] = None, 527 ): 528 # yapf: enable 529 internals = self.prepare_internals( 530 config_or_config_generator=config_or_config_generator, 531 state=state, 532 shapable_or_shape=mask, 533 rng=rng, 534 ) 535 return self.distort_mask_based_on_internals(internals, mask)
537 def get_active_mask_based_on_internals( 538 self, 539 internals: DistortionInternals[_T_CONFIG, _T_STATE], 540 ): 541 # TODO: Something is wrong with cv.remap when dealing with border interpolation. 542 # This method could generate a mask "connects" to the transformed border. 543 internals.restore_rng_if_supported() 544 545 if self.func_active_mask: 546 return self.func_active_mask( 547 internals.config, 548 internals.state, 549 internals.shape, 550 internals.rng, 551 ) 552 553 else: 554 mask = Mask.from_shape(internals.shape, value=1) 555 return self.distort_mask_based_on_internals(internals, mask)
558 def get_active_mask( 559 self, 560 config_or_config_generator: Union[ 561 Union[_T_CONFIG, Mapping[str, Any]], 562 Callable[ 563 [Tuple[int, int], RandomGenerator], 564 Union[_T_CONFIG, Mapping[str, Any]], 565 ], 566 ], 567 shapable_or_shape: Union[Shapable, Tuple[int, int]], 568 state: Optional[_T_STATE] = None, 569 rng: Optional[RandomGenerator] = None, 570 ): 571 # yapf: enable 572 internals = self.prepare_internals( 573 config_or_config_generator=config_or_config_generator, 574 state=state, 575 shapable_or_shape=shapable_or_shape, 576 rng=rng, 577 ) 578 return self.get_active_mask_based_on_internals(internals)
580 def distort_point_based_on_internals( 581 self, 582 internals: DistortionInternals[_T_CONFIG, _T_STATE], 583 point: Point, 584 ): 585 internals.restore_rng_if_supported() 586 587 if self.func_point: 588 return self.func_point( 589 internals.config, 590 internals.state, 591 internals.shape, 592 point, 593 internals.rng, 594 ) 595 596 elif self.func_points: 597 distorted_points = self.func_points( 598 internals.config, 599 internals.state, 600 internals.shape, 601 [point], 602 internals.rng, 603 ) 604 return distorted_points[0] 605 606 else: 607 if self.is_geometric: 608 raise RuntimeError('Missing self.func_points or self.func_point.') 609 610 # NOP. 611 return point
614 def distort_point( 615 self, 616 config_or_config_generator: Union[ 617 Union[_T_CONFIG, Mapping[str, Any]], 618 Callable[ 619 [Tuple[int, int], RandomGenerator], 620 Union[_T_CONFIG, Mapping[str, Any]], 621 ], 622 ], 623 shapable_or_shape: Union[Shapable, Tuple[int, int]], 624 point: Point, 625 state: Optional[_T_STATE] = None, 626 rng: Optional[RandomGenerator] = None, 627 ): 628 # yapf: enable 629 internals = self.prepare_internals( 630 config_or_config_generator=config_or_config_generator, 631 state=state, 632 shapable_or_shape=shapable_or_shape, 633 rng=rng, 634 ) 635 return self.distort_point_based_on_internals(internals, point)
637 def distort_points_based_on_internals( 638 self, 639 internals: DistortionInternals[_T_CONFIG, _T_STATE], 640 points: Union[PointList, PointTuple, Iterable[Point]], 641 ): 642 internals.restore_rng_if_supported() 643 644 points = PointList(points) 645 646 if self.func_points: 647 return self.func_points( 648 internals.config, 649 internals.state, 650 internals.shape, 651 points, 652 internals.rng, 653 ) 654 655 else: 656 new_points = PointList() 657 for point in points: 658 new_point = self.distort_point_based_on_internals(internals, point) 659 new_points.append(new_point) 660 return new_points.to_point_tuple()
663 def distort_points( 664 self, 665 config_or_config_generator: Union[ 666 Union[_T_CONFIG, Mapping[str, Any]], 667 Callable[ 668 [Tuple[int, int], RandomGenerator], 669 Union[_T_CONFIG, Mapping[str, Any]], 670 ], 671 ], 672 shapable_or_shape: Union[Shapable, Tuple[int, int]], 673 points: Union[PointList, PointTuple, Iterable[Point]], 674 state: Optional[_T_STATE] = None, 675 rng: Optional[RandomGenerator] = None, 676 ): 677 # yapf: enable 678 internals = self.prepare_internals( 679 config_or_config_generator=config_or_config_generator, 680 state=state, 681 shapable_or_shape=shapable_or_shape, 682 rng=rng, 683 ) 684 return self.distort_points_based_on_internals(internals, points)
686 def distort_polygon_based_on_internals( 687 self, 688 internals: DistortionInternals[_T_CONFIG, _T_STATE], 689 polygon: Polygon, 690 ): 691 internals.restore_rng_if_supported() 692 693 if self.func_polygon: 694 return self.func_polygon( 695 internals.config, 696 internals.state, 697 internals.shape, 698 polygon, 699 internals.rng, 700 ) 701 702 elif self.func_polygons: 703 distorted_polygons = self.func_polygons( 704 internals.config, 705 internals.state, 706 internals.shape, 707 [polygon], 708 internals.rng, 709 ) 710 return distorted_polygons[0] 711 712 else: 713 new_points = self.distort_points_based_on_internals(internals, polygon.points) 714 return Polygon.create(points=new_points)
717 def distort_polygon( 718 self, 719 config_or_config_generator: Union[ 720 Union[_T_CONFIG, Mapping[str, Any]], 721 Callable[ 722 [Tuple[int, int], RandomGenerator], 723 Union[_T_CONFIG, Mapping[str, Any]], 724 ], 725 ], 726 shapable_or_shape: Union[Shapable, Tuple[int, int]], 727 polygon: Polygon, 728 state: Optional[_T_STATE] = None, 729 rng: Optional[RandomGenerator] = None, 730 ): 731 # yapf: enable 732 internals = self.prepare_internals( 733 config_or_config_generator=config_or_config_generator, 734 state=state, 735 shapable_or_shape=shapable_or_shape, 736 rng=rng, 737 ) 738 return self.distort_polygon_based_on_internals(internals, polygon)
740 def distort_polygons_based_on_internals( 741 self, 742 internals: DistortionInternals[_T_CONFIG, _T_STATE], 743 polygons: Iterable[Polygon], 744 ): 745 internals.restore_rng_if_supported() 746 747 if self.func_polygons: 748 return self.func_polygons( 749 internals.config, 750 internals.state, 751 internals.shape, 752 polygons, 753 internals.rng, 754 ) 755 756 else: 757 new_polygons = [] 758 for polygon in polygons: 759 new_polygon = self.distort_polygon_based_on_internals(internals, polygon) 760 new_polygons.append(new_polygon) 761 return new_polygons
764 def distort_polygons( 765 self, 766 config_or_config_generator: Union[ 767 Union[_T_CONFIG, Mapping[str, Any]], 768 Callable[ 769 [Tuple[int, int], RandomGenerator], 770 Union[_T_CONFIG, Mapping[str, Any]], 771 ], 772 ], 773 shapable_or_shape: Union[Shapable, Tuple[int, int]], 774 polygons: Iterable[Polygon], 775 state: Optional[_T_STATE] = None, 776 rng: Optional[RandomGenerator] = None, 777 ): 778 # yapf: enable 779 internals = self.prepare_internals( 780 config_or_config_generator=config_or_config_generator, 781 state=state, 782 shapable_or_shape=shapable_or_shape, 783 rng=rng, 784 ) 785 return self.distort_polygons_based_on_internals(internals, polygons)
787 @classmethod 788 def get_shape( 789 cls, 790 shapable_or_shape: Optional[Union[Shapable, Tuple[int, int]]] = None, 791 image: Optional[Image] = None, 792 mask: Optional[Mask] = None, 793 score_map: Optional[ScoreMap] = None, 794 ): 795 if shapable_or_shape is None: 796 shapable_or_shape = image or mask or score_map 797 assert shapable_or_shape 798 799 return cls.get_shape_from_shapable_or_shape(shapable_or_shape)
801 def clip_result_elements(self, result: DistortionResult): 802 if not self.is_geometric: 803 return 804 805 if result.point: 806 result.point = result.point.to_clipped_point(result.shape) 807 808 if result.points: 809 result.points = result.points.to_clipped_points(result.shape) 810 811 if result.corner_points: 812 result.corner_points = result.corner_points.to_clipped_points(result.shape) 813 814 if result.polygon: 815 result.polygon = result.polygon.to_clipped_polygon(result.shape) 816 817 if result.polygons: 818 result.polygons = [ 819 polygon.to_clipped_polygon(result.shape) for polygon in result.polygons 820 ]
823 def distort( 824 self, 825 config_or_config_generator: Union[ 826 Union[_T_CONFIG, Mapping[str, Any]], 827 Callable[ 828 [Tuple[int, int], RandomGenerator], 829 Union[_T_CONFIG, Mapping[str, Any]], 830 ], 831 ], 832 shapable_or_shape: Optional[Union[Shapable, Tuple[int, int]]] = None, 833 image: Optional[Image] = None, 834 mask: Optional[Mask] = None, 835 score_map: Optional[ScoreMap] = None, 836 point: Optional[Point] = None, 837 points: Optional[Union[PointList, PointTuple, Iterable[Point]]] = None, 838 corner_points: Optional[Union[PointList, PointTuple, Iterable[Point]]] = None, 839 polygon: Optional[Polygon] = None, 840 polygons: Optional[Iterable[Polygon]] = None, 841 get_active_mask: bool = False, 842 get_config: bool = False, 843 get_state: bool = False, 844 rng: Optional[RandomGenerator] = None, 845 ): 846 # yapf: enable 847 shape = self.get_shape( 848 shapable_or_shape=shapable_or_shape, 849 image=image, 850 mask=mask, 851 score_map=score_map, 852 ) 853 854 internals = self.prepare_internals( 855 config_or_config_generator=config_or_config_generator, 856 state=None, 857 shapable_or_shape=shape, 858 rng=rng, 859 ) 860 861 # If is geometric distortion, the shape will be updated. 862 result = DistortionResult(shape=shape) 863 864 if self.is_geometric: 865 assert internals.state and internals.state.result_shape 866 result.shape = internals.state.result_shape 867 else: 868 result.shape = shape 869 870 if image: 871 result.image = self.distort_image_based_on_internals(internals, image) 872 assert result.shape == result.image.shape 873 874 if mask: 875 result.mask = self.distort_mask_based_on_internals(internals, mask) 876 assert result.shape == result.mask.shape 877 878 if score_map: 879 result.score_map = self.distort_score_map_based_on_internals(internals, score_map) 880 assert result.shape == result.score_map.shape 881 882 if point: 883 result.point = self.distort_point_based_on_internals(internals, point) 884 885 if points: 886 result.points = self.distort_points_based_on_internals(internals, points) 887 888 if corner_points: 889 result.corner_points = self.distort_points_based_on_internals(internals, corner_points) 890 891 if polygon: 892 result.polygon = self.distort_polygon_based_on_internals(internals, polygon) 893 894 if polygons: 895 result.polygons = self.distort_polygons_based_on_internals(internals, polygons) 896 897 if get_active_mask: 898 result.active_mask = self.get_active_mask_based_on_internals(internals) 899 assert result.shape == result.active_mask.shape 900 901 if get_config: 902 result.config = internals.config 903 904 if get_state: 905 result.state = internals.state 906 907 self.clip_result_elements(result) 908 909 return result