coralnet-toolbox 0.0.73__py2.py3-none-any.whl → 0.0.75__py2.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.
Files changed (50) hide show
  1. coralnet_toolbox/Annotations/QtAnnotation.py +28 -69
  2. coralnet_toolbox/Annotations/QtMaskAnnotation.py +408 -0
  3. coralnet_toolbox/Annotations/QtMultiPolygonAnnotation.py +72 -56
  4. coralnet_toolbox/Annotations/QtPatchAnnotation.py +165 -216
  5. coralnet_toolbox/Annotations/QtPolygonAnnotation.py +497 -353
  6. coralnet_toolbox/Annotations/QtRectangleAnnotation.py +126 -116
  7. coralnet_toolbox/CoralNet/QtDownload.py +2 -1
  8. coralnet_toolbox/Explorer/QtDataItem.py +52 -22
  9. coralnet_toolbox/Explorer/QtExplorer.py +293 -1614
  10. coralnet_toolbox/Explorer/QtSettingsWidgets.py +203 -85
  11. coralnet_toolbox/Explorer/QtViewers.py +1568 -0
  12. coralnet_toolbox/Explorer/transformer_models.py +59 -0
  13. coralnet_toolbox/Explorer/yolo_models.py +112 -0
  14. coralnet_toolbox/IO/QtExportTagLabAnnotations.py +30 -10
  15. coralnet_toolbox/IO/QtImportTagLabAnnotations.py +21 -15
  16. coralnet_toolbox/IO/QtOpenProject.py +46 -78
  17. coralnet_toolbox/IO/QtSaveProject.py +18 -43
  18. coralnet_toolbox/MachineLearning/ExportDataset/QtBase.py +1 -1
  19. coralnet_toolbox/MachineLearning/ImportDataset/QtBase.py +253 -141
  20. coralnet_toolbox/MachineLearning/VideoInference/QtBase.py +0 -4
  21. coralnet_toolbox/MachineLearning/VideoInference/YOLO3D/run.py +102 -16
  22. coralnet_toolbox/QtAnnotationWindow.py +16 -10
  23. coralnet_toolbox/QtEventFilter.py +11 -0
  24. coralnet_toolbox/QtImageWindow.py +120 -75
  25. coralnet_toolbox/QtLabelWindow.py +13 -1
  26. coralnet_toolbox/QtMainWindow.py +5 -27
  27. coralnet_toolbox/QtProgressBar.py +52 -27
  28. coralnet_toolbox/Rasters/RasterTableModel.py +28 -8
  29. coralnet_toolbox/SAM/QtDeployGenerator.py +1 -4
  30. coralnet_toolbox/SAM/QtDeployPredictor.py +11 -3
  31. coralnet_toolbox/SeeAnything/QtDeployGenerator.py +805 -162
  32. coralnet_toolbox/SeeAnything/QtDeployPredictor.py +130 -151
  33. coralnet_toolbox/Tools/QtCutSubTool.py +18 -2
  34. coralnet_toolbox/Tools/QtPolygonTool.py +42 -3
  35. coralnet_toolbox/Tools/QtRectangleTool.py +30 -0
  36. coralnet_toolbox/Tools/QtResizeSubTool.py +19 -2
  37. coralnet_toolbox/Tools/QtSAMTool.py +72 -50
  38. coralnet_toolbox/Tools/QtSeeAnythingTool.py +8 -5
  39. coralnet_toolbox/Tools/QtSelectTool.py +27 -3
  40. coralnet_toolbox/Tools/QtSubtractSubTool.py +66 -0
  41. coralnet_toolbox/Tools/__init__.py +2 -0
  42. coralnet_toolbox/__init__.py +1 -1
  43. coralnet_toolbox/utilities.py +158 -47
  44. coralnet_toolbox-0.0.75.dist-info/METADATA +378 -0
  45. {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.75.dist-info}/RECORD +49 -44
  46. coralnet_toolbox-0.0.73.dist-info/METADATA +0 -341
  47. {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.75.dist-info}/WHEEL +0 -0
  48. {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.75.dist-info}/entry_points.txt +0 -0
  49. {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.75.dist-info}/licenses/LICENSE.txt +0 -0
  50. {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.75.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,59 @@
1
+ """
2
+ Transformer models configuration for the Explorer tool.
3
+
4
+ This module contains the transformer models dictionary used in the Explorer tool.
5
+ It's extracted into a separate module to allow easy importing in tests without
6
+ Qt dependencies.
7
+ """
8
+
9
+ TRANSFORMER_MODELS = {
10
+ 'DINOv2 (Small)': 'facebook/dinov2-small',
11
+ 'DINOv2 (Base)': 'facebook/dinov2-base',
12
+ 'DINOv2 (Large)': 'facebook/dinov2-large',
13
+ 'DINOv3 ConvNext (Tiny)': 'facebook/dinov3-convnext-tiny-pretrain-lvd1689m',
14
+ 'ResNet-50': 'microsoft/resnet-50',
15
+ 'ResNet-101': 'microsoft/resnet-101',
16
+ 'Swin Transformer (Tiny)': 'microsoft/swin-tiny-patch4-window7-224',
17
+ 'Swin Transformer (Base)': 'microsoft/swin-base-patch4-window7-224',
18
+ 'ViT (Base)': 'google/vit-base-patch16-224',
19
+ 'ViT (Large)': 'google/vit-large-patch16-224',
20
+ }
21
+
22
+
23
+ def is_transformer_model(model_name):
24
+ """
25
+ Determine if a model name refers to a transformer model.
26
+
27
+ This function checks if the model name indicates a HuggingFace transformer model
28
+ that should be handled by the transformer feature extraction pipeline.
29
+
30
+ Args:
31
+ model_name (str): The model name to check
32
+
33
+ Returns:
34
+ bool: True if this is a transformer model, False otherwise
35
+ """
36
+ if not model_name or not isinstance(model_name, str):
37
+ return False
38
+
39
+ # Check if it's one of our known transformer model IDs
40
+ if model_name in TRANSFORMER_MODELS.values():
41
+ return True
42
+
43
+ # Check for common HuggingFace model naming patterns
44
+ # Models from HuggingFace typically contain '/' in their names
45
+ if "/" in model_name:
46
+ # Check for known organization prefixes
47
+ known_prefixes = ("facebook/",
48
+ "microsoft/",
49
+ "google/",
50
+ "openai/",
51
+ "imageomics/")
52
+
53
+ if model_name.startswith(known_prefixes):
54
+ return True
55
+
56
+ # Any model with '/' is likely a HuggingFace model
57
+ return True
58
+
59
+ return False
@@ -0,0 +1,112 @@
1
+ """
2
+ YOLO models configuration for the Explorer tool.
3
+
4
+ This module contains the YOLO models dictionary used in the Explorer tool.
5
+ It's extracted into a separate module to allow easy importing in tests without
6
+ Qt dependencies.
7
+ """
8
+
9
+ from coralnet_toolbox.MachineLearning.Community.cfg import get_available_configs
10
+
11
+ # Dictionary mapping display names to model file names (classification models only)
12
+ YOLO_MODELS = {
13
+ # YOLOv8 classification models
14
+ 'YOLOv8 (Nano)': 'yolov8n-cls.pt',
15
+ 'YOLOv8 (Small)': 'yolov8s-cls.pt',
16
+ 'YOLOv8 (Medium)': 'yolov8m-cls.pt',
17
+ 'YOLOv8 (Large)': 'yolov8l-cls.pt',
18
+ 'YOLOv8 (X-Large)': 'yolov8x-cls.pt',
19
+
20
+ # YOLOv11 classification models
21
+ 'YOLOv11 (Nano)': 'yolov11n-cls.pt',
22
+ 'YOLOv11 (Small)': 'yolov11s-cls.pt',
23
+ 'YOLOv11 (Medium)': 'yolov11m-cls.pt',
24
+ 'YOLOv11 (Large)': 'yolov11l-cls.pt',
25
+ 'YOLOv11 (X-Large)': 'yolov11x-cls.pt',
26
+
27
+ # YOLOv12 classification models
28
+ 'YOLOv12 (Nano)': 'yolov12n-cls.pt',
29
+ 'YOLOv12 (Small)': 'yolov12s-cls.pt',
30
+ 'YOLOv12 (Medium)': 'yolov12m-cls.pt',
31
+ 'YOLOv12 (Large)': 'yolov12l-cls.pt',
32
+ 'YOLOv12 (X-Large)': 'yolov12x-cls.pt',
33
+ }
34
+
35
+
36
+ def get_community_models(task='classify'):
37
+ """
38
+ Get available community models for a specific task.
39
+
40
+ Args:
41
+ task (str): The task type, default is 'classify'
42
+
43
+ Returns:
44
+ dict: Dictionary of community models
45
+ """
46
+ return get_available_configs(task=task)
47
+
48
+
49
+ def is_yolo_model(model_name):
50
+ """
51
+ Determine if a model name refers to a YOLO model.
52
+
53
+ This function checks if the model name indicates a YOLO model
54
+ that should be handled by the YOLO feature extraction pipeline.
55
+
56
+ Args:
57
+ model_name (str): The model name to check
58
+
59
+ Returns:
60
+ bool: True if this is a YOLO model, False otherwise
61
+ """
62
+ if not model_name or not isinstance(model_name, str):
63
+ return False
64
+
65
+ # Check if it's one of our known YOLO model IDs
66
+ if model_name in YOLO_MODELS.values():
67
+ return True
68
+
69
+ # Check if it's a community model (check both keys and values)
70
+ community_models = get_community_models()
71
+ if community_models:
72
+ if model_name in community_models or model_name in community_models.values():
73
+ return True
74
+
75
+ # Check for .pt file extension (any PyTorch model file)
76
+ if model_name.lower().endswith('.pt'):
77
+ return True
78
+
79
+ return False
80
+
81
+
82
+ def get_yolo_model_task(model_name):
83
+ """
84
+ Determine the task type of a YOLO model based on its name.
85
+
86
+ Args:
87
+ model_name (str): The model name or path
88
+
89
+ Returns:
90
+ str: One of 'classify', 'detect', 'segment', or 'unknown'
91
+ """
92
+ if not model_name or not isinstance(model_name, str):
93
+ return 'unknown'
94
+
95
+ # Check if it's a community model - all community models are classify
96
+ community_models = get_community_models()
97
+ if community_models:
98
+ if model_name in community_models or model_name in community_models.values():
99
+ return 'classify'
100
+
101
+ # Extract just the filename if a full path is provided
102
+ filename = model_name.split('/')[-1].split('\\')[-1].lower()
103
+
104
+ if '-cls' in filename:
105
+ return 'classify'
106
+ elif '-seg' in filename:
107
+ return 'segment'
108
+ elif filename.endswith('.pt'):
109
+ # Default YOLO models without specific suffixes are detection models
110
+ return 'detect'
111
+
112
+ return 'unknown'
@@ -197,30 +197,50 @@ class ExportTagLabAnnotations:
197
197
  """
198
198
  # Calculate bounding box, centroid, area, perimeter, and contour
199
199
  points = annotation.points
200
+ # Convert points to TagLab contour format
201
+ contour = self.taglabToPoints(np.array([[point.x(), point.y()] for point in points]))
202
+ inner_contours = []
203
+ if hasattr(annotation, 'holes') and annotation.holes:
204
+ # Convert holes to TagLab format
205
+ for hole in annotation.holes:
206
+ inner_contours.append(self.taglabToPoints(np.array([[point.x(), point.y()] for point in hole])))
207
+
208
+ # Calculate bounding box
200
209
  min_x = int(min(point.x() for point in points))
201
210
  min_y = int(min(point.y() for point in points))
202
211
  max_x = int(max(point.x() for point in points))
203
- max_y = int(max(point.y() for point in points))
212
+ max_y = int(max(point.y() for point in points))
204
213
  width = max_x - min_x
205
214
  height = max_y - min_y
215
+ bbox = [min_x, min_y, width, height]
216
+
217
+ # Calculate centroid
206
218
  centroid_x = float(f"{sum(point.x() for point in points) / len(points):.1f}")
207
219
  centroid_y = float(f"{sum(point.y() for point in points) / len(points):.1f}")
220
+ centroid = [centroid_x, centroid_y]
221
+
208
222
  area = float(f"{annotation.get_area():.1f}")
209
223
  perimeter = float(f"{annotation.get_perimeter():.1f}")
210
- contour = self.taglabToPoints(np.array([[point.x(), point.y()] for point in points]))
224
+ data = annotation.data if hasattr(annotation, 'data') else {}
225
+
226
+ # Pop these keys from data if they exist
227
+ class_name = data.pop('class_name', annotation.label.short_label_code)
228
+ instance_name = data.pop('instance_name', "coral0")
229
+ blob_name = data.pop('blob_name', f"c-0-{centroid_x}x-{centroid_y}y")
230
+ note = data.pop('note', "")
211
231
 
212
232
  annotation_dict = {
213
- "bbox": [min_y, min_x, width, height],
214
- "centroid": [centroid_x, centroid_y],
233
+ "bbox": bbox,
234
+ "centroid": centroid,
215
235
  "area": area,
216
236
  "perimeter": perimeter,
217
237
  "contour": contour,
218
- "inner contours": [],
219
- "class name": annotation.label.short_label_code,
220
- "instance name": "coral0", # Placeholder, update as needed
221
- "blob name": f"c-0-{centroid_x}x-{centroid_y}y",
222
- "note": "",
223
- "data": {}
238
+ "inner contours": inner_contours,
239
+ "class name": class_name,
240
+ "instance name": instance_name,
241
+ "blob name": blob_name,
242
+ "note": note,
243
+ "data": data
224
244
  }
225
245
 
226
246
  return annotation_dict
@@ -161,23 +161,25 @@ class ImportTagLabAnnotations:
161
161
  short_label_code = label_info['name'].strip()
162
162
  long_label_code = label_info['name'].strip()
163
163
  color = QColor(*label_info['fill'])
164
-
165
- # Unpack the annotation data
166
- bbox = annotation['bbox']
167
- centroid = annotation['centroid']
168
- area = annotation['area']
169
- perimeter = annotation['perimeter']
170
- contour = annotation['contour']
171
- inner_contours = annotation['inner contours']
172
- class_name = annotation['class name']
173
- instance_name = annotation['instance name']
174
- blob_name = annotation['blob name']
175
- idx = annotation['id']
176
- note = annotation['note']
177
- data = annotation['data']
164
+
165
+ # Pack all other data into a dict
166
+ imported_data = {
167
+ 'bbox': annotation.get('bbox'),
168
+ 'centroid': annotation.get('centroid'),
169
+ 'area': annotation.get('area'),
170
+ 'perimeter': annotation.get('perimeter'),
171
+ 'class_name': annotation.get('class name'),
172
+ 'instance_name': annotation.get('instance name'),
173
+ 'blob_name': annotation.get('blob name'),
174
+ 'id': annotation.get('id'),
175
+ 'note': annotation.get('note'),
176
+ 'data': annotation.get('data'),
177
+ }
178
178
 
179
179
  # Convert contour string to points
180
180
  points = self.parse_contour(annotation['contour'])
181
+ # Convert inner contours to a list of lists of points (holes)
182
+ holes = [self.parse_contour(inner) for inner in annotation.get('inner contours', [])]
181
183
 
182
184
  # Create the label if it doesn't exist
183
185
  label = self.label_window.add_label_if_not_exists(short_label_code,
@@ -191,8 +193,12 @@ class ImportTagLabAnnotations:
191
193
  long_label_code=long_label_code,
192
194
  color=color,
193
195
  image_path=image_full_path,
194
- label_id=label_id
196
+ label_id=label_id,
197
+ holes=holes,
195
198
  )
199
+ # Add additional data to the annotation
200
+ polygon_annotation.data = imported_data
201
+
196
202
  # Add annotation to the dict
197
203
  self.annotation_window.add_annotation_to_dict(polygon_annotation)
198
204
 
@@ -12,16 +12,12 @@ from PyQt5.QtWidgets import (QDialog, QFileDialog, QVBoxLayout, QPushButton, QLa
12
12
  QLineEdit)
13
13
 
14
14
  from coralnet_toolbox.QtLabelWindow import Label
15
-
16
15
  from coralnet_toolbox.QtWorkArea import WorkArea
17
-
18
16
  from coralnet_toolbox.Annotations.QtPatchAnnotation import PatchAnnotation
19
17
  from coralnet_toolbox.Annotations.QtPolygonAnnotation import PolygonAnnotation
20
18
  from coralnet_toolbox.Annotations.QtRectangleAnnotation import RectangleAnnotation
21
19
  from coralnet_toolbox.Annotations.QtMultiPolygonAnnotation import MultiPolygonAnnotation
22
-
23
20
  from coralnet_toolbox.Common.QtUpdateImagePaths import UpdateImagePaths
24
-
25
21
  from coralnet_toolbox.QtProgressBar import ProgressBar
26
22
 
27
23
 
@@ -143,19 +139,18 @@ class OpenProject(QDialog):
143
139
  with open(file_path, 'r') as file:
144
140
  project_data = json.load(file)
145
141
 
142
+ # Handle both new and old project formats for images and work areas
143
+ images_data = project_data.get('images', project_data.get('image_paths'))
144
+ legacy_workareas = project_data.get('workareas') # For backward compatibility
145
+
146
146
  # Update main window with loaded project data
147
- self.import_images(project_data.get('image_paths'))
148
- self.import_workareas(project_data.get('workareas'))
147
+ self.import_images(images_data, legacy_workareas)
149
148
  self.import_labels(project_data.get('labels'))
150
149
  self.import_annotations(project_data.get('annotations'))
151
150
 
152
151
  # Update current project path
153
152
  self.current_project_path = file_path
154
153
 
155
- QMessageBox.information(self.annotation_window,
156
- "Project Loaded",
157
- "Project has been successfully loaded.")
158
-
159
154
  except Exception as e:
160
155
  QMessageBox.warning(self.annotation_window,
161
156
  "Error Loading Project",
@@ -168,10 +163,15 @@ class OpenProject(QDialog):
168
163
  # Exit
169
164
  self.accept()
170
165
 
171
- def import_images(self, image_paths):
172
- """Import images from the given paths."""
173
- if not image_paths:
166
+ def import_images(self, images_data, legacy_workareas=None):
167
+ """Import images, states, and work areas from the given data."""
168
+ if not images_data:
174
169
  return
170
+
171
+ # Determine if the format is old (list of strings) or new (list of dicts)
172
+ is_new_format = isinstance(images_data[0], dict)
173
+
174
+ image_paths = [img['path'] for img in images_data] if is_new_format else images_data
175
175
 
176
176
  if not all([os.path.exists(path) for path in image_paths]):
177
177
  image_paths, self.updated_paths = UpdateImagePaths.update_paths(image_paths)
@@ -183,15 +183,46 @@ class OpenProject(QDialog):
183
183
  progress_bar.start_progress(total_images)
184
184
 
185
185
  try:
186
+ # Create a map for quick data lookup if using the new format
187
+ image_data_map = {img['path']: img for img in images_data} if is_new_format else {}
188
+
186
189
  # Add images to the image window's raster manager one by one
187
190
  for path in image_paths:
188
- # Use the improved add_image method which handles both
189
- # adding to raster_manager and updating filtered_paths
190
191
  self.image_window.add_image(path)
192
+ raster = self.image_window.raster_manager.get_raster(path)
193
+ if not raster:
194
+ continue
195
+
196
+ # If using the new format, apply saved state and work areas
197
+ if is_new_format and path in image_data_map:
198
+ data = image_data_map[path]
199
+ state = data.get('state', {})
200
+ work_areas_list = data.get('work_areas', [])
201
+
202
+ # Apply raster state
203
+ raster.checkbox_state = state.get('checkbox_state', False)
204
+
205
+ # Import work areas for this image
206
+ for work_area_data in work_areas_list:
207
+ try:
208
+ work_area = WorkArea.from_dict(work_area_data, path)
209
+ raster.add_work_area(work_area)
210
+ except Exception as e:
211
+ print(f"Warning: Could not import work area {work_area_data}: {str(e)}")
191
212
 
192
213
  # Update the progress bar
193
214
  progress_bar.update_progress()
194
215
 
216
+ # Handle backward compatibility for old, top-level work areas
217
+ if legacy_workareas:
218
+ for image_path, work_areas_list in legacy_workareas.items():
219
+ current_path = self.updated_paths.get(image_path, image_path)
220
+ raster = self.image_window.raster_manager.get_raster(current_path)
221
+ if raster:
222
+ for work_area_data in work_areas_list:
223
+ work_area = WorkArea.from_dict(work_area_data, current_path)
224
+ raster.add_work_area(work_area)
225
+
195
226
  # Show the last image if any were imported
196
227
  if self.image_window.raster_manager.image_paths:
197
228
  self.image_window.load_image_by_path(self.image_window.raster_manager.image_paths[-1])
@@ -204,69 +235,6 @@ class OpenProject(QDialog):
204
235
  # Close progress bar
205
236
  progress_bar.stop_progress()
206
237
  progress_bar.close()
207
-
208
- def import_workareas(self, workareas):
209
- """Import work areas for each image."""
210
- if not workareas:
211
- return
212
-
213
- # Start the progress bar
214
- total_images = len(workareas)
215
- progress_bar = ProgressBar(self.annotation_window, title="Importing Work Areas")
216
- progress_bar.show()
217
- progress_bar.start_progress(total_images)
218
-
219
- try:
220
- # Loop through each image's work areas
221
- for image_path, work_areas_list in workareas.items():
222
-
223
- # Check if the image path was updated (moved)
224
- updated_path = False
225
-
226
- if image_path not in self.image_window.raster_manager.image_paths:
227
- # Check if the path was updated
228
- if image_path in self.updated_paths:
229
- image_path = self.updated_paths[image_path]
230
- updated_path = True
231
- else:
232
- print(f"Warning: Image not found for work areas: {image_path}")
233
- continue
234
-
235
- # Get the raster for this image
236
- raster = self.image_window.raster_manager.get_raster(image_path)
237
- if not raster:
238
- print(f"Warning: Could not get raster for image: {image_path}")
239
- continue
240
-
241
- # Import each work area for this image
242
- for work_area_data in work_areas_list:
243
- try:
244
- # Update image path if it was changed
245
- if updated_path:
246
- work_area_data['image_path'] = image_path
247
-
248
- # Create WorkArea from dictionary
249
- work_area = WorkArea.from_dict(work_area_data, image_path)
250
-
251
- # Add work area to the raster
252
- raster.add_work_area(work_area)
253
-
254
- except Exception as e:
255
- print(f"Warning: Could not import work area {work_area_data}: {str(e)}")
256
- continue
257
-
258
- # Update the progress bar
259
- progress_bar.update_progress()
260
-
261
- except Exception as e:
262
- QMessageBox.warning(self.annotation_window,
263
- "Error Importing Work Areas",
264
- f"An error occurred while importing work areas: {str(e)}")
265
-
266
- finally:
267
- # Close progress bar
268
- progress_bar.stop_progress()
269
- progress_bar.close()
270
238
 
271
239
  def import_labels(self, labels):
272
240
  """Import labels from the given list."""
@@ -96,10 +96,9 @@ class SaveProject(QDialog):
96
96
 
97
97
  try:
98
98
  project_data = {
99
- 'image_paths': self.get_images(),
99
+ 'images': self.get_images(),
100
100
  'labels': self.get_labels(),
101
- 'annotations': self.get_annotations(),
102
- 'workareas': self.get_workareas()
101
+ 'annotations': self.get_annotations()
103
102
  }
104
103
 
105
104
  with open(file_path, 'w') as file:
@@ -125,10 +124,10 @@ class SaveProject(QDialog):
125
124
  self.accept()
126
125
 
127
126
  def get_images(self):
128
- """Get the list of image paths to export."""
127
+ """Get the list of image objects, including paths, states, and work areas."""
129
128
  # Start the progress bar
130
129
  total_images = len(self.image_window.raster_manager.image_paths)
131
- progress_bar = ProgressBar(self.label_window, "Exporting Images")
130
+ progress_bar = ProgressBar(self.label_window, "Exporting Image Data")
132
131
  progress_bar.show()
133
132
  progress_bar.start_progress(total_images)
134
133
 
@@ -137,7 +136,19 @@ class SaveProject(QDialog):
137
136
 
138
137
  # Loop through all of the image paths
139
138
  for image_path in self.image_window.raster_manager.image_paths:
140
- export_images.append(image_path)
139
+ raster = self.image_window.raster_manager.get_raster(image_path)
140
+ if raster:
141
+ # Get work areas for this raster
142
+ work_areas_list = [wa.to_dict() for wa in raster.get_work_areas()]
143
+
144
+ image_data = {
145
+ 'path': image_path,
146
+ 'state': {
147
+ 'checkbox_state': raster.checkbox_state
148
+ },
149
+ 'work_areas': work_areas_list
150
+ }
151
+ export_images.append(image_data)
141
152
  progress_bar.update_progress()
142
153
 
143
154
  except Exception as e:
@@ -234,42 +245,6 @@ class SaveProject(QDialog):
234
245
 
235
246
  return export_annotations
236
247
 
237
- def get_workareas(self):
238
- """Get the work areas to export."""
239
- # Start progress bar
240
- total_rasters = len(self.image_window.raster_manager.image_paths)
241
- progress_bar = ProgressBar(self.annotation_window, title="Exporting Work Areas")
242
- progress_bar.show()
243
- progress_bar.start_progress(total_rasters)
244
-
245
- try:
246
- export_workareas = {}
247
-
248
- # Loop through all rasters to get their work areas
249
- for image_path in self.image_window.raster_manager.image_paths:
250
- raster = self.image_window.raster_manager.get_raster(image_path)
251
- if raster and raster.has_work_areas():
252
- work_areas_list = []
253
- for work_area in raster.get_work_areas():
254
- work_areas_list.append(work_area.to_dict())
255
-
256
- if work_areas_list: # Only add if there are work areas
257
- export_workareas[image_path] = work_areas_list
258
-
259
- progress_bar.update_progress()
260
-
261
- except Exception as e:
262
- QMessageBox.warning(self.annotation_window,
263
- "Error Exporting Work Areas",
264
- f"An error occurred while exporting work areas: {str(e)}")
265
-
266
- finally:
267
- # Stop the progress bar
268
- progress_bar.stop_progress()
269
- progress_bar.close()
270
-
271
- return export_workareas
272
-
273
248
  def get_project_path(self):
274
249
  """Get the current project path."""
275
250
  return self.current_project_path
@@ -290,4 +265,4 @@ class SaveProject(QDialog):
290
265
  """Handle dialog rejection (Cancel or close)"""
291
266
  if self.current_project_path:
292
267
  self.file_path_edit.setText(self.current_project_path)
293
- super().reject()
268
+ super().reject()
@@ -42,7 +42,7 @@ class Base(QDialog):
42
42
  self.annotation_window = main_window.annotation_window
43
43
  self.image_window = main_window.image_window
44
44
 
45
- self.resize(1000, 800)
45
+ self.resize(800, 800)
46
46
  self.setWindowIcon(get_icon("coral.png"))
47
47
  self.setWindowTitle("Export Dataset")
48
48