coralnet-toolbox 0.0.70__py2.py3-none-any.whl → 0.0.72__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.
@@ -149,6 +149,21 @@ class FeatureStore:
149
149
  index_path = f"{self.index_path_base}_{model_key}.faiss"
150
150
  print(f"Saving FAISS index for '{model_key}' to {index_path}")
151
151
  faiss.write_index(index_to_save, index_path)
152
+
153
+ def remove_features_for_annotation(self, annotation_id):
154
+ """
155
+ Removes an annotation's feature metadata from the SQLite database.
156
+ This effectively orphans the vector in the FAISS index, invalidating it.
157
+ """
158
+ try:
159
+ self.cursor.execute(
160
+ "DELETE FROM features WHERE annotation_id = ?",
161
+ (annotation_id,)
162
+ )
163
+ self.conn.commit()
164
+ print(f"Invalidated features for annotation_id: {annotation_id}")
165
+ except sqlite3.Error as e:
166
+ print(f"Error removing feature for annotation {annotation_id}: {e}")
152
167
 
153
168
  def close(self):
154
169
  """Closes the database connection."""
@@ -670,8 +670,8 @@ class EmbeddingSettingsWidget(QGroupBox):
670
670
 
671
671
  def apply_embedding(self):
672
672
  if self.explorer_window and hasattr(self.explorer_window, 'run_embedding_pipeline'):
673
- # Clear all selections before running embedding pipeline
674
- if hasattr(self.explorer_window, 'handle_selection_change'):
675
- self.explorer_window.handle_selection_change([])
673
+ # Clear all selections before running a new embedding pipeline.
674
+ if hasattr(self.explorer_window, '_clear_selections'):
675
+ self.explorer_window._clear_selections()
676
676
 
677
677
  self.explorer_window.run_embedding_pipeline()
Binary file
@@ -660,6 +660,10 @@ class Base(QDialog):
660
660
  QMessageBox.warning(self, "Import Warning", "The YAML file appears to be empty or invalid.")
661
661
  return
662
662
 
663
+ # For backward compatibility, check if the old nested 'parameters' key exists.
664
+ # If not, use the whole data dictionary.
665
+ params_to_load = data.get('parameters', data)
666
+
663
667
  # Helper function to infer type from value
664
668
  def infer_type_and_value(value):
665
669
  """
@@ -673,12 +677,9 @@ class Base(QDialog):
673
677
  elif isinstance(value, float):
674
678
  return "float", value
675
679
  elif isinstance(value, str):
676
- # Check for boolean strings
677
680
  if value.lower() in ['true', 'false']:
678
681
  return "bool", value.lower() == 'true'
679
- # Check for numeric strings
680
682
  try:
681
- # Try to convert to int first
682
683
  if '.' not in value:
683
684
  return "int", int(value)
684
685
  else:
@@ -686,14 +687,13 @@ class Base(QDialog):
686
687
  except ValueError:
687
688
  return "string", value
688
689
  else:
689
- # For any other type, convert to string
690
690
  return "string", str(value)
691
691
 
692
692
  # Clear existing custom parameters before importing
693
693
  while self.custom_params:
694
694
  self.remove_parameter_pair()
695
695
 
696
- # Map parameters to UI controls
696
+ # Map standard parameters to their UI controls
697
697
  param_mapping = {
698
698
  'epochs': self.epochs_spinbox,
699
699
  'patience': self.patience_spinbox,
@@ -712,37 +712,31 @@ class Base(QDialog):
712
712
  }
713
713
 
714
714
  # Update UI controls with imported values
715
- for param_name, value in data.items():
715
+ for param_name, value in params_to_load.items():
716
716
  param_type, converted_value = infer_type_and_value(value)
717
717
 
718
718
  if param_name in param_mapping:
719
719
  widget = param_mapping[param_name]
720
720
 
721
721
  if isinstance(widget, QSpinBox):
722
- if param_type in ['int', 'float'] and isinstance(converted_value, (int, float)):
722
+ if isinstance(converted_value, (int, float)):
723
723
  widget.setValue(int(converted_value))
724
724
  elif isinstance(widget, QDoubleSpinBox):
725
- if param_type in ['int', 'float'] and isinstance(converted_value, (int, float)):
725
+ if isinstance(converted_value, (int, float)):
726
726
  widget.setValue(float(converted_value))
727
727
  elif isinstance(widget, QComboBox):
728
728
  if param_name in ['multi_scale', 'save', 'weighted', 'val', 'verbose']:
729
- # Boolean parameters
730
- if param_type == 'bool':
731
- widget.setCurrentText("True" if converted_value else "False")
732
- else:
733
- # String parameters like optimizer
734
- if str(converted_value) in [widget.itemText(i) for i in range(widget.count())]:
735
- widget.setCurrentText(str(converted_value))
729
+ widget.setCurrentText("True" if converted_value else "False")
730
+ elif str(converted_value) in [widget.itemText(i) for i in range(widget.count())]:
731
+ widget.setCurrentText(str(converted_value))
736
732
  else:
737
- # Add as custom parameter using inferred type
733
+ # Add as a custom parameter
738
734
  self.add_parameter_pair()
739
- param_widgets = self.custom_params[-1]
740
- param_name_widget, param_value_widget, param_type_widget = param_widgets
735
+ param_name_widget, param_value_widget, param_type_widget = self.custom_params[-1]
741
736
 
742
737
  param_name_widget.setText(param_name)
743
738
  param_type_widget.setCurrentText(param_type)
744
739
 
745
- # Set value based on type
746
740
  if param_type == "bool":
747
741
  param_value_widget.setText("True" if converted_value else "False")
748
742
  else:
@@ -750,14 +744,14 @@ class Base(QDialog):
750
744
 
751
745
  QMessageBox.information(self,
752
746
  "Import Success",
753
- "Parameters successfully imported with automatic type inference")
747
+ "Parameters successfully imported with automatic type inference.")
754
748
 
755
749
  except Exception as e:
756
750
  QMessageBox.critical(self, "Import Error", f"Failed to import parameters: {str(e)}")
757
751
 
758
752
  def export_parameters(self):
759
753
  """
760
- Export current parameters to a YAML file with explicit type information.
754
+ Export current parameters to a flat YAML file.
761
755
  """
762
756
  file_path, _ = QFileDialog.getSaveFileName(self,
763
757
  "Export Parameters to YAML",
@@ -767,69 +761,56 @@ class Base(QDialog):
767
761
  return
768
762
 
769
763
  try:
770
- # Structure: types section followed by parameters section
771
- export_data = {
772
- 'types': {},
773
- 'parameters': {}
774
- }
775
-
776
- # Standard parameters with their types
777
- standard_params = {
778
- 'epochs': ('int', self.epochs_spinbox.value()),
779
- 'patience': ('int', self.patience_spinbox.value()),
780
- 'imgsz': ('int', self.imgsz_spinbox.value()),
781
- 'batch': ('int', self.batch_spinbox.value()),
782
- 'workers': ('int', self.workers_spinbox.value()),
783
- 'save_period': ('int', self.save_period_spinbox.value()),
784
- 'freeze_layers': ('float', self.freeze_layers_spinbox.value()),
785
- 'dropout': ('float', self.dropout_spinbox.value()),
786
- 'multi_scale': ('bool', self.multi_scale_combo.currentText() == "True"),
787
- 'save': ('bool', self.save_combo.currentText() == "True"),
788
- 'weighted': ('bool', self.weighted_combo.currentText() == "True"),
789
- 'val': ('bool', self.val_combo.currentText() == "True"),
790
- 'verbose': ('bool', self.verbose_combo.currentText() == "True"),
791
- 'optimizer': ('string', self.optimizer_combo.currentText())
792
- }
793
-
794
- # Add standard parameters
795
- for param_name, (param_type, value) in standard_params.items():
796
- export_data['types'][param_name] = param_type
797
- export_data['parameters'][param_name] = value
764
+ # Use a single flat dictionary for export
765
+ export_data = {}
766
+
767
+ # Standard parameters
768
+ export_data['epochs'] = self.epochs_spinbox.value()
769
+ export_data['patience'] = self.patience_spinbox.value()
770
+ export_data['imgsz'] = self.imgsz_spinbox.value()
771
+ export_data['batch'] = self.batch_spinbox.value()
772
+ export_data['workers'] = self.workers_spinbox.value()
773
+ export_data['save_period'] = self.save_period_spinbox.value()
774
+ export_data['freeze_layers'] = self.freeze_layers_spinbox.value()
775
+ export_data['dropout'] = self.dropout_spinbox.value()
776
+ export_data['multi_scale'] = self.multi_scale_combo.currentText() == "True"
777
+ export_data['save'] = self.save_combo.currentText() == "True"
778
+ export_data['weighted'] = self.weighted_combo.currentText() == "True"
779
+ export_data['val'] = self.val_combo.currentText() == "True"
780
+ export_data['verbose'] = self.verbose_combo.currentText() == "True"
781
+ export_data['optimizer'] = self.optimizer_combo.currentText()
798
782
 
799
783
  # Custom parameters
800
784
  for param_info in self.custom_params:
801
- param_name, param_value, param_type = param_info
802
- name = param_name.text().strip()
803
- value = param_value.text().strip()
804
- type_name = param_type.currentText()
785
+ param_name_widget, param_value_widget, param_type_widget = param_info
786
+ name = param_name_widget.text().strip()
787
+ value_str = param_value_widget.text().strip()
788
+ type_name = param_type_widget.currentText()
805
789
 
806
- if name and value:
807
- export_data['types'][name] = type_name
808
-
809
- if type_name == "bool":
810
- export_data['parameters'][name] = value.lower() == "true"
811
- elif type_name == "int":
812
- try:
813
- export_data['parameters'][name] = int(value)
814
- except ValueError:
815
- export_data['parameters'][name] = value
816
- export_data['types'][name] = "string" # Fallback to string
817
- elif type_name == "float":
818
- try:
819
- export_data['parameters'][name] = float(value)
820
- except ValueError:
821
- export_data['parameters'][name] = value
822
- export_data['types'][name] = "string" # Fallback to string
823
- else: # string type
824
- export_data['parameters'][name] = value
825
-
826
- # Write to YAML file
790
+ if name and value_str:
791
+ # Convert value to the correct type before exporting
792
+ try:
793
+ if type_name == "bool":
794
+ value = value_str.lower() == "true"
795
+ elif type_name == "int":
796
+ value = int(value_str)
797
+ elif type_name == "float":
798
+ value = float(value_str)
799
+ else: # string type
800
+ value = value_str
801
+ export_data[name] = value
802
+ except ValueError:
803
+ # If conversion fails, save it as a string
804
+ print(f"Warning: Could not convert '{value_str}' to {type_name} for parameter '{name}'. Saving as string.")
805
+ export_data[name] = value_str
806
+
807
+ # Write the flat dictionary to the YAML file
827
808
  with open(file_path, 'w') as f:
828
- yaml.dump(export_data, f, default_flow_style=False, indent=2)
809
+ yaml.dump(export_data, f, default_flow_style=False, sort_keys=False, indent=2)
829
810
 
830
811
  QMessageBox.information(self,
831
812
  "Export Success",
832
- "Parameters successfully exported")
813
+ "Parameters successfully exported.")
833
814
 
834
815
  except Exception as e:
835
816
  QMessageBox.critical(self,
@@ -48,6 +48,7 @@ class AnnotationWindow(QGraphicsView):
48
48
  annotationSelected = pyqtSignal(int) # Signal to emit when annotation is selected
49
49
  annotationDeleted = pyqtSignal(str) # Signal to emit when annotation is deleted
50
50
  annotationCreated = pyqtSignal(str) # Signal to emit when annotation is created
51
+ annotationModified = pyqtSignal(str) # Signal to emit when annotation is modified
51
52
 
52
53
  def __init__(self, main_window, parent=None):
53
54
  """Initialize the annotation window with the main window and parent widget."""
@@ -374,6 +375,9 @@ class AnnotationWindow(QGraphicsView):
374
375
 
375
376
  def set_image(self, image_path):
376
377
  """Set and display an image at the given path."""
378
+ # Calculate GDIs for Windows if needed
379
+ self.main_window.check_windows_gdi_count()
380
+
377
381
  # Clean up
378
382
  self.clear_scene()
379
383
 
@@ -543,7 +547,7 @@ class AnnotationWindow(QGraphicsView):
543
547
  return type(self.selected_annotations[0])
544
548
  return None
545
549
 
546
- def select_annotation(self, annotation, multi_select=False):
550
+ def select_annotation(self, annotation, multi_select=False, quiet_mode=False):
547
551
  """Select an annotation and update the UI accordingly."""
548
552
  # If the annotation is already selected and Ctrl is pressed, unselect it
549
553
  if annotation in self.selected_annotations and multi_select:
@@ -569,7 +573,11 @@ class AnnotationWindow(QGraphicsView):
569
573
 
570
574
  # If this is the only selected annotation, update label window and confidence window
571
575
  if len(self.selected_annotations) == 1:
572
- self.labelSelected.emit(annotation.label.id)
576
+
577
+ if not quiet_mode:
578
+ # Emit the label selected signal, unless in quiet mode.
579
+ # This is in Explorer to avoid overwriting preview label.
580
+ self.labelSelected.emit(annotation.label.id)
573
581
 
574
582
  # Make sure we have a cropped image
575
583
  if not annotation.cropped_image:
@@ -4,6 +4,7 @@ warnings.filterwarnings("ignore", category=DeprecationWarning)
4
4
 
5
5
  import os
6
6
  import re
7
+ import ctypes
7
8
  import requests
8
9
 
9
10
  from packaging import version
@@ -130,6 +131,9 @@ class MainWindow(QMainWindow):
130
131
 
131
132
  def __init__(self, __version__):
132
133
  super().__init__()
134
+
135
+ # Get the process ID
136
+ self.pid = os.getpid()
133
137
 
134
138
  # Define icons
135
139
  self.coral_icon = get_icon("coral.png")
@@ -2329,6 +2333,62 @@ class MainWindow(QMainWindow):
2329
2333
  msg.exec_()
2330
2334
  except Exception as e:
2331
2335
  QMessageBox.critical(self, "Critical Error", f"{e}")
2336
+
2337
+ def check_windows_gdi_count(self):
2338
+ """Calculate and print the number of GDI objects for the current process on Windows."""
2339
+ # 1. Check if the OS is Windows. If not, return early.
2340
+ if os.name != 'nt':
2341
+ return
2342
+
2343
+ # Load necessary libraries
2344
+ kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
2345
+ user32 = ctypes.WinDLL('user32', use_last_error=True)
2346
+
2347
+ # Define constants
2348
+ PROCESS_QUERY_INFORMATION = 0x0400
2349
+ GR_GDIOBJECTS = 0
2350
+
2351
+ process_handle = None
2352
+ try:
2353
+ # 2. Get a handle to the process from its PID
2354
+ process_handle = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, self.pid)
2355
+
2356
+ if not process_handle:
2357
+ error_code = ctypes.get_last_error()
2358
+ raise RuntimeError(f"Failed to open PID {self.pid}. Error code: {error_code}")
2359
+
2360
+ # 3. Use the handle to get the GDI object count
2361
+ gdi_count = user32.GetGuiResources(process_handle, GR_GDIOBJECTS)
2362
+
2363
+ if gdi_count >= 9500: # GDI limit
2364
+ self.show_gdi_limit_warning()
2365
+
2366
+ except Exception as e:
2367
+ pass
2368
+
2369
+ finally:
2370
+ # 4. CRITICAL: Always close the handle when you're done
2371
+ if process_handle:
2372
+ kernel32.CloseHandle(process_handle)
2373
+
2374
+ return
2375
+
2376
+ def show_gdi_limit_warning(self):
2377
+ """
2378
+ Show a warning dialog if the GDI limit is reached.
2379
+ """
2380
+ try:
2381
+ self.untoggle_all_tools()
2382
+ msg = QMessageBox()
2383
+ msg.setWindowIcon(self.coral_icon)
2384
+ msg.setWindowTitle("GDI Limit Reached")
2385
+ msg.setText(
2386
+ "The GDI limit has been reached! Please immediately save your work, close, and reopen the application!"
2387
+ )
2388
+ msg.setStandardButtons(QMessageBox.Ok)
2389
+ msg.exec_()
2390
+ except Exception as e:
2391
+ QMessageBox.critical(self, "Critical Error", f"{e}")
2332
2392
 
2333
2393
  def open_snake_game_dialog(self):
2334
2394
  """
@@ -59,9 +59,14 @@ class ResizeSubTool(SubTool):
59
59
  def mouseReleaseEvent(self, event):
60
60
  """Finalize the resize, update related windows, and deactivate."""
61
61
  if self.target_annotation:
62
+ # Normalize the coordinates after resize is complete
63
+ if hasattr(self.target_annotation, 'normalize_coordinates'):
64
+ self.target_annotation.normalize_coordinates()
65
+
62
66
  self.target_annotation.create_cropped_image(self.annotation_window.rasterio_image)
63
67
  self.parent_tool.main_window.confidence_window.display_cropped_image(self.target_annotation)
64
-
68
+ self.annotation_window.annotationModified.emit(self.target_annotation.id) # Emit modified signal
69
+
65
70
  self.parent_tool.deactivate_subtool()
66
71
 
67
72
  # --- Handle Management Logic (moved from original class) ---
@@ -49,6 +49,7 @@ class SelectTool(Tool):
49
49
 
50
50
  # --- State for transient UI (like resize handles) ---
51
51
  self.resize_handles_visible = False
52
+ self.selection_locked = False
52
53
 
53
54
  self._connect_signals()
54
55
 
@@ -81,16 +82,36 @@ class SelectTool(Tool):
81
82
  self.deactivate_subtool()
82
83
  self._hide_resize_handles()
83
84
  self.annotation_window.viewport().setCursor(self.cursor)
85
+ self.selection_locked = False
84
86
 
85
87
  def deactivate(self):
86
88
  self.deactivate_subtool()
87
89
  self._hide_resize_handles()
88
90
  self.annotation_window.viewport().setCursor(self.default_cursor)
91
+ self.selection_locked = False
89
92
  super().deactivate()
90
93
 
91
94
  # --- Event Handlers (Dispatcher Logic) ---
92
95
 
93
96
  def mousePressEvent(self, event: QMouseEvent):
97
+ if self.selection_locked:
98
+ # If selection is locked, only allow interaction with resize handles.
99
+ # Check if a handle was clicked to start a resize operation.
100
+ position = self.annotation_window.mapToScene(event.pos())
101
+ items = self.annotation_window.scene.items(position)
102
+ if self.resize_handles_visible:
103
+ for item in items:
104
+ if item in self.resize_subtool.resize_handles_items:
105
+ handle_name = item.data(1)
106
+ if handle_name and len(self.selected_annotations) == 1:
107
+ self.set_active_subtool(
108
+ self.resize_subtool, event,
109
+ annotation=self.selected_annotations[0],
110
+ handle_name=handle_name
111
+ )
112
+ return # Exit after starting resize
113
+ return # Otherwise, ignore the click entirely
114
+
94
115
  # Ignore right mouse button events (used for panning)
95
116
  if event.button() == Qt.RightButton:
96
117
  return
@@ -215,14 +236,35 @@ class SelectTool(Tool):
215
236
  return self.annotation_window.annotations_dict.get(annotation_id) if annotation_id else None
216
237
 
217
238
  def _get_annotation_from_items(self, items, position):
218
- """Finds the first valid annotation at a position from a list of items."""
219
- for item in items:
220
- # We don't want to select by clicking a resize handle
221
- if item in self.resize_subtool.resize_handles_items:
222
- continue
239
+ """
240
+ Finds the first valid annotation at a position from a list of items.
241
+ First prioritizes annotations where the center graphic contains the point,
242
+ then falls back to any annotation that contains the point.
243
+ """
244
+ # Filter out resize handles
245
+ valid_items = [item for item in items if item not in self.resize_subtool.resize_handles_items]
246
+
247
+ center_threshold = 10.0 # Distance threshold in pixels to consider a click "on center"
248
+ center_candidates = []
249
+ general_candidates = []
250
+
251
+ # Gather all potential candidates
252
+ for item in valid_items:
223
253
  annotation = self._get_annotation_from_item(item)
224
254
  if annotation and annotation.contains_point(position):
225
- return annotation
255
+ # Calculate distance to center
256
+ center_distance = (position - annotation.center_xy).manhattanLength()
257
+ if center_distance <= center_threshold:
258
+ center_candidates.append(annotation)
259
+ else:
260
+ general_candidates.append(annotation)
261
+
262
+ # Return priority: center candidates first, then general candidates
263
+ if center_candidates:
264
+ return center_candidates[0]
265
+ elif general_candidates:
266
+ return general_candidates[0]
267
+
226
268
  return None
227
269
 
228
270
  def _handle_annotation_selection(self, position, items, modifiers):
@@ -1,6 +1,6 @@
1
1
  """Top-level package for CoralNet-Toolbox."""
2
2
 
3
- __version__ = "0.0.70"
3
+ __version__ = "0.0.72"
4
4
  __author__ = "Jordan Pierce"
5
5
  __email__ = "jordan.pierce@noaa.gov"
6
6
  __credits__ = "National Center for Coastal and Ocean Sciences (NCCOS)"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coralnet-toolbox
3
- Version: 0.0.70
3
+ Version: 0.0.72
4
4
  Summary: Tools for annotating and developing ML models for benthic imagery
5
5
  Author-email: Jordan Pierce <jordan.pierce@noaa.gov>
6
6
  License: MIT License
@@ -179,7 +179,7 @@ Enhance your CoralNet experience with these tools:
179
179
  - 🚀 Optimize: Productionize models for faster inferencing
180
180
  - ⚙️ Batch Inference: Perform predictions on multiple images, automatically
181
181
  - 🎞️ Video Inference: Perform predictions on a video in real-time, record the output and analytics
182
- - 🔮 Explorer: Cluster, view, and re-label annotations using embeddings, mapped from feature-space
182
+ - 🔮 [Explorer](https://youtu.be/68eZt5l_7nA): Cluster, view, and re-label annotations using embeddings, mapped from feature-space
183
183
  - ↔️ I/O: Import and Export annotations from / to CoralNet, Viscore, and TagLab
184
184
  - Export annotations as [GeoJSONs](https://datatracker.ietf.org/doc/html/rfc7946), segmentation masks
185
185
  - 📸 YOLO: Import and Export YOLO datasets for machine learning
@@ -1,20 +1,20 @@
1
- coralnet_toolbox/QtAnnotationWindow.py,sha256=3pGy_81qaOYOrD_356DSiyXzmuoQnHl5GCp3o-9ay48,39096
1
+ coralnet_toolbox/QtAnnotationWindow.py,sha256=ZlaYYAMNqu95SJhduQxH1K0YP7mOkMv3BzGQwemfByg,39518
2
2
  coralnet_toolbox/QtConfidenceWindow.py,sha256=L5hR23uW91GpqnsNS9R1XF3zCTe2aU7w0iDoQMV0oyE,16190
3
3
  coralnet_toolbox/QtEventFilter.py,sha256=KKC9de3e66PvGVgiML8P7MZ9-r7vvHidPJJYpcbTwyM,6696
4
4
  coralnet_toolbox/QtImageWindow.py,sha256=vLziMSEWFfVRSBN0nUNkosgk3LiNxZDqPwbinz9ZivQ,49356
5
5
  coralnet_toolbox/QtLabelWindow.py,sha256=-4GCk4pTY9g4ADH1iE__4xwqT-7UR_7VCT8v-bJzerk,50869
6
- coralnet_toolbox/QtMainWindow.py,sha256=W86kc-ZFmZHcSKcNTWN5RFCvt7sysvTCYZrPWZPWd6E,112220
6
+ coralnet_toolbox/QtMainWindow.py,sha256=z_Erak0fJC4x962rsFgK3IwjLQYVUe6rayzQzfJtkhw,114372
7
7
  coralnet_toolbox/QtPatchSampling.py,sha256=Ehj06auBGfQwIruLNYQjF8eFOCpl8G72p42UXXb2mUo,29013
8
8
  coralnet_toolbox/QtProgressBar.py,sha256=pnozUOcVjfO_yTS9z8wOMPcrrrOtG_FeCknTcdI6eyk,6250
9
9
  coralnet_toolbox/QtWorkArea.py,sha256=YXRvHQKpWUtWyv_o9lZ8rmxfm28dUOG9pmMUeimDhQ4,13578
10
- coralnet_toolbox/__init__.py,sha256=jJh2Y95YrQKB66vTaSKx69MySCS7FLjHdOkQcpz04A4,207
10
+ coralnet_toolbox/__init__.py,sha256=-OIPFY1gy8COaSsB29_2bWyVd3ro4BAdKgI2eIvHhPY,207
11
11
  coralnet_toolbox/main.py,sha256=6j2B_1reC_KDmqvq1C0fB-UeSEm8eeJOozp2f4XXMLQ,1573
12
12
  coralnet_toolbox/utilities.py,sha256=eUkxXuWaNFH83LSW-KniwujkXKJ2rK04czx3k3OPiAY,27115
13
13
  coralnet_toolbox/Annotations/QtAnnotation.py,sha256=-3ASbjl1dXw9U731vyCgwyiyZT9zOD5Mvp1jt-7bCnA,29242
14
14
  coralnet_toolbox/Annotations/QtMultiPolygonAnnotation.py,sha256=ErAT31gw-zhEVNxkPRpyB9uw-NSpPh-ShCBxpscXdRw,15579
15
15
  coralnet_toolbox/Annotations/QtPatchAnnotation.py,sha256=67fNnK_-muyhGZdGB0kBDx-JGuflv1TM6q5ikfW_zOk,20076
16
16
  coralnet_toolbox/Annotations/QtPolygonAnnotation.py,sha256=1EkZEJlO4VZ4so01Sat2T8LeO1LNs7HbGJLO-G2_73Q,26886
17
- coralnet_toolbox/Annotations/QtRectangleAnnotation.py,sha256=nLL_HIkcnuB6CeqE-v8LMAoFgTEhZGHwt5yeEnLaVWQ,20090
17
+ coralnet_toolbox/Annotations/QtRectangleAnnotation.py,sha256=TgeawekA3jNBlCmZBRqXYRHoZqch7pWM-NSBBPG6S60,21404
18
18
  coralnet_toolbox/Annotations/__init__.py,sha256=bpMldC70tT_lzMrOdBNDkEhG9dCX3tXEBd48IrcUg3E,419
19
19
  coralnet_toolbox/AutoDistill/QtBatchInference.py,sha256=k871aW3XRX8kc4BDaS1aipbPh9WOZxgmilF2c4KOdVA,5646
20
20
  coralnet_toolbox/AutoDistill/QtDeployModel.py,sha256=6alhzvA3KYEeLaQj-Qhs9GicjNQyVoQbnvgZ3lxGnCU,25162
@@ -35,10 +35,10 @@ coralnet_toolbox/Common/QtUpdateImagePaths.py,sha256=_hJYx6hXdAOfH_m77f75AQduQ0W
35
35
  coralnet_toolbox/CoralNet/QtAuthenticate.py,sha256=Y__iY0Kcosz6AOV7dlJBwiB6Hte40wHahHe-OmRngZA,13267
36
36
  coralnet_toolbox/CoralNet/QtDownload.py,sha256=HBb8TpZRIEFirGIaIAV1v8qg3fL4cP6Bf-hUiqXoiLE,48516
37
37
  coralnet_toolbox/CoralNet/__init__.py,sha256=ILkAZh6mlAK1UaCCZjCB9JZxd-oY4cIgfnIC8UgjjIU,188
38
- coralnet_toolbox/Explorer/QtDataItem.py,sha256=-O2Tneh9wYbAZarqwb_Cvy5cP1F_zQH2IAw9c-rHy1Y,13572
39
- coralnet_toolbox/Explorer/QtExplorer.py,sha256=iKtp3m7PowAZCOmJSx-Z34M50kmVCUNHrbCBQIBd12k,118900
40
- coralnet_toolbox/Explorer/QtFeatureStore.py,sha256=kMn--vuBed6wZS-BQhHt_KBA5z-tL1ydFgFkkIoGiB4,6742
41
- coralnet_toolbox/Explorer/QtSettingsWidgets.py,sha256=hIMj2lzqGKBoFWKYolH7bEPm5ePAIzYcGjYRQv2uWFE,27656
38
+ coralnet_toolbox/Explorer/QtDataItem.py,sha256=fNpCHJSxMzHL2XpBXtPwKchSbmY7H0HWzL1Kbs4W1Ts,14920
39
+ coralnet_toolbox/Explorer/QtExplorer.py,sha256=vAtGTNsEUcy14-_XJuOocH7xdAlesHm5faN9AINaEvg,131591
40
+ coralnet_toolbox/Explorer/QtFeatureStore.py,sha256=3VwGezs1stmu65Z4ZQpvY27rGEIJq_prERWkFwMATBo,7378
41
+ coralnet_toolbox/Explorer/QtSettingsWidgets.py,sha256=unm23yP329cVL84aOvy20DBt3YBHVLU85rnfN9VUF8A,27649
42
42
  coralnet_toolbox/Explorer/__init__.py,sha256=wZPhf2oaUUyIQ2WK48Aj-4q1ENIZG2dGl1HF_mjhI6w,116
43
43
  coralnet_toolbox/IO/QtExportAnnotations.py,sha256=xeaS0BukC3cpkBIGT9DXRqHmvHhp-vOU47h6EoANpNg,4474
44
44
  coralnet_toolbox/IO/QtExportCoralNetAnnotations.py,sha256=4royhF63EmeOlSIBX389EUjjvE-SF44_maW6qm52mdA,2778
@@ -89,6 +89,7 @@ coralnet_toolbox/Icons/rocket.png,sha256=iMlRGlrNBS_dNBD2XIpN4RSrphCGbw_Ds1AYJ01
89
89
  coralnet_toolbox/Icons/select.png,sha256=twnMIO9ylQYjvyGnAR28V6K3ds6xpArZQTrvf0uxS6g,1896
90
90
  coralnet_toolbox/Icons/settings.png,sha256=rklROt3oKrfEk_qwN9J-JwvKok08iOkZy3OD4oNsLJQ,1376
91
91
  coralnet_toolbox/Icons/snake.png,sha256=cwcekSkXwDi_fhtTU48u7FN4bIybbY53cWK0n7-IN9A,2361
92
+ coralnet_toolbox/Icons/target.png,sha256=jzb-S_sXWT8MfbvefhDNsuTdAZgV2nGf1ieawaCkByM,1702
92
93
  coralnet_toolbox/Icons/tile.png,sha256=WiXKBpWVBfPv7gC8dnkc_gW3wuLQmLUyxYMWEM-G9ZU,382
93
94
  coralnet_toolbox/Icons/transparent.png,sha256=ZkuGkVzh6zLVNau1Wj166-TtUlbCRqJObGt4vxMxnLk,1098
94
95
  coralnet_toolbox/Icons/turtle.png,sha256=55OG5atmEs8nIUiN2B5hW-Jx1fpuY9QI-zolQoUOKWw,1971
@@ -147,7 +148,7 @@ coralnet_toolbox/MachineLearning/MergeDatasets/QtClassify.py,sha256=FI4WdxJ4-vtn
147
148
  coralnet_toolbox/MachineLearning/MergeDatasets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
148
149
  coralnet_toolbox/MachineLearning/OptimizeModel/QtBase.py,sha256=06irheL8aKvtwKBQLLJUohvWvrMqKFC-jhEEoVqIYdg,8890
149
150
  coralnet_toolbox/MachineLearning/OptimizeModel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
150
- coralnet_toolbox/MachineLearning/TrainModel/QtBase.py,sha256=lLGmgDSvjQFYD2RdF7eC6U5xU5in2rC26RQ3lmKvfLY,37460
151
+ coralnet_toolbox/MachineLearning/TrainModel/QtBase.py,sha256=f__pgcG8yyV4IQbICED4q4YWO6GnNtD5b5UGpWuv4c0,36580
151
152
  coralnet_toolbox/MachineLearning/TrainModel/QtClassify.py,sha256=ss5ppGbrpULzUPmeRmfqZjiqZPp7XbdUZ4BzSX0ehu0,3267
152
153
  coralnet_toolbox/MachineLearning/TrainModel/QtDetect.py,sha256=uxopZkrNkl3tImMNSDwC2ENpFAxdG0NLiwRwqNnbep0,4467
153
154
  coralnet_toolbox/MachineLearning/TrainModel/QtSegment.py,sha256=y8bpNS24SQxyg967RSi6TraqHSmlJYj8kbvC_5HMBIM,3597
@@ -205,19 +206,19 @@ coralnet_toolbox/Tools/QtPanTool.py,sha256=q0g5Ryse6mIZ_Ss4qJw5NNwgoLuQQBIyQTXNF
205
206
  coralnet_toolbox/Tools/QtPatchTool.py,sha256=57vFeR2jQ_VQRlMEIC_mH8NigUqOlVvmhaVkXDvd_Gw,5574
206
207
  coralnet_toolbox/Tools/QtPolygonTool.py,sha256=yxnkwK3rb52pWCq7a3iAABhHUSS_a3vkL7G7Ev0uLDA,9174
207
208
  coralnet_toolbox/Tools/QtRectangleTool.py,sha256=gYOOsn1WRHLG0YzkKmmM7OzLpuLNh8GWIZ4MloXoLDc,7218
208
- coralnet_toolbox/Tools/QtResizeSubTool.py,sha256=-knQTmwURVYItfmDUCMjITsdRFAOhvDVCGP-2l9AkTE,5496
209
+ coralnet_toolbox/Tools/QtResizeSubTool.py,sha256=derPy4adRj758-xYtjL-_35yGBOjoSe_DRE48HpdQpA,5836
209
210
  coralnet_toolbox/Tools/QtSAMTool.py,sha256=TQ--xcR76lymFS0YVo5Gi4ay_tIsIEecYpLDMRBPLWQ,26174
210
211
  coralnet_toolbox/Tools/QtSeeAnythingTool.py,sha256=2uxyX_chOIXcmW6oBlb6XlgbRmSwSaQXmkmgOtFrqI4,30606
211
212
  coralnet_toolbox/Tools/QtSelectSubTool.py,sha256=xKtXYCwezLq3YZQLsSTG3mxs_ZRjLiPrYl-0ebgq-GA,3125
212
- coralnet_toolbox/Tools/QtSelectTool.py,sha256=Es4h-YdUyrebPoeI4cGaM4rOEiJtIzK2PObFkBisHp8,17917
213
+ coralnet_toolbox/Tools/QtSelectTool.py,sha256=rSzM9s7pMxrLqvcWgIcEnpEQhYHU6TbGUna8ZaamakA,19957
213
214
  coralnet_toolbox/Tools/QtSubTool.py,sha256=H25FoFqywdi6Bl35MfpEXGrr48ZTgdRRvHMxUy1tqN4,1601
214
215
  coralnet_toolbox/Tools/QtTool.py,sha256=2MCjT151gYBN8KbsK0GX4WOrEg1uw3oeSkp7Elw1AUA,2531
215
216
  coralnet_toolbox/Tools/QtWorkAreaTool.py,sha256=-CDrEPenOdSI3sf5wn19Cip4alE1ef7WsRDxQFDkHlc,22162
216
217
  coralnet_toolbox/Tools/QtZoomTool.py,sha256=F9CAoABv1jxcUS7dyIh1FYjgjOXYRI1xtBPNIR1g62o,4041
217
218
  coralnet_toolbox/Tools/__init__.py,sha256=218iQ8IFXIkKXiUDVYtXk9e08UY9-LhHjcryaJAanQ0,797
218
- coralnet_toolbox-0.0.70.dist-info/licenses/LICENSE.txt,sha256=AURacZ_G_PZKqqPQ9VB9Sqegblk67RNgWSGAYKwXXMY,521
219
- coralnet_toolbox-0.0.70.dist-info/METADATA,sha256=2BX8ksbRkMXuBR4m5xY6ks_qfTqmCZjEPwkpwCDccW0,18120
220
- coralnet_toolbox-0.0.70.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
221
- coralnet_toolbox-0.0.70.dist-info/entry_points.txt,sha256=oEeMoDlJ_2lq95quOeDHIx9hZpubUlSo80OLtgbcrbM,63
222
- coralnet_toolbox-0.0.70.dist-info/top_level.txt,sha256=SMWPh4_9JfB8zVpPOOvjucV2_B_hvWW7bNWmMjG0LsY,17
223
- coralnet_toolbox-0.0.70.dist-info/RECORD,,
219
+ coralnet_toolbox-0.0.72.dist-info/licenses/LICENSE.txt,sha256=AURacZ_G_PZKqqPQ9VB9Sqegblk67RNgWSGAYKwXXMY,521
220
+ coralnet_toolbox-0.0.72.dist-info/METADATA,sha256=cOB6wtxUBZcPWkW0L9v3LCWIsI_NOaaiKo9RyQJl15M,18152
221
+ coralnet_toolbox-0.0.72.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
222
+ coralnet_toolbox-0.0.72.dist-info/entry_points.txt,sha256=oEeMoDlJ_2lq95quOeDHIx9hZpubUlSo80OLtgbcrbM,63
223
+ coralnet_toolbox-0.0.72.dist-info/top_level.txt,sha256=SMWPh4_9JfB8zVpPOOvjucV2_B_hvWW7bNWmMjG0LsY,17
224
+ coralnet_toolbox-0.0.72.dist-info/RECORD,,