vkit.mechanism.distortion_policy.geometric.camera

  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
 18import numpy as np
 19
 20from vkit.mechanism import distortion
 21from ..type import DistortionConfigGenerator, DistortionPolicyFactory
 22from ..opt import sample_int, sample_float, generate_grid_size
 23
 24
 25def sample_camera_model_config(
 26    level: int,
 27    level_1_max: int,
 28    rotation_theta_max: int,
 29    vec_z_max: float,
 30    rng: RandomGenerator,
 31):
 32    rotation_theta = sample_int(
 33        level=level,
 34        value_min=1,
 35        value_max=rotation_theta_max,
 36        prob_negative=0.5,
 37        rng=rng,
 38    )
 39
 40    theta_xy = rng.uniform(0, 2 * np.pi)
 41    vec_x = np.cos(theta_xy)
 42    vec_y = np.sin(theta_xy)
 43    vec_z = 0.0
 44
 45    if level > level_1_max:
 46        # NOTE:
 47        # 1. rotation_unit_vec will be normalized to unit vector in
 48        #    CameraModel.prep_rotation_unit_vec.
 49        # 2. If vec_z is 1.0, the camera model is equivalent to affine rotation.
 50        vec_z = rng.uniform(0, vec_z_max)
 51        vec_x = (1 - vec_z) * vec_x
 52        vec_y = (1 - vec_z) * vec_y
 53
 54    return distortion.CameraModelConfig(
 55        rotation_unit_vec=[vec_x, vec_y, vec_z],
 56        rotation_theta=rotation_theta,
 57    )
 58
 59
 60@attrs.define
 61class CameraPlaneOnlyConfigGeneratorConfig:
 62    level_1_max: int = 5
 63    rotation_theta_max: int = 17
 64    vec_z_max: float = 0.5
 65    grid_size_min: int = 15
 66    grid_size_ratio: float = 0.01
 67
 68
 69class CameraPlaneOnlyConfigGenerator(
 70    DistortionConfigGenerator[
 71        CameraPlaneOnlyConfigGeneratorConfig,
 72        distortion.CameraPlaneOnlyConfig,
 73    ]
 74):  # yapf: disable
 75
 76    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
 77        camera_model_config = sample_camera_model_config(
 78            level=self.level,
 79            level_1_max=self.config.level_1_max,
 80            vec_z_max=self.config.vec_z_max,
 81            rotation_theta_max=self.config.rotation_theta_max,
 82            rng=rng,
 83        )
 84        grid_size = generate_grid_size(
 85            self.config.grid_size_min,
 86            self.config.grid_size_ratio,
 87            shape,
 88        )
 89        return distortion.CameraPlaneOnlyConfig(
 90            camera_model_config=camera_model_config,
 91            grid_size=grid_size,
 92        )
 93
 94
 95camera_plane_only_policy_factory = DistortionPolicyFactory(
 96    distortion.camera_plane_only,
 97    CameraPlaneOnlyConfigGenerator,
 98)
 99
100
101@attrs.define
102class CameraCubicCurveConfigGeneratorConfig:
103    curve_slope_range_min: float = 10.0
104    curve_slope_range_max: float = 90.0
105    curve_slope_max: float = 45
106    level_1_max: int = 5
107    rotation_theta_max: int = 17
108    vec_z_max: float = 0.5
109    grid_size_min: int = 15
110    grid_size_ratio: float = 0.01
111
112
113class CameraCubicCurveConfigGenerator(
114    DistortionConfigGenerator[
115        CameraCubicCurveConfigGeneratorConfig,
116        distortion.CameraCubicCurveConfig,
117    ]
118):  # yapf: disable
119
120    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
121        curve_slope_range = sample_float(
122            level=self.level,
123            value_min=self.config.curve_slope_range_min,
124            value_max=self.config.curve_slope_range_max,
125            prob_reciprocal=None,
126            rng=rng,
127        )
128        alpha_ratio = rng.uniform()
129        curve_alpha = curve_slope_range * alpha_ratio
130        curve_beta = curve_slope_range - curve_alpha
131
132        # Clip.
133        curve_alpha = min(self.config.curve_slope_max, curve_alpha)
134        curve_beta = min(self.config.curve_slope_max, curve_beta)
135
136        if rng.random() < 0.5:
137            curve_alpha *= -1
138        if rng.random() < 0.5:
139            curve_beta *= -1
140
141        curve_direction = rng.uniform(0, 180)
142
143        camera_model_config = sample_camera_model_config(
144            level=self.level,
145            level_1_max=self.config.level_1_max,
146            rotation_theta_max=self.config.rotation_theta_max,
147            vec_z_max=self.config.vec_z_max,
148            rng=rng,
149        )
150        grid_size = generate_grid_size(
151            self.config.grid_size_min,
152            self.config.grid_size_ratio,
153            shape,
154        )
155        return distortion.CameraCubicCurveConfig(
156            curve_alpha=curve_alpha,
157            curve_beta=curve_beta,
158            curve_direction=curve_direction,
159            curve_scale=1.0,
160            camera_model_config=camera_model_config,
161            grid_size=grid_size,
162        )
163
164
165camera_cubic_curve_policy_factory = DistortionPolicyFactory(
166    distortion.camera_cubic_curve,
167    CameraCubicCurveConfigGenerator,
168)
169
170
171@attrs.define
172class CameraPlaneLineFoldConfigGeneratorConfig:
173    fold_alpha_min: float = 0.1
174    fold_alpha_max: float = 1.25
175    level_1_max: int = 5
176    rotation_theta_max: int = 17
177    vec_z_max: float = 0.5
178    grid_size_min: int = 15
179    grid_size_ratio: float = 0.01
180
181
182class CameraPlaneLineFoldConfigGenerator(
183    DistortionConfigGenerator[
184        CameraPlaneLineFoldConfigGeneratorConfig,
185        distortion.CameraPlaneLineFoldConfig,
186    ]
187):  # yapf: disable
188
189    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
190        height, width = shape
191        fold_point = (rng.integers(0, width), rng.integers(0, height))
192
193        fold_direction = rng.uniform(0, 180)
194
195        fold_perturb_vec_z = max(shape) / 4
196        if rng.random() < 0.5:
197            fold_perturb_vec_z *= -1.0
198        fold_perturb_vec = (0.0, 0.0, fold_perturb_vec_z)
199
200        fold_alpha = sample_float(
201            level=self.level,
202            value_min=self.config.fold_alpha_min,
203            value_max=self.config.fold_alpha_max,
204            prob_reciprocal=None,
205            rng=rng,
206            inverse_level=True,
207        )
208
209        camera_model_config = sample_camera_model_config(
210            level=self.level,
211            level_1_max=self.config.level_1_max,
212            rotation_theta_max=self.config.rotation_theta_max,
213            vec_z_max=self.config.vec_z_max,
214            rng=rng,
215        )
216        grid_size = generate_grid_size(
217            self.config.grid_size_min,
218            self.config.grid_size_ratio,
219            shape,
220        )
221        return distortion.CameraPlaneLineFoldConfig(
222            fold_point=fold_point,
223            fold_direction=fold_direction,
224            fold_perturb_vec=fold_perturb_vec,
225            fold_alpha=fold_alpha,
226            camera_model_config=camera_model_config,
227            grid_size=grid_size,
228        )
229
230
231camera_plane_line_fold_policy_factory = DistortionPolicyFactory(
232    distortion.camera_plane_line_fold,
233    CameraPlaneLineFoldConfigGenerator,
234)
235
236
237@attrs.define
238class CameraPlaneLineCurveConfigGeneratorConfig:
239    curve_alpha_min: float = 1.0
240    curve_alpha_max: float = 2.0
241    level_1_max: int = 5
242    rotation_theta_max: int = 17
243    vec_z_max: float = 0.5
244    grid_size_min: int = 15
245    grid_size_ratio: float = 0.01
246
247
248class CameraPlaneLineCurveConfigGenerator(
249    DistortionConfigGenerator[
250        CameraPlaneLineCurveConfigGeneratorConfig,
251        distortion.CameraPlaneLineCurveConfig,
252    ]
253):  # yapf: disable
254
255    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
256        height, width = shape
257        curve_point = (rng.integers(0, width), rng.integers(0, height))
258
259        curve_direction = rng.uniform(0, 180)
260
261        curve_perturb_vec_z = max(shape) / 4
262        if rng.random() < 0.5:
263            curve_perturb_vec_z *= -1.0
264        curve_perturb_vec = (0.0, 0.0, curve_perturb_vec_z)
265
266        curve_alpha = sample_float(
267            level=self.level,
268            value_min=self.config.curve_alpha_min,
269            value_max=self.config.curve_alpha_max,
270            prob_reciprocal=None,
271            rng=rng,
272            inverse_level=True,
273        )
274
275        camera_model_config = sample_camera_model_config(
276            level=self.level,
277            level_1_max=self.config.level_1_max,
278            rotation_theta_max=self.config.rotation_theta_max,
279            vec_z_max=self.config.vec_z_max,
280            rng=rng,
281        )
282        grid_size = generate_grid_size(
283            self.config.grid_size_min,
284            self.config.grid_size_ratio,
285            shape,
286        )
287        return distortion.CameraPlaneLineCurveConfig(
288            curve_point=curve_point,
289            curve_direction=curve_direction,
290            curve_perturb_vec=curve_perturb_vec,
291            curve_alpha=curve_alpha,
292            camera_model_config=camera_model_config,
293            grid_size=grid_size,
294        )
295
296
297camera_plane_line_curve_policy_factory = DistortionPolicyFactory(
298    distortion.camera_plane_line_curve,
299    CameraPlaneLineCurveConfigGenerator,
300)
def sample_camera_model_config( level: int, level_1_max: int, rotation_theta_max: int, vec_z_max: float, rng: numpy.random._generator.Generator):
26def sample_camera_model_config(
27    level: int,
28    level_1_max: int,
29    rotation_theta_max: int,
30    vec_z_max: float,
31    rng: RandomGenerator,
32):
33    rotation_theta = sample_int(
34        level=level,
35        value_min=1,
36        value_max=rotation_theta_max,
37        prob_negative=0.5,
38        rng=rng,
39    )
40
41    theta_xy = rng.uniform(0, 2 * np.pi)
42    vec_x = np.cos(theta_xy)
43    vec_y = np.sin(theta_xy)
44    vec_z = 0.0
45
46    if level > level_1_max:
47        # NOTE:
48        # 1. rotation_unit_vec will be normalized to unit vector in
49        #    CameraModel.prep_rotation_unit_vec.
50        # 2. If vec_z is 1.0, the camera model is equivalent to affine rotation.
51        vec_z = rng.uniform(0, vec_z_max)
52        vec_x = (1 - vec_z) * vec_x
53        vec_y = (1 - vec_z) * vec_y
54
55    return distortion.CameraModelConfig(
56        rotation_unit_vec=[vec_x, vec_y, vec_z],
57        rotation_theta=rotation_theta,
58    )
class CameraPlaneOnlyConfigGeneratorConfig:
62class CameraPlaneOnlyConfigGeneratorConfig:
63    level_1_max: int = 5
64    rotation_theta_max: int = 17
65    vec_z_max: float = 0.5
66    grid_size_min: int = 15
67    grid_size_ratio: float = 0.01
CameraPlaneOnlyConfigGeneratorConfig( level_1_max: int = 5, rotation_theta_max: int = 17, vec_z_max: float = 0.5, grid_size_min: int = 15, grid_size_ratio: float = 0.01)
2def __init__(self, level_1_max=attr_dict['level_1_max'].default, rotation_theta_max=attr_dict['rotation_theta_max'].default, vec_z_max=attr_dict['vec_z_max'].default, grid_size_min=attr_dict['grid_size_min'].default, grid_size_ratio=attr_dict['grid_size_ratio'].default):
3    self.level_1_max = level_1_max
4    self.rotation_theta_max = rotation_theta_max
5    self.vec_z_max = vec_z_max
6    self.grid_size_min = grid_size_min
7    self.grid_size_ratio = grid_size_ratio

Method generated by attrs for class CameraPlaneOnlyConfigGeneratorConfig.

70class CameraPlaneOnlyConfigGenerator(
71    DistortionConfigGenerator[
72        CameraPlaneOnlyConfigGeneratorConfig,
73        distortion.CameraPlaneOnlyConfig,
74    ]
75):  # yapf: disable
76
77    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
78        camera_model_config = sample_camera_model_config(
79            level=self.level,
80            level_1_max=self.config.level_1_max,
81            vec_z_max=self.config.vec_z_max,
82            rotation_theta_max=self.config.rotation_theta_max,
83            rng=rng,
84        )
85        grid_size = generate_grid_size(
86            self.config.grid_size_min,
87            self.config.grid_size_ratio,
88            shape,
89        )
90        return distortion.CameraPlaneOnlyConfig(
91            camera_model_config=camera_model_config,
92            grid_size=grid_size,
93        )

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 CameraCubicCurveConfigGeneratorConfig:
103class CameraCubicCurveConfigGeneratorConfig:
104    curve_slope_range_min: float = 10.0
105    curve_slope_range_max: float = 90.0
106    curve_slope_max: float = 45
107    level_1_max: int = 5
108    rotation_theta_max: int = 17
109    vec_z_max: float = 0.5
110    grid_size_min: int = 15
111    grid_size_ratio: float = 0.01
CameraCubicCurveConfigGeneratorConfig( curve_slope_range_min: float = 10.0, curve_slope_range_max: float = 90.0, curve_slope_max: float = 45, level_1_max: int = 5, rotation_theta_max: int = 17, vec_z_max: float = 0.5, grid_size_min: int = 15, grid_size_ratio: float = 0.01)
 2def __init__(self, curve_slope_range_min=attr_dict['curve_slope_range_min'].default, curve_slope_range_max=attr_dict['curve_slope_range_max'].default, curve_slope_max=attr_dict['curve_slope_max'].default, level_1_max=attr_dict['level_1_max'].default, rotation_theta_max=attr_dict['rotation_theta_max'].default, vec_z_max=attr_dict['vec_z_max'].default, grid_size_min=attr_dict['grid_size_min'].default, grid_size_ratio=attr_dict['grid_size_ratio'].default):
 3    self.curve_slope_range_min = curve_slope_range_min
 4    self.curve_slope_range_max = curve_slope_range_max
 5    self.curve_slope_max = curve_slope_max
 6    self.level_1_max = level_1_max
 7    self.rotation_theta_max = rotation_theta_max
 8    self.vec_z_max = vec_z_max
 9    self.grid_size_min = grid_size_min
10    self.grid_size_ratio = grid_size_ratio

Method generated by attrs for class CameraCubicCurveConfigGeneratorConfig.

114class CameraCubicCurveConfigGenerator(
115    DistortionConfigGenerator[
116        CameraCubicCurveConfigGeneratorConfig,
117        distortion.CameraCubicCurveConfig,
118    ]
119):  # yapf: disable
120
121    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
122        curve_slope_range = sample_float(
123            level=self.level,
124            value_min=self.config.curve_slope_range_min,
125            value_max=self.config.curve_slope_range_max,
126            prob_reciprocal=None,
127            rng=rng,
128        )
129        alpha_ratio = rng.uniform()
130        curve_alpha = curve_slope_range * alpha_ratio
131        curve_beta = curve_slope_range - curve_alpha
132
133        # Clip.
134        curve_alpha = min(self.config.curve_slope_max, curve_alpha)
135        curve_beta = min(self.config.curve_slope_max, curve_beta)
136
137        if rng.random() < 0.5:
138            curve_alpha *= -1
139        if rng.random() < 0.5:
140            curve_beta *= -1
141
142        curve_direction = rng.uniform(0, 180)
143
144        camera_model_config = sample_camera_model_config(
145            level=self.level,
146            level_1_max=self.config.level_1_max,
147            rotation_theta_max=self.config.rotation_theta_max,
148            vec_z_max=self.config.vec_z_max,
149            rng=rng,
150        )
151        grid_size = generate_grid_size(
152            self.config.grid_size_min,
153            self.config.grid_size_ratio,
154            shape,
155        )
156        return distortion.CameraCubicCurveConfig(
157            curve_alpha=curve_alpha,
158            curve_beta=curve_beta,
159            curve_direction=curve_direction,
160            curve_scale=1.0,
161            camera_model_config=camera_model_config,
162            grid_size=grid_size,
163        )

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 CameraPlaneLineFoldConfigGeneratorConfig:
173class CameraPlaneLineFoldConfigGeneratorConfig:
174    fold_alpha_min: float = 0.1
175    fold_alpha_max: float = 1.25
176    level_1_max: int = 5
177    rotation_theta_max: int = 17
178    vec_z_max: float = 0.5
179    grid_size_min: int = 15
180    grid_size_ratio: float = 0.01
CameraPlaneLineFoldConfigGeneratorConfig( fold_alpha_min: float = 0.1, fold_alpha_max: float = 1.25, level_1_max: int = 5, rotation_theta_max: int = 17, vec_z_max: float = 0.5, grid_size_min: int = 15, grid_size_ratio: float = 0.01)
2def __init__(self, fold_alpha_min=attr_dict['fold_alpha_min'].default, fold_alpha_max=attr_dict['fold_alpha_max'].default, level_1_max=attr_dict['level_1_max'].default, rotation_theta_max=attr_dict['rotation_theta_max'].default, vec_z_max=attr_dict['vec_z_max'].default, grid_size_min=attr_dict['grid_size_min'].default, grid_size_ratio=attr_dict['grid_size_ratio'].default):
3    self.fold_alpha_min = fold_alpha_min
4    self.fold_alpha_max = fold_alpha_max
5    self.level_1_max = level_1_max
6    self.rotation_theta_max = rotation_theta_max
7    self.vec_z_max = vec_z_max
8    self.grid_size_min = grid_size_min
9    self.grid_size_ratio = grid_size_ratio

Method generated by attrs for class CameraPlaneLineFoldConfigGeneratorConfig.

183class CameraPlaneLineFoldConfigGenerator(
184    DistortionConfigGenerator[
185        CameraPlaneLineFoldConfigGeneratorConfig,
186        distortion.CameraPlaneLineFoldConfig,
187    ]
188):  # yapf: disable
189
190    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
191        height, width = shape
192        fold_point = (rng.integers(0, width), rng.integers(0, height))
193
194        fold_direction = rng.uniform(0, 180)
195
196        fold_perturb_vec_z = max(shape) / 4
197        if rng.random() < 0.5:
198            fold_perturb_vec_z *= -1.0
199        fold_perturb_vec = (0.0, 0.0, fold_perturb_vec_z)
200
201        fold_alpha = sample_float(
202            level=self.level,
203            value_min=self.config.fold_alpha_min,
204            value_max=self.config.fold_alpha_max,
205            prob_reciprocal=None,
206            rng=rng,
207            inverse_level=True,
208        )
209
210        camera_model_config = sample_camera_model_config(
211            level=self.level,
212            level_1_max=self.config.level_1_max,
213            rotation_theta_max=self.config.rotation_theta_max,
214            vec_z_max=self.config.vec_z_max,
215            rng=rng,
216        )
217        grid_size = generate_grid_size(
218            self.config.grid_size_min,
219            self.config.grid_size_ratio,
220            shape,
221        )
222        return distortion.CameraPlaneLineFoldConfig(
223            fold_point=fold_point,
224            fold_direction=fold_direction,
225            fold_perturb_vec=fold_perturb_vec,
226            fold_alpha=fold_alpha,
227            camera_model_config=camera_model_config,
228            grid_size=grid_size,
229        )

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 CameraPlaneLineCurveConfigGeneratorConfig:
239class CameraPlaneLineCurveConfigGeneratorConfig:
240    curve_alpha_min: float = 1.0
241    curve_alpha_max: float = 2.0
242    level_1_max: int = 5
243    rotation_theta_max: int = 17
244    vec_z_max: float = 0.5
245    grid_size_min: int = 15
246    grid_size_ratio: float = 0.01
CameraPlaneLineCurveConfigGeneratorConfig( curve_alpha_min: float = 1.0, curve_alpha_max: float = 2.0, level_1_max: int = 5, rotation_theta_max: int = 17, vec_z_max: float = 0.5, grid_size_min: int = 15, grid_size_ratio: float = 0.01)
2def __init__(self, curve_alpha_min=attr_dict['curve_alpha_min'].default, curve_alpha_max=attr_dict['curve_alpha_max'].default, level_1_max=attr_dict['level_1_max'].default, rotation_theta_max=attr_dict['rotation_theta_max'].default, vec_z_max=attr_dict['vec_z_max'].default, grid_size_min=attr_dict['grid_size_min'].default, grid_size_ratio=attr_dict['grid_size_ratio'].default):
3    self.curve_alpha_min = curve_alpha_min
4    self.curve_alpha_max = curve_alpha_max
5    self.level_1_max = level_1_max
6    self.rotation_theta_max = rotation_theta_max
7    self.vec_z_max = vec_z_max
8    self.grid_size_min = grid_size_min
9    self.grid_size_ratio = grid_size_ratio

Method generated by attrs for class CameraPlaneLineCurveConfigGeneratorConfig.

249class CameraPlaneLineCurveConfigGenerator(
250    DistortionConfigGenerator[
251        CameraPlaneLineCurveConfigGeneratorConfig,
252        distortion.CameraPlaneLineCurveConfig,
253    ]
254):  # yapf: disable
255
256    def __call__(self, shape: Tuple[int, int], rng: RandomGenerator):
257        height, width = shape
258        curve_point = (rng.integers(0, width), rng.integers(0, height))
259
260        curve_direction = rng.uniform(0, 180)
261
262        curve_perturb_vec_z = max(shape) / 4
263        if rng.random() < 0.5:
264            curve_perturb_vec_z *= -1.0
265        curve_perturb_vec = (0.0, 0.0, curve_perturb_vec_z)
266
267        curve_alpha = sample_float(
268            level=self.level,
269            value_min=self.config.curve_alpha_min,
270            value_max=self.config.curve_alpha_max,
271            prob_reciprocal=None,
272            rng=rng,
273            inverse_level=True,
274        )
275
276        camera_model_config = sample_camera_model_config(
277            level=self.level,
278            level_1_max=self.config.level_1_max,
279            rotation_theta_max=self.config.rotation_theta_max,
280            vec_z_max=self.config.vec_z_max,
281            rng=rng,
282        )
283        grid_size = generate_grid_size(
284            self.config.grid_size_min,
285            self.config.grid_size_ratio,
286            shape,
287        )
288        return distortion.CameraPlaneLineCurveConfig(
289            curve_point=curve_point,
290            curve_direction=curve_direction,
291            curve_perturb_vec=curve_perturb_vec,
292            curve_alpha=curve_alpha,
293            camera_model_config=camera_model_config,
294            grid_size=grid_size,
295        )

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