modelstudio-sdk 0.0.0.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- modelstudio/__init__.py +25 -0
- modelstudio/_http.py +116 -0
- modelstudio/_pandas.py +33 -0
- modelstudio/_polling.py +59 -0
- modelstudio/_version.py +1 -0
- modelstudio/client.py +108 -0
- modelstudio/exceptions.py +80 -0
- modelstudio/models/__init__.py +81 -0
- modelstudio/models/annotations.py +80 -0
- modelstudio/models/categories.py +106 -0
- modelstudio/models/common.py +18 -0
- modelstudio/models/datasets.py +55 -0
- modelstudio/models/deletion.py +29 -0
- modelstudio/models/exports.py +32 -0
- modelstudio/models/few_shot.py +41 -0
- modelstudio/models/filters.py +81 -0
- modelstudio/models/history.py +58 -0
- modelstudio/models/images.py +91 -0
- modelstudio/models/imports.py +141 -0
- modelstudio/models/media.py +37 -0
- modelstudio/models/merge.py +51 -0
- modelstudio/models/metrics.py +180 -0
- modelstudio/models/oversample.py +41 -0
- modelstudio/models/splits.py +112 -0
- modelstudio/models/validation.py +89 -0
- modelstudio/resources/__init__.py +1 -0
- modelstudio/resources/dataset.py +732 -0
- modelstudio/resources/split.py +140 -0
- modelstudio_sdk-0.0.0.dev0.dist-info/METADATA +513 -0
- modelstudio_sdk-0.0.0.dev0.dist-info/RECORD +32 -0
- modelstudio_sdk-0.0.0.dev0.dist-info/WHEEL +4 -0
- modelstudio_sdk-0.0.0.dev0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Metrics and statistics models (OverviewDto + 11 nested types)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DescriptiveStats(BaseModel):
|
|
11
|
+
"""Reusable descriptive statistics for any numeric distribution."""
|
|
12
|
+
|
|
13
|
+
count: int = 0
|
|
14
|
+
min: float = 0.0
|
|
15
|
+
max: float = 0.0
|
|
16
|
+
mean: float = 0.0
|
|
17
|
+
median: float = 0.0
|
|
18
|
+
mode: float | None = None
|
|
19
|
+
std_dev: float = 0.0
|
|
20
|
+
variance: float = 0.0
|
|
21
|
+
q1: float = 0.0
|
|
22
|
+
q3: float = 0.0
|
|
23
|
+
iqr: float = 0.0
|
|
24
|
+
p5: float = 0.0
|
|
25
|
+
p95: float = 0.0
|
|
26
|
+
skewness: float | None = None
|
|
27
|
+
outlier_count: int = 0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AspectRatioDistribution(BaseModel):
|
|
31
|
+
"""Aspect ratio distribution for images."""
|
|
32
|
+
|
|
33
|
+
portrait: int = 0
|
|
34
|
+
landscape: int = 0
|
|
35
|
+
square: int = 0
|
|
36
|
+
min_ratio: float = 0.0
|
|
37
|
+
max_ratio: float = 0.0
|
|
38
|
+
avg_ratio: float = 0.0
|
|
39
|
+
median_ratio: float = 0.0
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ImageDimensionStats(BaseModel):
|
|
43
|
+
"""Image dimension statistics."""
|
|
44
|
+
|
|
45
|
+
min_width: int = 0
|
|
46
|
+
max_width: int = 0
|
|
47
|
+
avg_width: float = 0.0
|
|
48
|
+
median_width: float = 0.0
|
|
49
|
+
mode_width: int | None = None
|
|
50
|
+
std_dev_width: float = 0.0
|
|
51
|
+
q1_width: float = 0.0
|
|
52
|
+
q3_width: float = 0.0
|
|
53
|
+
p5_width: float = 0.0
|
|
54
|
+
p95_width: float = 0.0
|
|
55
|
+
outlier_count_width: int = 0
|
|
56
|
+
min_height: int = 0
|
|
57
|
+
max_height: int = 0
|
|
58
|
+
avg_height: float = 0.0
|
|
59
|
+
median_height: float = 0.0
|
|
60
|
+
mode_height: int | None = None
|
|
61
|
+
std_dev_height: float = 0.0
|
|
62
|
+
q1_height: float = 0.0
|
|
63
|
+
q3_height: float = 0.0
|
|
64
|
+
p5_height: float = 0.0
|
|
65
|
+
p95_height: float = 0.0
|
|
66
|
+
outlier_count_height: int = 0
|
|
67
|
+
aspect_ratio: AspectRatioDistribution | None = None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class SyntheticBreakdown(BaseModel):
|
|
71
|
+
"""Breakdown of real vs synthetic images."""
|
|
72
|
+
|
|
73
|
+
real_images: int = 0
|
|
74
|
+
synthetic_images: int = 0
|
|
75
|
+
synthetic_ratio: float = 0.0
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class AnnotationDistribution(BaseModel):
|
|
79
|
+
"""Annotation count distribution across images."""
|
|
80
|
+
|
|
81
|
+
per_image_stats: DescriptiveStats | None = None
|
|
82
|
+
buckets: dict[str, int] = {}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class CategoryStats(BaseModel):
|
|
86
|
+
"""Per-category statistics with bbox dimension statistics."""
|
|
87
|
+
|
|
88
|
+
category_name: str | None = None
|
|
89
|
+
annotation_count: int = 0
|
|
90
|
+
bbox_width_stats: DescriptiveStats | None = None
|
|
91
|
+
bbox_height_stats: DescriptiveStats | None = None
|
|
92
|
+
bbox_area_stats: DescriptiveStats | None = None
|
|
93
|
+
zero_area_count: int = 0
|
|
94
|
+
out_of_bounds_count: int = 0
|
|
95
|
+
crowd_count: int = 0
|
|
96
|
+
ignored_count: int = 0
|
|
97
|
+
avg_coverage_percent: float = 0.0
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class SegmentationCategoryStats(BaseModel):
|
|
101
|
+
"""Per-category pixel coverage stats for segmentation datasets."""
|
|
102
|
+
|
|
103
|
+
category_name: str | None = None
|
|
104
|
+
image_count: int = 0
|
|
105
|
+
total_pixel_count: int = 0
|
|
106
|
+
pixel_percentage: float = 0.0
|
|
107
|
+
avg_coverage_percent: float = 0.0
|
|
108
|
+
pixel_count_stats: DescriptiveStats | None = None
|
|
109
|
+
area_m2: float | None = None
|
|
110
|
+
avg_area_m2_per_image: float | None = None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class DataQualityMetrics(BaseModel):
|
|
114
|
+
"""Data quality metrics for a split."""
|
|
115
|
+
|
|
116
|
+
empty_image_count: int = 0
|
|
117
|
+
empty_image_percent: float = 0.0
|
|
118
|
+
crowd_annotation_count: int = 0
|
|
119
|
+
crowd_annotation_percent: float = 0.0
|
|
120
|
+
ignored_annotation_count: int = 0
|
|
121
|
+
ignored_annotation_percent: float = 0.0
|
|
122
|
+
annotations_with_segmentation: int = 0
|
|
123
|
+
annotations_with_keypoints: int = 0
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class ClassImbalance(BaseModel):
|
|
127
|
+
"""Class imbalance information for a split."""
|
|
128
|
+
|
|
129
|
+
top_class_name: str | None = None
|
|
130
|
+
top_class_count: int = 0
|
|
131
|
+
top_class_share: float = 0.0
|
|
132
|
+
imbalance_score: float = 0.0
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class SplitStatistics(BaseModel):
|
|
136
|
+
"""Complete statistics for a single split."""
|
|
137
|
+
|
|
138
|
+
image_count: int = 0
|
|
139
|
+
annotation_count: int = 0
|
|
140
|
+
category_count: int = 0
|
|
141
|
+
image_dimensions: ImageDimensionStats | None = None
|
|
142
|
+
images_by_format: dict[str, int] = {}
|
|
143
|
+
synthetic_breakdown: SyntheticBreakdown | None = None
|
|
144
|
+
annotation_distribution: AnnotationDistribution | None = None
|
|
145
|
+
per_category_stats: dict[str, CategoryStats] = {}
|
|
146
|
+
data_quality: DataQualityMetrics | None = None
|
|
147
|
+
class_imbalance: ClassImbalance | None = None
|
|
148
|
+
segmentation_category_stats: dict[str, SegmentationCategoryStats] | None = None
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class DatasetSummary(BaseModel):
|
|
152
|
+
"""Dataset-level summary derived from per-split statistics."""
|
|
153
|
+
|
|
154
|
+
total_images: int = 0
|
|
155
|
+
total_annotations: int = 0
|
|
156
|
+
total_categories: int = 0
|
|
157
|
+
distinct_classes: int = 0
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class OverviewModel(BaseModel):
|
|
161
|
+
"""Comprehensive dataset overview from GET /datasets/{id}/metrics/overview."""
|
|
162
|
+
|
|
163
|
+
summary: DatasetSummary | None = None
|
|
164
|
+
splits: dict[str, SplitStatistics] = {}
|
|
165
|
+
category_co_occurrence: dict[str, dict[str, int]] = {}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class ClassDistributionModel(BaseModel):
|
|
169
|
+
"""Response from GET /datasets/{id}/metrics/class-distribution."""
|
|
170
|
+
|
|
171
|
+
classes: list[dict[str, Any]] = []
|
|
172
|
+
totals: dict[str, Any] = {}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class BinnedModel(BaseModel):
|
|
176
|
+
"""Response from GET /datasets/{id}/metrics/bbox-area."""
|
|
177
|
+
|
|
178
|
+
bins: list[dict[str, Any]] = []
|
|
179
|
+
bin_type: str | None = None
|
|
180
|
+
image_area_norm: bool = False
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Oversample models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class OversampleRequest(BaseModel):
|
|
12
|
+
"""Request for POST /datasets/{id}/oversample."""
|
|
13
|
+
|
|
14
|
+
target_ratio: float
|
|
15
|
+
strategy: str = "PREFER_ANNOTATED"
|
|
16
|
+
seed: int | None = None
|
|
17
|
+
target_split_id: UUID | None = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class OversampleAnalysisModel(BaseModel):
|
|
21
|
+
"""Response from POST /datasets/{id}/oversample/analyze."""
|
|
22
|
+
|
|
23
|
+
current_real_images: int = 0
|
|
24
|
+
current_synthetic_images: int = 0
|
|
25
|
+
current_ratio: float = 0.0
|
|
26
|
+
target_ratio: float = 0.0
|
|
27
|
+
oversample_target: int | str | None = None
|
|
28
|
+
images_to_duplicate: int = 0
|
|
29
|
+
selected_images: list[dict[str, Any]] = []
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class OversampleResponseModel(BaseModel):
|
|
33
|
+
"""Response from POST /datasets/{id}/oversample."""
|
|
34
|
+
|
|
35
|
+
success: bool = False
|
|
36
|
+
message: str | None = None
|
|
37
|
+
images_duplicated: int = 0
|
|
38
|
+
annotations_duplicated: int = 0
|
|
39
|
+
new_real_count: int = 0
|
|
40
|
+
new_synthetic_count: int = 0
|
|
41
|
+
new_ratio: float = 0.0
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Split-related models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Any
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SplitModel(BaseModel):
|
|
13
|
+
"""Represents a dataset split."""
|
|
14
|
+
|
|
15
|
+
split_id: UUID
|
|
16
|
+
split_type: str | None = None
|
|
17
|
+
tao_id: UUID | None = None
|
|
18
|
+
name: str | None = None
|
|
19
|
+
description: str | None = None
|
|
20
|
+
created_on: datetime | None = None
|
|
21
|
+
locked: bool = False
|
|
22
|
+
uploaded: bool = False
|
|
23
|
+
import_status: str | None = None
|
|
24
|
+
import_progress: int | None = None
|
|
25
|
+
import_error: str | None = None
|
|
26
|
+
import_started_at: datetime | None = None
|
|
27
|
+
import_finished_at: datetime | None = None
|
|
28
|
+
import_source: str | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class CreateSplitsResponse(BaseModel):
|
|
32
|
+
"""Response from POST /datasets/{id}/splits/create."""
|
|
33
|
+
|
|
34
|
+
success: bool
|
|
35
|
+
message: str | None = None
|
|
36
|
+
splits: list[SplitModel] = []
|
|
37
|
+
summary: dict[str, Any] | None = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class RedistributeResponse(BaseModel):
|
|
41
|
+
"""Response from POST /datasets/{id}/splits/redistribute."""
|
|
42
|
+
|
|
43
|
+
previous_distribution: dict[str, Any] | None = None
|
|
44
|
+
new_distribution: dict[str, Any] | None = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SmartRedistributeResponse(BaseModel):
|
|
48
|
+
"""Response from POST /datasets/{id}/splits/smart-redistribute."""
|
|
49
|
+
|
|
50
|
+
previous_distribution: dict[str, Any] | None = None
|
|
51
|
+
new_distribution: dict[str, Any] | None = None
|
|
52
|
+
class_distribution_error: dict[str, Any] | None = None
|
|
53
|
+
iterations_used: int | None = None
|
|
54
|
+
converged: bool | None = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class LeakageResponse(BaseModel):
|
|
58
|
+
"""Response from POST /datasets/{id}/splits/check-leakage."""
|
|
59
|
+
|
|
60
|
+
has_leakage: bool = False
|
|
61
|
+
leakage_count: int = 0
|
|
62
|
+
method: str | None = None
|
|
63
|
+
leakages: list[dict[str, Any]] = []
|
|
64
|
+
summary: dict[str, Any] | None = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# ── Class-aware redistribute (replaces smart-redistribute) ───────────────
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class SplitStats(BaseModel):
|
|
71
|
+
"""Per-split statistics in a class-aware redistribute response."""
|
|
72
|
+
|
|
73
|
+
image_count: int = 0
|
|
74
|
+
annotation_count: int = 0
|
|
75
|
+
ratio: float = 0.0
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ClassError(BaseModel):
|
|
79
|
+
"""Per-class distribution error."""
|
|
80
|
+
|
|
81
|
+
category_name: str | None = None
|
|
82
|
+
target_ratio: float = 0.0
|
|
83
|
+
actual_ratio: float = 0.0
|
|
84
|
+
error: float = 0.0
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class DistributionError(BaseModel):
|
|
88
|
+
"""Per-split distribution error breakdown."""
|
|
89
|
+
|
|
90
|
+
split_name: str | None = None
|
|
91
|
+
mean_error: float = 0.0
|
|
92
|
+
max_error: float = 0.0
|
|
93
|
+
class_errors: list[ClassError] = []
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class AlgorithmStats(BaseModel):
|
|
97
|
+
"""Algorithm statistics from class-aware redistribution."""
|
|
98
|
+
|
|
99
|
+
iterations_used: int = 0
|
|
100
|
+
converged: bool = False
|
|
101
|
+
final_error: float = 0.0
|
|
102
|
+
elapsed_ms: int | None = None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class ClassAwareRedistributeResponse(BaseModel):
|
|
106
|
+
"""Response from POST /datasets/{id}/splits/class-aware-redistribute."""
|
|
107
|
+
|
|
108
|
+
previous_distribution: dict[str, Any] | None = None
|
|
109
|
+
new_distribution: dict[str, Any] | None = None
|
|
110
|
+
split_stats: dict[str, SplitStats] | None = None
|
|
111
|
+
distribution_errors: list[DistributionError] = []
|
|
112
|
+
algorithm_stats: AlgorithmStats | None = None
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Validation and QA models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DuplicateItem(BaseModel):
|
|
12
|
+
"""A single image in a duplicate group."""
|
|
13
|
+
|
|
14
|
+
image_id: UUID | None = None
|
|
15
|
+
file_name: str | None = None
|
|
16
|
+
split_name: str | None = None
|
|
17
|
+
hash: str | None = None
|
|
18
|
+
uri: str | None = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DuplicateGroup(BaseModel):
|
|
22
|
+
"""A group of duplicate images."""
|
|
23
|
+
|
|
24
|
+
group_id: int = 0
|
|
25
|
+
duplicate_type: str | None = None
|
|
26
|
+
similarity: float = 0.0
|
|
27
|
+
items: list[DuplicateItem] = []
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DuplicateCheckModel(BaseModel):
|
|
31
|
+
"""Response from POST /datasets/{id}/validate/duplicates."""
|
|
32
|
+
|
|
33
|
+
has_duplicates: bool = False
|
|
34
|
+
duplicate_group_count: int = 0
|
|
35
|
+
total_duplicate_images: int = 0
|
|
36
|
+
check_type: str | None = None
|
|
37
|
+
hash_threshold: float = 0.95
|
|
38
|
+
duplicate_groups: list[DuplicateGroup] = []
|
|
39
|
+
summary: dict[str, Any] = {}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ConflictAnnotation(BaseModel):
|
|
43
|
+
"""Annotation in a temporal conflict cluster."""
|
|
44
|
+
|
|
45
|
+
split_id: UUID | None = None
|
|
46
|
+
annotation_id: int = 0
|
|
47
|
+
image_id: UUID | None = None
|
|
48
|
+
image_file_name: str | None = None
|
|
49
|
+
category_name: str | None = None
|
|
50
|
+
category_id: int = 0
|
|
51
|
+
bbox: list[float] = []
|
|
52
|
+
capture_timestamp: str | None = None
|
|
53
|
+
is_outlier: bool = False
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ConflictCluster(BaseModel):
|
|
57
|
+
"""A temporal conflict cluster."""
|
|
58
|
+
|
|
59
|
+
cluster_id: int = 0
|
|
60
|
+
tile_id: str | None = None
|
|
61
|
+
annotations: list[ConflictAnnotation] = []
|
|
62
|
+
distinct_categories: int = 0
|
|
63
|
+
suggested_category: str | None = None
|
|
64
|
+
confidence: float = 0.0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TemporalConflictModel(BaseModel):
|
|
68
|
+
"""Response from POST /datasets/{id}/qa/temporal-conflicts."""
|
|
69
|
+
|
|
70
|
+
has_conflicts: bool = False
|
|
71
|
+
clusters: list[ConflictCluster] = []
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class ResolveTemporalConflictResponse(BaseModel):
|
|
75
|
+
"""Response from POST /datasets/{id}/qa/temporal-conflicts/resolve."""
|
|
76
|
+
|
|
77
|
+
success: bool = False
|
|
78
|
+
message: str | None = None
|
|
79
|
+
dataset_id: str | None = None
|
|
80
|
+
clusters_resolved: int = 0
|
|
81
|
+
annotations_updated: int = 0
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class ValidationResultsModel(BaseModel):
|
|
85
|
+
"""Generic validation results."""
|
|
86
|
+
|
|
87
|
+
valid: bool = True
|
|
88
|
+
errors: list[dict[str, Any]] = []
|
|
89
|
+
warnings: list[dict[str, Any]] = []
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Resource objects that map to API endpoints."""
|