diff --git a/annolid/annotation/labelme2yolo.py b/annolid/annotation/labelme2yolo.py index 3d49272..e750258 100644 --- a/annolid/annotation/labelme2yolo.py +++ b/annolid/annotation/labelme2yolo.py @@ -101,7 +101,7 @@ class Labelme2YOLO: def __init__(self, json_dir, - yolo_dataset_name="YOLOV8_dataset" + yolo_dataset_name="YOLO_dataset" ): self.json_file_dir = json_dir self.label_to_id_dict = self.map_label_to_id(self.json_file_dir) diff --git a/annolid/gui/app.py b/annolid/gui/app.py index 6ade41f..eaf4249 100644 --- a/annolid/gui/app.py +++ b/annolid/gui/app.py @@ -296,6 +296,15 @@ def __init__(self, "Load SLEAP h5", self.tr("Load SLEAP h5") ) + + convert_labelme2yolo_format = action( + self.tr("&Convert Labelme to YOLO format"), + self.convert_labelme2yolo_format, + None, + "Convert Labelme to YOLO format", + self.tr("Convert Labelme to YOLO format") + ) + place_perference = action( self.tr("&Place Preference"), self.place_preference_analyze, @@ -467,6 +476,7 @@ def __init__(self, utils.addActions(self.menus.file, (convert_csv,)) utils.addActions(self.menus.file, (extract_shape_keypoints,)) utils.addActions(self.menus.file, (convert_sleap,)) + utils.addActions(self.menus.file, (convert_labelme2yolo_format,)) utils.addActions(self.menus.file, (place_perference,)) utils.addActions(self.menus.file, (advance_params,)) @@ -590,6 +600,11 @@ def convert_sleap_h5_to_labelme(self): convert_sleap_h5_widget = ConvertSleapDialog() convert_sleap_h5_widget.exec_() + def convert_labelme2yolo_format(self): + from annolid.gui.widgets import convert_labelme2yolo + convert_labelme2yolo_widget = convert_labelme2yolo.YOLOConverterWidget() + convert_labelme2yolo_widget.exec_() + def extract_and_save_shape_keypoints(self): extract_shape_keypoints_dialog = ExtractShapeKeyPointsDialog() extract_shape_keypoints_dialog.exec_() diff --git a/annolid/gui/widgets/convert_labelme2yolo.py b/annolid/gui/widgets/convert_labelme2yolo.py new file mode 100644 index 0000000..3fb36c0 --- /dev/null +++ b/annolid/gui/widgets/convert_labelme2yolo.py @@ -0,0 +1,99 @@ +import os +from qtpy.QtWidgets import ( + QDialog, QVBoxLayout, QLabel, QLineEdit, QPushButton, QFileDialog, QMessageBox, QProgressBar +) +from qtpy.QtCore import Qt +from annolid.annotation.labelme2yolo import Labelme2YOLO + + +class YOLOConverterWidget(QDialog): + def __init__(self, parent=None): + super(YOLOConverterWidget, self).__init__(parent) + + # Layout + self.setWindowTitle("LabelMe to YOLO Converter") + self.layout = QVBoxLayout() + + # Widgets for folder selection + self.json_dir_label = QLabel("Select JSON Directory:") + self.json_dir_input = QLineEdit() + self.browse_button = QPushButton("Browse") + self.browse_button.clicked.connect(self.select_json_directory) + + self.layout.addWidget(self.json_dir_label) + self.layout.addWidget(self.json_dir_input) + self.layout.addWidget(self.browse_button) + + # Validation and test size inputs + self.val_size_label = QLabel("Validation Size (0.0 to 1.0):") + self.val_size_input = QLineEdit("0.1") # Default value is 10% + self.test_size_label = QLabel("Test Size (0.0 to 1.0):") + self.test_size_input = QLineEdit("0.1") # Default value is 10% + + self.layout.addWidget(self.val_size_label) + self.layout.addWidget(self.val_size_input) + self.layout.addWidget(self.test_size_label) + self.layout.addWidget(self.test_size_input) + + # Convert button + self.convert_button = QPushButton("Convert to YOLO Format") + self.convert_button.clicked.connect(self.run_conversion) + self.layout.addWidget(self.convert_button) + + # Progress bar and message label + self.progress_bar = QProgressBar() + self.progress_bar.setAlignment(Qt.AlignCenter) + self.layout.addWidget(self.progress_bar) + + self.message_label = QLabel("") + self.layout.addWidget(self.message_label) + + self.setLayout(self.layout) + + def select_json_directory(self): + """ + Opens a file dialog to select the directory containing JSON files. + """ + json_dir = QFileDialog.getExistingDirectory( + self, "Select JSON Directory") + if json_dir: + self.json_dir_input.setText(json_dir) + + def run_conversion(self): + """ + Runs the LabelMe to YOLO conversion script. + """ + json_dir = self.json_dir_input.text().strip() + val_size = self.val_size_input.text().strip() + test_size = self.test_size_input.text().strip() + + if not os.path.isdir(json_dir): + QMessageBox.warning( + self, "Error", "Please select a valid JSON directory.") + return + + try: + val_size = float(val_size) + test_size = float(test_size) + if not (0 <= val_size <= 1) or not (0 <= test_size <= 1): + raise ValueError( + "Validation and test sizes must be between 0.0 and 1.0.") + except ValueError: + QMessageBox.warning( + self, "Error", "Please enter valid numbers for validation and test sizes.") + return + + self.progress_bar.setValue(10) + self.message_label.setText("Starting conversion...") + + try: + converter = Labelme2YOLO(json_dir) + converter.convert(val_size=val_size, test_size=test_size) + self.progress_bar.setValue(100) + self.message_label.setText("Conversion successful!") + QMessageBox.information( + self, "Success", "YOLO dataset created successfully.") + except Exception as e: + self.progress_bar.setValue(0) + self.message_label.setText(f"Error: {str(e)}") + QMessageBox.critical(self, "Error", f"An error occurred: {str(e)}")