vkit.mechanism.distortion.interface

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

Abstract base class for generic types.

A generic type is typically declared by inheriting from this class parameterized with one or more type variables. For example, a generic mapping type might be defined as::

class Mapping(Generic[KT, VT]): def __getitem__(self, key: KT) -> VT: ... # Etc.

This class can then be used as follows::

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: try: return mapping[key] except KeyError: return default

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