vkit.mechanism.distortion_policy.photometric.color

  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 Tuple
 15
 16import attrs
 17from numpy.random import Generator as RandomGenerator
 18
 19from vkit.mechanism import distortion
 20from ..type import DistortionConfigGenerator, DistortionPolicyFactory
 21from ..opt import LEVEL_MAX, sample_int, sample_float, sample_channels
 22
 23
 24@attrs.define
 25class MeanShiftConfigGeneratorConfig:
 26    delta_max: int = 127
 27    prob_negative: float = 0.5
 28    prob_enable_threshold: float = 0.5
 29    threshold_ratio_min: float = 1.0
 30    threshold_ratio_max: float = 1.5
 31
 32
 33class MeanShiftConfigGenerator(
 34    DistortionConfigGenerator[
 35        MeanShiftConfigGeneratorConfig,
 36        distortion.MeanShiftConfig,
 37    ]
 38):  # yapf: disable
 39
 40    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
 41        delta = sample_int(
 42            level=self.level,
 43            value_min=0,
 44            value_max=self.config.delta_max,
 45            prob_negative=self.config.prob_negative,
 46            rng=rng,
 47        )
 48
 49        channels = sample_channels(rng)
 50
 51        threshold = None
 52        if rng.random() < self.config.prob_enable_threshold:
 53            ratio = rng.uniform(
 54                self.config.threshold_ratio_min,
 55                self.config.threshold_ratio_max,
 56            )
 57            if delta < 0:
 58                threshold = round(-delta * ratio)
 59            else:
 60                threshold = round(255 - delta * ratio)
 61
 62        return distortion.MeanShiftConfig(
 63            delta=delta,
 64            channels=channels,
 65            threshold=threshold,
 66        )
 67
 68
 69mean_shift_policy_factory = DistortionPolicyFactory(
 70    distortion.mean_shift,
 71    MeanShiftConfigGenerator,
 72)
 73
 74
 75@attrs.define
 76class ColorShiftConfigGeneratorConfig:
 77    delta_max: int = 127
 78    prob_negative: float = 0.5
 79
 80
 81class ColorShiftConfigGenerator(
 82    DistortionConfigGenerator[
 83        ColorShiftConfigGeneratorConfig,
 84        distortion.ColorShiftConfig,
 85    ]
 86):  # yapf: disable
 87
 88    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
 89        delta = sample_int(
 90            level=self.level,
 91            value_min=0,
 92            value_max=self.config.delta_max,
 93            prob_negative=self.config.prob_negative,
 94            rng=rng,
 95        )
 96
 97        return distortion.ColorShiftConfig(delta=delta)
 98
 99
100color_shift_policy_factory = DistortionPolicyFactory(
101    distortion.color_shift,
102    ColorShiftConfigGenerator,
103)
104
105
106@attrs.define
107class BrightnessShiftConfigGeneratorConfig:
108    delta_max: int = 127
109    prob_negative: float = 0.5
110
111
112class BrightnessShiftConfigGenerator(
113    DistortionConfigGenerator[
114        BrightnessShiftConfigGeneratorConfig,
115        distortion.BrightnessShiftConfig,
116    ]
117):  # yapf: disable
118
119    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
120        delta = sample_int(
121            level=self.level,
122            value_min=0,
123            value_max=self.config.delta_max,
124            prob_negative=self.config.prob_negative,
125            rng=rng,
126        )
127
128        return distortion.BrightnessShiftConfig(delta=delta)
129
130
131brightness_shift_policy_factory = DistortionPolicyFactory(
132    distortion.brightness_shift,
133    BrightnessShiftConfigGenerator,
134)
135
136
137@attrs.define
138class StdShiftConfigGeneratorConfig:
139    scale_min: float = 1.0
140    scale_max: float = 2.5
141    prob_reciprocal: float = 0.5
142
143
144class StdShiftConfigGenerator(
145    DistortionConfigGenerator[
146        StdShiftConfigGeneratorConfig,
147        distortion.StdShiftConfig,
148    ]
149):  # yapf: disable
150
151    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
152        scale = sample_float(
153            level=self.level,
154            value_min=self.config.scale_min,
155            value_max=self.config.scale_max,
156            prob_reciprocal=self.config.prob_reciprocal,
157            rng=rng,
158        )
159        channels = sample_channels(rng)
160
161        return distortion.StdShiftConfig(
162            scale=scale,
163            channels=channels,
164        )
165
166
167std_shift_policy_factory = DistortionPolicyFactory(
168    distortion.std_shift,
169    StdShiftConfigGenerator,
170)
171
172
173@attrs.define
174class BoundaryEqualizationConfigGeneratorConfig:
175    pass
176
177
178class BoundaryEqualizationConfigGenerator(
179    DistortionConfigGenerator[
180        BoundaryEqualizationConfigGeneratorConfig,
181        distortion.BoundaryEqualizationConfig,
182    ]
183):  # yapf: disable
184
185    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
186        channels = sample_channels(rng)
187
188        return distortion.BoundaryEqualizationConfig(channels=channels)
189
190
191boundary_equalization_policy_factory = DistortionPolicyFactory(
192    distortion.boundary_equalization,
193    BoundaryEqualizationConfigGenerator,
194)
195
196
197@attrs.define
198class HistogramEqualizationConfigGeneratorConfig:
199    pass
200
201
202class HistogramEqualizationConfigGenerator(
203    DistortionConfigGenerator[
204        HistogramEqualizationConfigGeneratorConfig,
205        distortion.HistogramEqualizationConfig,
206    ]
207):  # yapf: disable
208
209    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
210        channels = sample_channels(rng)
211
212        return distortion.HistogramEqualizationConfig(channels=channels)
213
214
215histogram_equalization_policy_factory = DistortionPolicyFactory(
216    distortion.histogram_equalization,
217    HistogramEqualizationConfigGenerator,
218)
219
220
221@attrs.define
222class ComplementConfigGeneratorConfig:
223    enable_threshold_level: int = 6
224    threshold_min: int = 77
225    threshold_max: int = 177
226
227
228class ComplementConfigGenerator(
229    DistortionConfigGenerator[
230        ComplementConfigGeneratorConfig,
231        distortion.ComplementConfig,
232    ]
233):  # yapf: disable
234
235    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
236        channels = sample_channels(rng)
237
238        threshold = None
239        enable_threshold_lte = (rng.random() < 0.5)
240        if self.level >= self.config.enable_threshold_level:
241            threshold = rng.integers(self.config.threshold_min, self.config.threshold_max + 1)
242
243        return distortion.ComplementConfig(
244            threshold=threshold,
245            enable_threshold_lte=enable_threshold_lte,
246            channels=channels,
247        )
248
249
250complement_policy_factory = DistortionPolicyFactory(
251    distortion.complement,
252    ComplementConfigGenerator,
253)
254
255
256@attrs.define
257class PosterizationConfigGeneratorConfig:
258    enable_threshold_level: int = 6
259    threshold_min: int = 77
260    threshold_max: int = 177
261
262
263class PosterizationConfigGenerator(
264    DistortionConfigGenerator[
265        PosterizationConfigGeneratorConfig,
266        distortion.PosterizationConfig,
267    ]
268):  # yapf: disable
269
270    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
271        # To [1, 7].
272        num_bits = round(self.level / LEVEL_MAX * 7)
273        channels = sample_channels(rng)
274
275        return distortion.PosterizationConfig(
276            num_bits=num_bits,
277            channels=channels,
278        )
279
280
281posterization_policy_factory = DistortionPolicyFactory(
282    distortion.posterization,
283    PosterizationConfigGenerator,
284)
285
286
287@attrs.define
288class ColorBalanceConfigGeneratorConfig:
289    ratio_min: float = 0.0
290    ratio_max: float = 1.0
291
292
293class ColorBalanceConfigGenerator(
294    DistortionConfigGenerator[
295        ColorBalanceConfigGeneratorConfig,
296        distortion.ColorBalanceConfig,
297    ]
298):  # yapf: disable
299
300    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
301        ratio = sample_float(
302            level=self.level,
303            value_min=self.config.ratio_min,
304            value_max=self.config.ratio_max,
305            prob_reciprocal=None,
306            rng=rng,
307            inverse_level=True,
308        )
309
310        return distortion.ColorBalanceConfig(ratio=ratio)
311
312
313color_balance_policy_factory = DistortionPolicyFactory(
314    distortion.color_balance,
315    ColorBalanceConfigGenerator,
316)
317
318
319@attrs.define
320class ChannelPermutationConfigGeneratorConfig:
321    pass
322
323
324class ChannelPermutationConfigGenerator(
325    DistortionConfigGenerator[
326        ChannelPermutationConfigGeneratorConfig,
327        distortion.ChannelPermutationConfig,
328    ]
329):  # yapf: disable
330
331    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
332        return distortion.ChannelPermutationConfig()
333
334
335channel_permutation_policy_factory = DistortionPolicyFactory(
336    distortion.channel_permutation,
337    ChannelPermutationConfigGenerator,
338)
class MeanShiftConfigGeneratorConfig:
26class MeanShiftConfigGeneratorConfig:
27    delta_max: int = 127
28    prob_negative: float = 0.5
29    prob_enable_threshold: float = 0.5
30    threshold_ratio_min: float = 1.0
31    threshold_ratio_max: float = 1.5
MeanShiftConfigGeneratorConfig( delta_max: int = 127, prob_negative: float = 0.5, prob_enable_threshold: float = 0.5, threshold_ratio_min: float = 1.0, threshold_ratio_max: float = 1.5)
2def __init__(self, delta_max=attr_dict['delta_max'].default, prob_negative=attr_dict['prob_negative'].default, prob_enable_threshold=attr_dict['prob_enable_threshold'].default, threshold_ratio_min=attr_dict['threshold_ratio_min'].default, threshold_ratio_max=attr_dict['threshold_ratio_max'].default):
3    self.delta_max = delta_max
4    self.prob_negative = prob_negative
5    self.prob_enable_threshold = prob_enable_threshold
6    self.threshold_ratio_min = threshold_ratio_min
7    self.threshold_ratio_max = threshold_ratio_max

Method generated by attrs for class MeanShiftConfigGeneratorConfig.

34class MeanShiftConfigGenerator(
35    DistortionConfigGenerator[
36        MeanShiftConfigGeneratorConfig,
37        distortion.MeanShiftConfig,
38    ]
39):  # yapf: disable
40
41    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
42        delta = sample_int(
43            level=self.level,
44            value_min=0,
45            value_max=self.config.delta_max,
46            prob_negative=self.config.prob_negative,
47            rng=rng,
48        )
49
50        channels = sample_channels(rng)
51
52        threshold = None
53        if rng.random() < self.config.prob_enable_threshold:
54            ratio = rng.uniform(
55                self.config.threshold_ratio_min,
56                self.config.threshold_ratio_max,
57            )
58            if delta < 0:
59                threshold = round(-delta * ratio)
60            else:
61                threshold = round(255 - delta * ratio)
62
63        return distortion.MeanShiftConfig(
64            delta=delta,
65            channels=channels,
66            threshold=threshold,
67        )

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

class ColorShiftConfigGeneratorConfig:
77class ColorShiftConfigGeneratorConfig:
78    delta_max: int = 127
79    prob_negative: float = 0.5
ColorShiftConfigGeneratorConfig(delta_max: int = 127, prob_negative: float = 0.5)
2def __init__(self, delta_max=attr_dict['delta_max'].default, prob_negative=attr_dict['prob_negative'].default):
3    self.delta_max = delta_max
4    self.prob_negative = prob_negative

Method generated by attrs for class ColorShiftConfigGeneratorConfig.

82class ColorShiftConfigGenerator(
83    DistortionConfigGenerator[
84        ColorShiftConfigGeneratorConfig,
85        distortion.ColorShiftConfig,
86    ]
87):  # yapf: disable
88
89    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
90        delta = sample_int(
91            level=self.level,
92            value_min=0,
93            value_max=self.config.delta_max,
94            prob_negative=self.config.prob_negative,
95            rng=rng,
96        )
97
98        return distortion.ColorShiftConfig(delta=delta)

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

class BrightnessShiftConfigGeneratorConfig:
108class BrightnessShiftConfigGeneratorConfig:
109    delta_max: int = 127
110    prob_negative: float = 0.5
BrightnessShiftConfigGeneratorConfig(delta_max: int = 127, prob_negative: float = 0.5)
2def __init__(self, delta_max=attr_dict['delta_max'].default, prob_negative=attr_dict['prob_negative'].default):
3    self.delta_max = delta_max
4    self.prob_negative = prob_negative

Method generated by attrs for class BrightnessShiftConfigGeneratorConfig.

113class BrightnessShiftConfigGenerator(
114    DistortionConfigGenerator[
115        BrightnessShiftConfigGeneratorConfig,
116        distortion.BrightnessShiftConfig,
117    ]
118):  # yapf: disable
119
120    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
121        delta = sample_int(
122            level=self.level,
123            value_min=0,
124            value_max=self.config.delta_max,
125            prob_negative=self.config.prob_negative,
126            rng=rng,
127        )
128
129        return distortion.BrightnessShiftConfig(delta=delta)

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

class StdShiftConfigGeneratorConfig:
139class StdShiftConfigGeneratorConfig:
140    scale_min: float = 1.0
141    scale_max: float = 2.5
142    prob_reciprocal: float = 0.5
StdShiftConfigGeneratorConfig( scale_min: float = 1.0, scale_max: float = 2.5, prob_reciprocal: float = 0.5)
2def __init__(self, scale_min=attr_dict['scale_min'].default, scale_max=attr_dict['scale_max'].default, prob_reciprocal=attr_dict['prob_reciprocal'].default):
3    self.scale_min = scale_min
4    self.scale_max = scale_max
5    self.prob_reciprocal = prob_reciprocal

Method generated by attrs for class StdShiftConfigGeneratorConfig.

145class StdShiftConfigGenerator(
146    DistortionConfigGenerator[
147        StdShiftConfigGeneratorConfig,
148        distortion.StdShiftConfig,
149    ]
150):  # yapf: disable
151
152    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
153        scale = sample_float(
154            level=self.level,
155            value_min=self.config.scale_min,
156            value_max=self.config.scale_max,
157            prob_reciprocal=self.config.prob_reciprocal,
158            rng=rng,
159        )
160        channels = sample_channels(rng)
161
162        return distortion.StdShiftConfig(
163            scale=scale,
164            channels=channels,
165        )

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

class BoundaryEqualizationConfigGeneratorConfig:
175class BoundaryEqualizationConfigGeneratorConfig:
176    pass
BoundaryEqualizationConfigGeneratorConfig()
2def __init__(self, ):
3    pass

Method generated by attrs for class BoundaryEqualizationConfigGeneratorConfig.

179class BoundaryEqualizationConfigGenerator(
180    DistortionConfigGenerator[
181        BoundaryEqualizationConfigGeneratorConfig,
182        distortion.BoundaryEqualizationConfig,
183    ]
184):  # yapf: disable
185
186    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
187        channels = sample_channels(rng)
188
189        return distortion.BoundaryEqualizationConfig(channels=channels)

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

class HistogramEqualizationConfigGeneratorConfig:
199class HistogramEqualizationConfigGeneratorConfig:
200    pass
HistogramEqualizationConfigGeneratorConfig()
2def __init__(self, ):
3    pass

Method generated by attrs for class HistogramEqualizationConfigGeneratorConfig.

203class HistogramEqualizationConfigGenerator(
204    DistortionConfigGenerator[
205        HistogramEqualizationConfigGeneratorConfig,
206        distortion.HistogramEqualizationConfig,
207    ]
208):  # yapf: disable
209
210    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
211        channels = sample_channels(rng)
212
213        return distortion.HistogramEqualizationConfig(channels=channels)

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

class ComplementConfigGeneratorConfig:
223class ComplementConfigGeneratorConfig:
224    enable_threshold_level: int = 6
225    threshold_min: int = 77
226    threshold_max: int = 177
ComplementConfigGeneratorConfig( enable_threshold_level: int = 6, threshold_min: int = 77, threshold_max: int = 177)
2def __init__(self, enable_threshold_level=attr_dict['enable_threshold_level'].default, threshold_min=attr_dict['threshold_min'].default, threshold_max=attr_dict['threshold_max'].default):
3    self.enable_threshold_level = enable_threshold_level
4    self.threshold_min = threshold_min
5    self.threshold_max = threshold_max

Method generated by attrs for class ComplementConfigGeneratorConfig.

229class ComplementConfigGenerator(
230    DistortionConfigGenerator[
231        ComplementConfigGeneratorConfig,
232        distortion.ComplementConfig,
233    ]
234):  # yapf: disable
235
236    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
237        channels = sample_channels(rng)
238
239        threshold = None
240        enable_threshold_lte = (rng.random() < 0.5)
241        if self.level >= self.config.enable_threshold_level:
242            threshold = rng.integers(self.config.threshold_min, self.config.threshold_max + 1)
243
244        return distortion.ComplementConfig(
245            threshold=threshold,
246            enable_threshold_lte=enable_threshold_lte,
247            channels=channels,
248        )

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

class PosterizationConfigGeneratorConfig:
258class PosterizationConfigGeneratorConfig:
259    enable_threshold_level: int = 6
260    threshold_min: int = 77
261    threshold_max: int = 177
PosterizationConfigGeneratorConfig( enable_threshold_level: int = 6, threshold_min: int = 77, threshold_max: int = 177)
2def __init__(self, enable_threshold_level=attr_dict['enable_threshold_level'].default, threshold_min=attr_dict['threshold_min'].default, threshold_max=attr_dict['threshold_max'].default):
3    self.enable_threshold_level = enable_threshold_level
4    self.threshold_min = threshold_min
5    self.threshold_max = threshold_max

Method generated by attrs for class PosterizationConfigGeneratorConfig.

264class PosterizationConfigGenerator(
265    DistortionConfigGenerator[
266        PosterizationConfigGeneratorConfig,
267        distortion.PosterizationConfig,
268    ]
269):  # yapf: disable
270
271    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
272        # To [1, 7].
273        num_bits = round(self.level / LEVEL_MAX * 7)
274        channels = sample_channels(rng)
275
276        return distortion.PosterizationConfig(
277            num_bits=num_bits,
278            channels=channels,
279        )

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

class ColorBalanceConfigGeneratorConfig:
289class ColorBalanceConfigGeneratorConfig:
290    ratio_min: float = 0.0
291    ratio_max: float = 1.0
ColorBalanceConfigGeneratorConfig(ratio_min: float = 0.0, ratio_max: float = 1.0)
2def __init__(self, ratio_min=attr_dict['ratio_min'].default, ratio_max=attr_dict['ratio_max'].default):
3    self.ratio_min = ratio_min
4    self.ratio_max = ratio_max

Method generated by attrs for class ColorBalanceConfigGeneratorConfig.

294class ColorBalanceConfigGenerator(
295    DistortionConfigGenerator[
296        ColorBalanceConfigGeneratorConfig,
297        distortion.ColorBalanceConfig,
298    ]
299):  # yapf: disable
300
301    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
302        ratio = sample_float(
303            level=self.level,
304            value_min=self.config.ratio_min,
305            value_max=self.config.ratio_max,
306            prob_reciprocal=None,
307            rng=rng,
308            inverse_level=True,
309        )
310
311        return distortion.ColorBalanceConfig(ratio=ratio)

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

class ChannelPermutationConfigGeneratorConfig:
321class ChannelPermutationConfigGeneratorConfig:
322    pass
ChannelPermutationConfigGeneratorConfig()
2def __init__(self, ):
3    pass

Method generated by attrs for class ChannelPermutationConfigGeneratorConfig.

325class ChannelPermutationConfigGenerator(
326    DistortionConfigGenerator[
327        ChannelPermutationConfigGeneratorConfig,
328        distortion.ChannelPermutationConfig,
329    ]
330):  # yapf: disable
331
332    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
333        return distortion.ChannelPermutationConfig()

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