segment-toolkit 1.0.0__tar.gz

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.
@@ -0,0 +1,217 @@
1
+ Metadata-Version: 2.4
2
+ Name: segment_toolkit
3
+ Version: 1.0.0
4
+ Summary: A Python toolkit to convert between binary segmentation masks and YOLO labels
5
+ Author: Antigravity
6
+ Requires-Python: >=3.6
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: numpy
9
+ Requires-Dist: opencv-python
10
+ Requires-Dist: pillow
11
+ Requires-Dist: pandas
12
+ Requires-Dist: matplotlib
13
+ Dynamic: author
14
+ Dynamic: description
15
+ Dynamic: description-content-type
16
+ Dynamic: requires-dist
17
+ Dynamic: requires-python
18
+ Dynamic: summary
19
+
20
+ # Segment Toolkit 🛠️
21
+
22
+ A modern, robust, and premium Python package designed to bridge the gap between pixel-level **binary segmentation masks** and **YOLO bounding box labels**. It provides a bidirectional pipeline with exception handling, extensive logging, a command-line interface (CLI), and a Python API.
23
+
24
+ ---
25
+
26
+ ## 📌 Features
27
+
28
+ - **Bidirectional Conversion**:
29
+ - **Forward Pipeline**: Convert binary masks to YOLO format labels (supports standard axis-aligned or advanced minimum area rotated bounding boxes).
30
+ - **Reverse Pipeline**: Reconstruct binary masks from YOLO labels.
31
+ - **Automatic Dependency Installer**: Missing required packages (`numpy`, `opencv-python`, `pillow`, `pandas`, `matplotlib`) are automatically detected and installed via `pip` upon package import or script execution.
32
+ - **Robust Exception Handling**: Try-catch blocks wrapped around file I/O, contour finding, and resizing to prevent application crashes on corrupted or missing files.
33
+ - **Dynamic Dataset Matching**: Read classification mappings (in **CSV** or **JSON** format) to automatically assign multi-class IDs matching standard dataset schemas (like the ISIC dataset).
34
+ - **YOLO Dataset Splitting**: Automatically shuffles and partitions images & labels into training and testing sets with customizable split ratios, creating standard `data.yaml` configs.
35
+ - **Overlay Visualizer**: Overlay bounding boxes and class indicators directly onto source images for annotation inspection.
36
+ - **Dual Interface**: Use as a command-line application (`segment-toolkit`) or import as a Python library (`import segment_toolkit`).
37
+
38
+ ---
39
+
40
+ ## 📂 Installation
41
+
42
+ To install the toolkit locally in editable mode (missing dependencies will install automatically):
43
+
44
+ ```bash
45
+ pip install -e .
46
+ ```
47
+
48
+ ### Manual Installation
49
+ If you prefer to install dependencies manually before installing the toolkit:
50
+
51
+ ```bash
52
+ pip install -r requirements.txt
53
+ pip install .
54
+ ```
55
+
56
+ ---
57
+
58
+ ## 🚀 Usage
59
+
60
+ ### 1. Command Line Interface (CLI)
61
+
62
+ The package installs a console script called `segment-toolkit`.
63
+
64
+ #### Convert Masks to YOLO Labels
65
+ - **Single File Conversion**:
66
+ ```bash
67
+ segment-toolkit mask-to-yolo \
68
+ --image images/ISIC_0024310.jpg \
69
+ --mask mask/ISIC_0024310_segmentation.png \
70
+ --output-txt labels/ISIC_0024310.txt \
71
+ --class-id 4
72
+ ```
73
+
74
+ - **Batch Directory Conversion**:
75
+ ```bash
76
+ segment-toolkit mask-to-yolo \
77
+ --image-dir images/ \
78
+ --mask-dir mask/ \
79
+ --output-dir labels/ \
80
+ --ground-truth GroundTruth.csv
81
+ ```
82
+
83
+ - **Options**:
84
+ - `--rotated`: Use rotated minimum area rectangles (`cv2.minAreaRect`) instead of standard axis-aligned rectangles.
85
+ - `--resize WIDTH HEIGHT`: Set target size for image and mask resizing (default: `640 640`).
86
+
87
+ #### Convert YOLO Labels to Masks
88
+ - **Single File Conversion**:
89
+ ```bash
90
+ segment-toolkit yolo-to-mask \
91
+ --label labels/ISIC_0024310.txt \
92
+ --output-mask masks_reconstructed/ISIC_0024310_segmentation.png
93
+ ```
94
+
95
+ - **Batch Directory Conversion**:
96
+ ```bash
97
+ segment-toolkit yolo-to-mask \
98
+ --label-dir labels/ \
99
+ --output-dir masks_reconstructed/
100
+ ```
101
+
102
+ #### Visualize Bounding Boxes
103
+ Draw YOLO labels on top of the original image:
104
+ ```bash
105
+ segment-toolkit visualize \
106
+ --image images/ISIC_0024310.jpg \
107
+ --label labels/ISIC_0024310.txt \
108
+ --output visualization.png
109
+ ```
110
+
111
+ #### Split Dataset
112
+ Organize folders into YOLO-compliant structure (`dataset/train` and `dataset/test` splits) and output `data.yaml`:
113
+ ```bash
114
+ segment-toolkit split \
115
+ --images images/ \
116
+ --labels labels/ \
117
+ --output dataset/ \
118
+ --ratio 0.8 \
119
+ --seed 42
120
+ ```
121
+
122
+ ---
123
+
124
+ ### 2. Ground Truth Formats
125
+
126
+ The `--ground-truth` parameter in batch conversion supports both CSV and JSON formats.
127
+
128
+ #### CSV Format
129
+ Assumes the first column contains the image identifier/filename, and the subsequent columns represent binary indicator classes (where `1` indicates class presence).
130
+ Example `GroundTruth.csv`:
131
+ ```csv
132
+ image,MEL,NV,BCC,AKIEC,BKL,DF,VASC
133
+ ISIC_0024306,0,1,0,0,0,0,0
134
+ ISIC_0024310,1,0,0,0,0,0,0
135
+ ```
136
+
137
+ #### JSON Format
138
+ Supports three distinct schemas:
139
+
140
+ 1. **Flat Dictionary (Format A)**:
141
+ Maps image IDs directly to class integers or class name strings.
142
+ ```json
143
+ {
144
+ "ISIC_0024306": 5,
145
+ "ISIC_0024310": "MEL"
146
+ }
147
+ ```
148
+
149
+ 2. **Nested Indicators (Format B)**:
150
+ Maps image IDs to dictionaries of binary class indicators.
151
+ ```json
152
+ {
153
+ "ISIC_0024306": { "MEL": 0, "NV": 1, "BCC": 0 },
154
+ "ISIC_0024310": { "MEL": 1, "NV": 0, "BCC": 0 }
155
+ }
156
+ ```
157
+
158
+ 3. **List of Records (Format C)**:
159
+ A list of objects containing image IDs and class descriptors.
160
+ ```json
161
+ [
162
+ { "image": "ISIC_0024306", "class_id": 5 },
163
+ { "image": "ISIC_0024310", "MEL": 1, "NV": 0 }
164
+ ]
165
+ ```
166
+
167
+ *Note: Class name strings (like `"MEL"`, `"NV"`) are automatically mapped to standard ISIC IDs (`AKIEC=0, BCC=1, BKL=2, DF=3, MEL=4, NV=5, VASC=6`). Custom column names default to index-based IDs.*
168
+
169
+ ---
170
+
171
+ ### 3. Python API
172
+
173
+ Import classes directly into your code to programmatically build custom pipelines:
174
+
175
+ ```python
176
+ from segment_toolkit import MaskToYoloConverter, YoloToMaskConverter
177
+
178
+ # 1. Convert mask to YOLO label
179
+ yolo_conv = MaskToYoloConverter(target_size=(640, 640), bbox_type="standard")
180
+ yolo_conv.convert_single(
181
+ image_path="images/ISIC_0024310.jpg",
182
+ mask_path="mask/ISIC_0024310_segmentation.png",
183
+ output_txt_path="labels/ISIC_0024310.txt",
184
+ class_id=4
185
+ )
186
+
187
+ # 2. Batch convert a folder of masks with a JSON ground truth
188
+ yolo_conv.convert_dataset(
189
+ images_dir="images",
190
+ masks_dir="mask",
191
+ output_labels_dir="labels",
192
+ ground_truth="GroundTruth.json"
193
+ )
194
+ ```
195
+
196
+ ---
197
+
198
+ ## 🧠 Technical Details
199
+
200
+ ### Coordinate Conversion Math
201
+
202
+ #### Bounding Box Center Calculation (Pixel Space)
203
+ For standard bounding boxes, the pixel coordinates from `boundingRect` are $(x_{min}, y_{min}, w_{pixel}, h_{pixel})$.
204
+ $$\text{Center } X \quad x_{center} = x_{min} + \frac{w_{pixel}}{2.0}$$
205
+ $$\text{Center } Y \quad y_{center} = y_{min} + \frac{h_{pixel}}{2.0}$$
206
+
207
+ #### Coordinate Normalization (YOLO Format)
208
+ All coordinates are normalized to the range $[0.0, 1.0]$:
209
+ $$x_{norm} = \frac{x_{center}}{img\_width}, \quad y_{norm} = \frac{y_{center}}{img\_height}$$
210
+ $$w_{norm} = \frac{w_{pixel}}{img\_width}, \quad h_{norm} = \frac{h_{pixel}}{img\_height}$$
211
+
212
+ ---
213
+
214
+ ## 🧑‍💻 Author
215
+ **Zakria Gamal**
216
+ - Computer Vision & AI Engineer
217
+ - 🧠 LinkedIn: [Zakria Gamal](https://www.linkedin.com/in/zkaria-gamal-82b486267/)
@@ -0,0 +1,198 @@
1
+ # Segment Toolkit 🛠️
2
+
3
+ A modern, robust, and premium Python package designed to bridge the gap between pixel-level **binary segmentation masks** and **YOLO bounding box labels**. It provides a bidirectional pipeline with exception handling, extensive logging, a command-line interface (CLI), and a Python API.
4
+
5
+ ---
6
+
7
+ ## 📌 Features
8
+
9
+ - **Bidirectional Conversion**:
10
+ - **Forward Pipeline**: Convert binary masks to YOLO format labels (supports standard axis-aligned or advanced minimum area rotated bounding boxes).
11
+ - **Reverse Pipeline**: Reconstruct binary masks from YOLO labels.
12
+ - **Automatic Dependency Installer**: Missing required packages (`numpy`, `opencv-python`, `pillow`, `pandas`, `matplotlib`) are automatically detected and installed via `pip` upon package import or script execution.
13
+ - **Robust Exception Handling**: Try-catch blocks wrapped around file I/O, contour finding, and resizing to prevent application crashes on corrupted or missing files.
14
+ - **Dynamic Dataset Matching**: Read classification mappings (in **CSV** or **JSON** format) to automatically assign multi-class IDs matching standard dataset schemas (like the ISIC dataset).
15
+ - **YOLO Dataset Splitting**: Automatically shuffles and partitions images & labels into training and testing sets with customizable split ratios, creating standard `data.yaml` configs.
16
+ - **Overlay Visualizer**: Overlay bounding boxes and class indicators directly onto source images for annotation inspection.
17
+ - **Dual Interface**: Use as a command-line application (`segment-toolkit`) or import as a Python library (`import segment_toolkit`).
18
+
19
+ ---
20
+
21
+ ## 📂 Installation
22
+
23
+ To install the toolkit locally in editable mode (missing dependencies will install automatically):
24
+
25
+ ```bash
26
+ pip install -e .
27
+ ```
28
+
29
+ ### Manual Installation
30
+ If you prefer to install dependencies manually before installing the toolkit:
31
+
32
+ ```bash
33
+ pip install -r requirements.txt
34
+ pip install .
35
+ ```
36
+
37
+ ---
38
+
39
+ ## 🚀 Usage
40
+
41
+ ### 1. Command Line Interface (CLI)
42
+
43
+ The package installs a console script called `segment-toolkit`.
44
+
45
+ #### Convert Masks to YOLO Labels
46
+ - **Single File Conversion**:
47
+ ```bash
48
+ segment-toolkit mask-to-yolo \
49
+ --image images/ISIC_0024310.jpg \
50
+ --mask mask/ISIC_0024310_segmentation.png \
51
+ --output-txt labels/ISIC_0024310.txt \
52
+ --class-id 4
53
+ ```
54
+
55
+ - **Batch Directory Conversion**:
56
+ ```bash
57
+ segment-toolkit mask-to-yolo \
58
+ --image-dir images/ \
59
+ --mask-dir mask/ \
60
+ --output-dir labels/ \
61
+ --ground-truth GroundTruth.csv
62
+ ```
63
+
64
+ - **Options**:
65
+ - `--rotated`: Use rotated minimum area rectangles (`cv2.minAreaRect`) instead of standard axis-aligned rectangles.
66
+ - `--resize WIDTH HEIGHT`: Set target size for image and mask resizing (default: `640 640`).
67
+
68
+ #### Convert YOLO Labels to Masks
69
+ - **Single File Conversion**:
70
+ ```bash
71
+ segment-toolkit yolo-to-mask \
72
+ --label labels/ISIC_0024310.txt \
73
+ --output-mask masks_reconstructed/ISIC_0024310_segmentation.png
74
+ ```
75
+
76
+ - **Batch Directory Conversion**:
77
+ ```bash
78
+ segment-toolkit yolo-to-mask \
79
+ --label-dir labels/ \
80
+ --output-dir masks_reconstructed/
81
+ ```
82
+
83
+ #### Visualize Bounding Boxes
84
+ Draw YOLO labels on top of the original image:
85
+ ```bash
86
+ segment-toolkit visualize \
87
+ --image images/ISIC_0024310.jpg \
88
+ --label labels/ISIC_0024310.txt \
89
+ --output visualization.png
90
+ ```
91
+
92
+ #### Split Dataset
93
+ Organize folders into YOLO-compliant structure (`dataset/train` and `dataset/test` splits) and output `data.yaml`:
94
+ ```bash
95
+ segment-toolkit split \
96
+ --images images/ \
97
+ --labels labels/ \
98
+ --output dataset/ \
99
+ --ratio 0.8 \
100
+ --seed 42
101
+ ```
102
+
103
+ ---
104
+
105
+ ### 2. Ground Truth Formats
106
+
107
+ The `--ground-truth` parameter in batch conversion supports both CSV and JSON formats.
108
+
109
+ #### CSV Format
110
+ Assumes the first column contains the image identifier/filename, and the subsequent columns represent binary indicator classes (where `1` indicates class presence).
111
+ Example `GroundTruth.csv`:
112
+ ```csv
113
+ image,MEL,NV,BCC,AKIEC,BKL,DF,VASC
114
+ ISIC_0024306,0,1,0,0,0,0,0
115
+ ISIC_0024310,1,0,0,0,0,0,0
116
+ ```
117
+
118
+ #### JSON Format
119
+ Supports three distinct schemas:
120
+
121
+ 1. **Flat Dictionary (Format A)**:
122
+ Maps image IDs directly to class integers or class name strings.
123
+ ```json
124
+ {
125
+ "ISIC_0024306": 5,
126
+ "ISIC_0024310": "MEL"
127
+ }
128
+ ```
129
+
130
+ 2. **Nested Indicators (Format B)**:
131
+ Maps image IDs to dictionaries of binary class indicators.
132
+ ```json
133
+ {
134
+ "ISIC_0024306": { "MEL": 0, "NV": 1, "BCC": 0 },
135
+ "ISIC_0024310": { "MEL": 1, "NV": 0, "BCC": 0 }
136
+ }
137
+ ```
138
+
139
+ 3. **List of Records (Format C)**:
140
+ A list of objects containing image IDs and class descriptors.
141
+ ```json
142
+ [
143
+ { "image": "ISIC_0024306", "class_id": 5 },
144
+ { "image": "ISIC_0024310", "MEL": 1, "NV": 0 }
145
+ ]
146
+ ```
147
+
148
+ *Note: Class name strings (like `"MEL"`, `"NV"`) are automatically mapped to standard ISIC IDs (`AKIEC=0, BCC=1, BKL=2, DF=3, MEL=4, NV=5, VASC=6`). Custom column names default to index-based IDs.*
149
+
150
+ ---
151
+
152
+ ### 3. Python API
153
+
154
+ Import classes directly into your code to programmatically build custom pipelines:
155
+
156
+ ```python
157
+ from segment_toolkit import MaskToYoloConverter, YoloToMaskConverter
158
+
159
+ # 1. Convert mask to YOLO label
160
+ yolo_conv = MaskToYoloConverter(target_size=(640, 640), bbox_type="standard")
161
+ yolo_conv.convert_single(
162
+ image_path="images/ISIC_0024310.jpg",
163
+ mask_path="mask/ISIC_0024310_segmentation.png",
164
+ output_txt_path="labels/ISIC_0024310.txt",
165
+ class_id=4
166
+ )
167
+
168
+ # 2. Batch convert a folder of masks with a JSON ground truth
169
+ yolo_conv.convert_dataset(
170
+ images_dir="images",
171
+ masks_dir="mask",
172
+ output_labels_dir="labels",
173
+ ground_truth="GroundTruth.json"
174
+ )
175
+ ```
176
+
177
+ ---
178
+
179
+ ## 🧠 Technical Details
180
+
181
+ ### Coordinate Conversion Math
182
+
183
+ #### Bounding Box Center Calculation (Pixel Space)
184
+ For standard bounding boxes, the pixel coordinates from `boundingRect` are $(x_{min}, y_{min}, w_{pixel}, h_{pixel})$.
185
+ $$\text{Center } X \quad x_{center} = x_{min} + \frac{w_{pixel}}{2.0}$$
186
+ $$\text{Center } Y \quad y_{center} = y_{min} + \frac{h_{pixel}}{2.0}$$
187
+
188
+ #### Coordinate Normalization (YOLO Format)
189
+ All coordinates are normalized to the range $[0.0, 1.0]$:
190
+ $$x_{norm} = \frac{x_{center}}{img\_width}, \quad y_{norm} = \frac{y_{center}}{img\_height}$$
191
+ $$w_{norm} = \frac{w_{pixel}}{img\_width}, \quad h_{norm} = \frac{h_{pixel}}{img\_height}$$
192
+
193
+ ---
194
+
195
+ ## 🧑‍💻 Author
196
+ **Zakria Gamal**
197
+ - Computer Vision & AI Engineer
198
+ - 🧠 LinkedIn: [Zakria Gamal](https://www.linkedin.com/in/zkaria-gamal-82b486267/)
@@ -0,0 +1,60 @@
1
+ """
2
+ Segment Toolkit: A library and CLI tool for converting binary segmentation masks to YOLO labels and vice versa.
3
+ """
4
+
5
+ __version__ = "1.0.0"
6
+
7
+ import sys
8
+ import subprocess
9
+
10
+ def _ensure_dependencies():
11
+ """
12
+ Checks for required external modules and attempts to install them via pip if missing.
13
+ """
14
+ dependencies = {
15
+ "numpy": "numpy",
16
+ "cv2": "opencv-python",
17
+ "PIL": "pillow",
18
+ "pandas": "pandas",
19
+ "matplotlib": "matplotlib"
20
+ }
21
+ missing = []
22
+ for module, pip_name in dependencies.items():
23
+ try:
24
+ __import__(module)
25
+ except ImportError:
26
+ missing.append(pip_name)
27
+
28
+ if missing:
29
+ print(f"[segment_toolkit] Missing required package(s): {', '.join(missing)}", file=sys.stderr)
30
+ print("[segment_toolkit] Attempting to auto-install dependencies...", file=sys.stderr)
31
+ try:
32
+ subprocess.check_call([sys.executable, "-m", "pip", "install", *missing])
33
+ print("[segment_toolkit] Dependencies installed successfully.", file=sys.stderr)
34
+ except Exception as err:
35
+ print(f"[segment_toolkit] Error: Auto-installation of dependencies failed: {err}", file=sys.stderr)
36
+ print("[segment_toolkit] Please install them manually using: pip install -r requirements.txt", file=sys.stderr)
37
+
38
+ # Check and install dependencies before importing other submodules
39
+ _ensure_dependencies()
40
+
41
+ from .source import MaskToYoloConverter, YoloToMaskConverter
42
+ from .helpers import (
43
+ safe_read_image,
44
+ preprocess_image,
45
+ get_largest_contour,
46
+ calculate_bbox,
47
+ normalize_coordinates,
48
+ denormalize_coordinates,
49
+ )
50
+
51
+ __all__ = [
52
+ "MaskToYoloConverter",
53
+ "YoloToMaskConverter",
54
+ "safe_read_image",
55
+ "preprocess_image",
56
+ "get_largest_contour",
57
+ "calculate_bbox",
58
+ "normalize_coordinates",
59
+ "denormalize_coordinates",
60
+ ]
@@ -0,0 +1,192 @@
1
+ """
2
+ Command Line Interface (CLI) for segment_toolkit.
3
+ Exposes mask-to-yolo, yolo-to-mask, split, and visualize commands.
4
+ """
5
+
6
+ import argparse
7
+ import sys
8
+ import logging
9
+ from typing import List, Optional
10
+
11
+ from .source import MaskToYoloConverter, YoloToMaskConverter
12
+
13
+ # Configure basic logging
14
+ logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ def parse_args(args: List[str]) -> argparse.Namespace:
19
+ """
20
+ Parses command-line arguments.
21
+ """
22
+ parser = argparse.ArgumentParser(
23
+ description="Segment Toolkit: Convert segmentation masks to/from YOLO format annotations."
24
+ )
25
+
26
+ subparsers = parser.add_subparsers(dest="command", required=True, help="Subcommands")
27
+
28
+ # subcommand: mask-to-yolo
29
+ m2y_parser = subparsers.add_parser(
30
+ "mask-to-yolo", help="Convert binary mask(s) to YOLO format labels."
31
+ )
32
+ m2y_parser.add_argument("--image", type=str, help="Path to a single input image.")
33
+ m2y_parser.add_argument("--mask", type=str, help="Path to a single input binary mask.")
34
+ m2y_parser.add_argument("--output-txt", type=str, help="Output file path for single YOLO label txt.")
35
+
36
+ m2y_parser.add_argument("--image-dir", type=str, help="Directory containing input images.")
37
+ m2y_parser.add_argument("--mask-dir", type=str, help="Directory containing input masks.")
38
+ m2y_parser.add_argument("--output-dir", type=str, help="Directory to save generated YOLO label files.")
39
+
40
+ m2y_parser.add_argument("--class-id", type=int, default=0, help="Default Class ID to write (default: 0).")
41
+ m2y_parser.add_argument(
42
+ "--ground-truth",
43
+ type=str,
44
+ help="Path to GroundTruth.csv mapping images to multi-class columns.",
45
+ )
46
+ m2y_parser.add_argument(
47
+ "--rotated",
48
+ action="store_true",
49
+ help="Use rotated minimum area rectangle (minAreaRect) instead of axis-aligned.",
50
+ )
51
+ m2y_parser.add_argument(
52
+ "--resize",
53
+ type=int,
54
+ nargs=2,
55
+ default=[640, 640],
56
+ metavar=("WIDTH", "HEIGHT"),
57
+ help="Target dimensions for resizing (default: 640 640).",
58
+ )
59
+
60
+ # subcommand: yolo-to-mask
61
+ y2m_parser = subparsers.add_parser(
62
+ "yolo-to-mask", help="Convert YOLO format label(s) to binary mask(s)."
63
+ )
64
+ y2m_parser.add_argument("--label", type=str, help="Path to a single input YOLO label txt file.")
65
+ y2m_parser.add_argument("--output-mask", type=str, help="Output path for single binary mask png.")
66
+
67
+ y2m_parser.add_argument("--label-dir", type=str, help="Directory containing YOLO label txt files.")
68
+ y2m_parser.add_argument("--output-dir", type=str, help="Directory to save generated binary mask png files.")
69
+
70
+ y2m_parser.add_argument(
71
+ "--resize",
72
+ type=int,
73
+ nargs=2,
74
+ default=[640, 640],
75
+ metavar=("WIDTH", "HEIGHT"),
76
+ help="Output mask dimensions (default: 640 640).",
77
+ )
78
+
79
+ # subcommand: split
80
+ split_parser = subparsers.add_parser(
81
+ "split", help="Randomly split a dataset of images and labels into train/test subfolders."
82
+ )
83
+ split_parser.add_argument("--images", type=str, required=True, help="Directory of source images.")
84
+ split_parser.add_argument("--labels", type=str, required=True, help="Directory of source YOLO label txt files.")
85
+ split_parser.add_argument("--output", type=str, required=True, help="Root directory for split dataset outputs.")
86
+ split_parser.add_argument(
87
+ "--ratio", type=float, default=0.8, help="Split ratio for training partition (default: 0.8)."
88
+ )
89
+ split_parser.add_argument("--seed", type=int, default=42, help="Seed value for reproduction (default: 42).")
90
+
91
+ # subcommand: visualize
92
+ vis_parser = subparsers.add_parser(
93
+ "visualize", help="Draw bounding boxes from a YOLO label file onto the source image."
94
+ )
95
+ vis_parser.add_argument("--image", type=str, required=True, help="Path to the source image.")
96
+ vis_parser.add_argument("--label", type=str, required=True, help="Path to the YOLO label file.")
97
+ vis_parser.add_argument("--output", type=str, required=True, help="Path to save output visualization image.")
98
+ vis_parser.add_argument(
99
+ "--resize",
100
+ type=int,
101
+ nargs=2,
102
+ default=[640, 640],
103
+ metavar=("WIDTH", "HEIGHT"),
104
+ help="Resize image for visualization (default: 640 640).",
105
+ )
106
+
107
+ return parser.parse_args(args)
108
+
109
+
110
+ def main(args: Optional[List[str]] = None) -> int:
111
+ """
112
+ Main entry point for command-line execution.
113
+ """
114
+ if args is None:
115
+ args = sys.argv[1:]
116
+
117
+ try:
118
+ parsed = parse_args(args)
119
+
120
+ if parsed.command == "mask-to-yolo":
121
+ converter = MaskToYoloConverter(
122
+ target_size=(parsed.resize[0], parsed.resize[1]),
123
+ bbox_type="rotated" if parsed.rotated else "standard",
124
+ )
125
+ # Check if processing single file or folder
126
+ if parsed.image or parsed.mask or parsed.output_txt:
127
+ if not (parsed.image and parsed.mask and parsed.output_txt):
128
+ logger.error("Error: --image, --mask, and --output-txt must all be specified for single conversion.")
129
+ return 1
130
+ success = converter.convert_single(
131
+ parsed.image, parsed.mask, parsed.output_txt, class_id=parsed.class_id
132
+ )
133
+ return 0 if success else 1
134
+ elif parsed.image_dir or parsed.mask_dir or parsed.output_dir:
135
+ if not (parsed.image_dir and parsed.mask_dir and parsed.output_dir):
136
+ logger.error("Error: --image-dir, --mask-dir, and --output-dir must all be specified for folder conversion.")
137
+ return 1
138
+ converter.convert_dataset(
139
+ parsed.image_dir,
140
+ parsed.mask_dir,
141
+ parsed.output_dir,
142
+ default_class_id=parsed.class_id,
143
+ ground_truth=parsed.ground_truth,
144
+ )
145
+ return 0
146
+ else:
147
+ logger.error("Error: Must specify either single file arguments (--image, --mask, --output-txt) or directory arguments (--image-dir, --mask-dir, --output-dir).")
148
+ return 1
149
+
150
+ elif parsed.command == "yolo-to-mask":
151
+ converter = YoloToMaskConverter(target_size=(parsed.resize[0], parsed.resize[1]))
152
+ if parsed.label or parsed.output_mask:
153
+ if not (parsed.label and parsed.output_mask):
154
+ logger.error("Error: Both --label and --output-mask must be specified for single conversion.")
155
+ return 1
156
+ success = converter.convert_single(parsed.label, parsed.output_mask)
157
+ return 0 if success else 1
158
+ elif parsed.label_dir or parsed.output_dir:
159
+ if not (parsed.label_dir and parsed.output_dir):
160
+ logger.error("Error: Both --label-dir and --output-dir must be specified for folder conversion.")
161
+ return 1
162
+ converter.convert_dataset(parsed.label_dir, parsed.output_dir)
163
+ return 0
164
+ else:
165
+ logger.error("Error: Must specify either single file arguments (--label, --output-mask) or directory arguments (--label-dir, --output-dir).")
166
+ return 1
167
+
168
+ elif parsed.command == "split":
169
+ converter = MaskToYoloConverter()
170
+ converter.split_dataset(
171
+ images_dir=parsed.images,
172
+ labels_dir=parsed.labels,
173
+ output_dataset_dir=parsed.output,
174
+ split_ratio=parsed.ratio,
175
+ seed=parsed.seed,
176
+ )
177
+ return 0
178
+
179
+ elif parsed.command == "visualize":
180
+ converter = YoloToMaskConverter(target_size=(parsed.resize[0], parsed.resize[1]))
181
+ success = converter.visualize_label(parsed.image, parsed.label, parsed.output)
182
+ return 0 if success else 1
183
+
184
+ except Exception as e:
185
+ logger.error(f"Execution failed: {str(e)}")
186
+ return 1
187
+
188
+ return 0
189
+
190
+
191
+ if __name__ == "__main__":
192
+ sys.exit(main())