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)
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
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
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
108class BrightnessShiftConfigGeneratorConfig: 109 delta_max: int = 127 110 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
139class StdShiftConfigGeneratorConfig: 140 scale_min: float = 1.0 141 scale_max: float = 2.5 142 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
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
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
223class ComplementConfigGeneratorConfig: 224 enable_threshold_level: int = 6 225 threshold_min: int = 77 226 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
258class PosterizationConfigGeneratorConfig: 259 enable_threshold_level: int = 6 260 threshold_min: int = 77 261 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
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
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