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
class DistortionConfig:
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
DistortionConfig()
@classmethod
def get_name(cls):
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
class DistortionState(typing.Generic[~_T_CONFIG]):
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

DistortionState( config: ~_T_CONFIG, shape: Tuple[int, int], rng: Union[numpy.random._generator.Generator, NoneType])
82    def __init__(
83        self,
84        config: _T_CONFIG,
85        shape: Tuple[int, int],
86        rng: Optional[RandomGenerator],
87    ):
88        raise NotImplementedError()
 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

DistortionNopState( config: ~_T_CONFIG, shape: Tuple[int, int], rng: Union[numpy.random._generator.Generator, NoneType])
 97    def __init__(
 98        self,
 99        config: _T_CONFIG,
100        shape: Tuple[int, int],
101        rng: Optional[RandomGenerator],
102    ):
103        raise NotImplementedError()
class DistortionResult:
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
DistortionResult( shape: Tuple[int, int], image: Union[vkit.element.image.Image, NoneType] = None, mask: Union[vkit.element.mask.Mask, NoneType] = None, score_map: Union[vkit.element.score_map.ScoreMap, NoneType] = None, active_mask: Union[vkit.element.mask.Mask, NoneType] = None, point: Union[vkit.element.point.Point, NoneType] = None, points: Union[vkit.element.point.PointTuple, NoneType] = None, corner_points: Union[vkit.element.point.PointTuple, NoneType] = None, polygon: Union[vkit.element.polygon.Polygon, NoneType] = None, polygons: Union[Sequence[vkit.element.polygon.Polygon], NoneType] = None, config: Union[Any, NoneType] = None, state: Union[Any, NoneType] = None, meta: Union[Mapping[str, Any], NoneType] = 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.

class DistortionInternals(typing.Generic[~_T_CONFIG, ~_T_STATE]):
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

DistortionInternals( config: ~_T_CONFIG, state: Union[~_T_STATE, NoneType], shape: Tuple[int, int], rng: Union[numpy.random._generator.Generator, NoneType])
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.

def restore_rng_if_supported(self):
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
class Distortion(typing.Generic[~_T_CONFIG, ~_T_STATE]):
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

Distortion( config_cls: Type[~_T_CONFIG], state_cls: Type[~_T_STATE], func_image: Callable[[~_T_CONFIG, Union[~_T_STATE, NoneType], vkit.element.image.Image, Union[numpy.random._generator.Generator, NoneType]], vkit.element.image.Image], func_mask: Union[Callable[[~_T_CONFIG, Union[~_T_STATE, NoneType], vkit.element.mask.Mask, Union[numpy.random._generator.Generator, NoneType]], vkit.element.mask.Mask], NoneType] = None, func_score_map: Union[Callable[[~_T_CONFIG, Union[~_T_STATE, NoneType], vkit.element.score_map.ScoreMap, Union[numpy.random._generator.Generator, NoneType]], vkit.element.score_map.ScoreMap], NoneType] = None, func_active_mask: Union[Callable[[~_T_CONFIG, Union[~_T_STATE, NoneType], Tuple[int, int], Union[numpy.random._generator.Generator, NoneType]], vkit.element.mask.Mask], NoneType] = None, func_point: Union[Callable[[~_T_CONFIG, Union[~_T_STATE, NoneType], Tuple[int, int], vkit.element.point.Point, Union[numpy.random._generator.Generator, NoneType]], vkit.element.point.Point], NoneType] = None, func_points: Union[Callable[[~_T_CONFIG, Union[~_T_STATE, NoneType], Tuple[int, int], Union[vkit.element.point.PointList, vkit.element.point.PointTuple, Iterable[vkit.element.point.Point]], Union[numpy.random._generator.Generator, NoneType]], vkit.element.point.PointTuple], NoneType] = None, func_polygon: Union[Callable[[~_T_CONFIG, Union[~_T_STATE, NoneType], Tuple[int, int], vkit.element.polygon.Polygon, Union[numpy.random._generator.Generator, NoneType]], vkit.element.polygon.Polygon], NoneType] = None, func_polygons: Union[Callable[[~_T_CONFIG, Union[~_T_STATE, NoneType], Tuple[int, int], Iterable[vkit.element.polygon.Polygon], Union[numpy.random._generator.Generator, NoneType]], Sequence[vkit.element.polygon.Polygon]], NoneType] = None)
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
def prepare_config_and_rng( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], shape: Tuple[int, int], rng: Union[numpy.random._generator.Generator, NoneType]) -> Tuple[~_T_CONFIG, Union[numpy.random._generator.Generator, NoneType]]:
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
@classmethod
def get_shape_from_shapable_or_shape( cls, shapable_or_shape: Union[vkit.element.type.Shapable, Tuple[int, int]]):
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
def prepare_internals( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], state: Union[~_T_STATE, NoneType], shapable_or_shape: Union[vkit.element.type.Shapable, Tuple[int, int]], rng: Union[numpy.random._generator.Generator, NoneType] = None, disable_state_initialization: bool = False):
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)
def generate_config_and_state( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], state: Union[~_T_STATE, NoneType], shapable_or_shape: Union[vkit.element.type.Shapable, Tuple[int, int]], rng: Union[numpy.random._generator.Generator, NoneType] = None):
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
def generate_config( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], shapable_or_shape: Union[vkit.element.type.Shapable, Tuple[int, int]], rng: Union[numpy.random._generator.Generator, NoneType] = None):
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
def generate_state( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], shapable_or_shape: Union[vkit.element.type.Shapable, Tuple[int, int]], rng: Union[numpy.random._generator.Generator, NoneType] = None):
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
def distort_image_based_on_internals( self, internals: vkit.mechanism.distortion.interface.DistortionInternals[~_T_CONFIG, ~_T_STATE], image: vkit.element.image.Image):
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        )
def distort_image( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], image: vkit.element.image.Image, state: Union[~_T_STATE, NoneType] = None, rng: Union[numpy.random._generator.Generator, NoneType] = None):
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)
def distort_score_map_based_on_internals( self, internals: vkit.mechanism.distortion.interface.DistortionInternals[~_T_CONFIG, ~_T_STATE], score_map: vkit.element.score_map.ScoreMap):
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
def distort_score_map( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], score_map: vkit.element.score_map.ScoreMap, state: Union[~_T_STATE, NoneType] = None, rng: Union[numpy.random._generator.Generator, NoneType] = None):
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)
def distort_mask_based_on_internals( self, internals: vkit.mechanism.distortion.interface.DistortionInternals[~_T_CONFIG, ~_T_STATE], mask: vkit.element.mask.Mask):
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
def distort_mask( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], mask: vkit.element.mask.Mask, state: Union[~_T_STATE, NoneType] = None, rng: Union[numpy.random._generator.Generator, NoneType] = None):
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)
def get_active_mask_based_on_internals( self, internals: vkit.mechanism.distortion.interface.DistortionInternals[~_T_CONFIG, ~_T_STATE]):
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)
def get_active_mask( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], shapable_or_shape: Union[vkit.element.type.Shapable, Tuple[int, int]], state: Union[~_T_STATE, NoneType] = None, rng: Union[numpy.random._generator.Generator, NoneType] = None):
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)
def distort_point_based_on_internals( self, internals: vkit.mechanism.distortion.interface.DistortionInternals[~_T_CONFIG, ~_T_STATE], point: vkit.element.point.Point):
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
def distort_point( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], shapable_or_shape: Union[vkit.element.type.Shapable, Tuple[int, int]], point: vkit.element.point.Point, state: Union[~_T_STATE, NoneType] = None, rng: Union[numpy.random._generator.Generator, NoneType] = None):
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)
def distort_points_based_on_internals( self, internals: vkit.mechanism.distortion.interface.DistortionInternals[~_T_CONFIG, ~_T_STATE], points: Union[vkit.element.point.PointList, vkit.element.point.PointTuple, Iterable[vkit.element.point.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()
def distort_points( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], shapable_or_shape: Union[vkit.element.type.Shapable, Tuple[int, int]], points: Union[vkit.element.point.PointList, vkit.element.point.PointTuple, Iterable[vkit.element.point.Point]], state: Union[~_T_STATE, NoneType] = None, rng: Union[numpy.random._generator.Generator, NoneType] = None):
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)
def distort_polygon_based_on_internals( self, internals: vkit.mechanism.distortion.interface.DistortionInternals[~_T_CONFIG, ~_T_STATE], polygon: vkit.element.polygon.Polygon):
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)
def distort_polygon( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], shapable_or_shape: Union[vkit.element.type.Shapable, Tuple[int, int]], polygon: vkit.element.polygon.Polygon, state: Union[~_T_STATE, NoneType] = None, rng: Union[numpy.random._generator.Generator, NoneType] = None):
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)
def distort_polygons_based_on_internals( self, internals: vkit.mechanism.distortion.interface.DistortionInternals[~_T_CONFIG, ~_T_STATE], polygons: Iterable[vkit.element.polygon.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
def distort_polygons( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], shapable_or_shape: Union[vkit.element.type.Shapable, Tuple[int, int]], polygons: Iterable[vkit.element.polygon.Polygon], state: Union[~_T_STATE, NoneType] = None, rng: Union[numpy.random._generator.Generator, NoneType] = None):
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)
@classmethod
def get_shape( cls, shapable_or_shape: Union[vkit.element.type.Shapable, Tuple[int, int], NoneType] = None, image: Union[vkit.element.image.Image, NoneType] = None, mask: Union[vkit.element.mask.Mask, NoneType] = None, score_map: Union[vkit.element.score_map.ScoreMap, NoneType] = None):
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)
def clip_result_elements(self, result: vkit.mechanism.distortion.interface.DistortionResult):
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            ]
def distort( self, config_or_config_generator: Union[~_T_CONFIG, Mapping[str, Any], Callable[[Tuple[int, int], numpy.random._generator.Generator], Union[~_T_CONFIG, Mapping[str, Any]]]], shapable_or_shape: Union[vkit.element.type.Shapable, Tuple[int, int], NoneType] = None, image: Union[vkit.element.image.Image, NoneType] = None, mask: Union[vkit.element.mask.Mask, NoneType] = None, score_map: Union[vkit.element.score_map.ScoreMap, NoneType] = None, point: Union[vkit.element.point.Point, NoneType] = None, points: Union[vkit.element.point.PointList, vkit.element.point.PointTuple, Iterable[vkit.element.point.Point], NoneType] = None, corner_points: Union[vkit.element.point.PointList, vkit.element.point.PointTuple, Iterable[vkit.element.point.Point], NoneType] = None, polygon: Union[vkit.element.polygon.Polygon, NoneType] = None, polygons: Union[Iterable[vkit.element.polygon.Polygon], NoneType] = None, get_active_mask: bool = False, get_config: bool = False, get_state: bool = False, rng: Union[numpy.random._generator.Generator, NoneType] = None):
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