Adding pen tool for annotation
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"""
|
||||
Annotation tab for the microscopy object detection application.
|
||||
Future feature for manual annotation.
|
||||
Manual annotation with pen tool and object class management.
|
||||
"""
|
||||
|
||||
from PySide6.QtWidgets import (
|
||||
@@ -21,13 +21,13 @@ from src.database.db_manager import DatabaseManager
|
||||
from src.utils.config_manager import ConfigManager
|
||||
from src.utils.image import Image, ImageLoadError
|
||||
from src.utils.logger import get_logger
|
||||
from src.gui.widgets import ImageDisplayWidget
|
||||
from src.gui.widgets import AnnotationCanvasWidget, AnnotationToolsWidget
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class AnnotationTab(QWidget):
|
||||
"""Annotation tab placeholder (future feature)."""
|
||||
"""Annotation tab for manual image annotation."""
|
||||
|
||||
def __init__(
|
||||
self, db_manager: DatabaseManager, config_manager: ConfigManager, parent=None
|
||||
@@ -37,6 +37,7 @@ class AnnotationTab(QWidget):
|
||||
self.config_manager = config_manager
|
||||
self.current_image = None
|
||||
self.current_image_path = None
|
||||
self.current_image_id = None
|
||||
|
||||
self._setup_ui()
|
||||
|
||||
@@ -52,49 +53,52 @@ class AnnotationTab(QWidget):
|
||||
self.left_splitter = QSplitter(Qt.Vertical)
|
||||
self.left_splitter.setHandleWidth(10)
|
||||
|
||||
# Image display section
|
||||
display_group = QGroupBox("Image Display")
|
||||
display_layout = QVBoxLayout()
|
||||
# Annotation canvas section
|
||||
canvas_group = QGroupBox("Annotation Canvas")
|
||||
canvas_layout = QVBoxLayout()
|
||||
|
||||
# Use the reusable ImageDisplayWidget
|
||||
self.image_display_widget = ImageDisplayWidget()
|
||||
self.image_display_widget.zoom_changed.connect(self._on_zoom_changed)
|
||||
display_layout.addWidget(self.image_display_widget)
|
||||
# Use the AnnotationCanvasWidget
|
||||
self.annotation_canvas = AnnotationCanvasWidget()
|
||||
self.annotation_canvas.zoom_changed.connect(self._on_zoom_changed)
|
||||
self.annotation_canvas.annotation_drawn.connect(self._on_annotation_drawn)
|
||||
canvas_layout.addWidget(self.annotation_canvas)
|
||||
|
||||
display_group.setLayout(display_layout)
|
||||
self.left_splitter.addWidget(display_group)
|
||||
canvas_group.setLayout(canvas_layout)
|
||||
self.left_splitter.addWidget(canvas_group)
|
||||
|
||||
# Zoom controls info
|
||||
zoom_info = QLabel("Zoom: Mouse wheel or +/- keys to zoom in/out")
|
||||
zoom_info.setStyleSheet("QLabel { color: #888; font-style: italic; }")
|
||||
self.left_splitter.addWidget(zoom_info)
|
||||
# Controls info
|
||||
controls_info = QLabel(
|
||||
"Zoom: Mouse wheel or +/- keys | Drawing: Enable pen and drag mouse"
|
||||
)
|
||||
controls_info.setStyleSheet("QLabel { color: #888; font-style: italic; }")
|
||||
self.left_splitter.addWidget(controls_info)
|
||||
# }
|
||||
|
||||
# { Right splitter for annotation tools and controls
|
||||
self.right_splitter = QSplitter(Qt.Vertical)
|
||||
self.right_splitter.setHandleWidth(10)
|
||||
|
||||
# Annotation tools section
|
||||
self.annotation_tools = AnnotationToolsWidget(self.db_manager)
|
||||
self.annotation_tools.pen_enabled_changed.connect(
|
||||
self.annotation_canvas.set_pen_enabled
|
||||
)
|
||||
self.annotation_tools.pen_color_changed.connect(
|
||||
self.annotation_canvas.set_pen_color
|
||||
)
|
||||
self.annotation_tools.pen_width_changed.connect(
|
||||
self.annotation_canvas.set_pen_width
|
||||
)
|
||||
self.annotation_tools.class_selected.connect(self._on_class_selected)
|
||||
self.annotation_tools.clear_annotations_requested.connect(
|
||||
self._on_clear_annotations
|
||||
)
|
||||
self.right_splitter.addWidget(self.annotation_tools)
|
||||
|
||||
# Image loading section
|
||||
load_group = QGroupBox("Image Loading")
|
||||
load_layout = QVBoxLayout()
|
||||
|
||||
# Future features info
|
||||
info_group = QGroupBox("Annotation Tool (Future Feature)")
|
||||
info_layout = QVBoxLayout()
|
||||
info_label = QLabel(
|
||||
"Full annotation functionality will be implemented in future version.\n\n"
|
||||
"Planned Features:\n"
|
||||
"- Drawing tools for bounding boxes\n"
|
||||
"- Class label assignment\n"
|
||||
"- Export annotations to YOLO format\n"
|
||||
"- Annotation verification"
|
||||
)
|
||||
info_label.setWordWrap(True)
|
||||
info_layout.addWidget(info_label)
|
||||
info_group.setLayout(info_layout)
|
||||
|
||||
self.right_splitter.addWidget(info_group)
|
||||
|
||||
# Load image button
|
||||
button_layout = QHBoxLayout()
|
||||
self.load_image_btn = QPushButton("Load Image")
|
||||
@@ -158,13 +162,22 @@ class AnnotationTab(QWidget):
|
||||
"annotation_tab/last_directory", str(Path(file_path).parent)
|
||||
)
|
||||
|
||||
# Display image using the ImageDisplayWidget
|
||||
self.image_display_widget.load_image(self.current_image)
|
||||
# Get or create image in database
|
||||
relative_path = str(Path(file_path).name) # Simplified for now
|
||||
self.current_image_id = self.db_manager.get_or_create_image(
|
||||
relative_path,
|
||||
Path(file_path).name,
|
||||
self.current_image.width,
|
||||
self.current_image.height,
|
||||
)
|
||||
|
||||
# Display image using the AnnotationCanvasWidget
|
||||
self.annotation_canvas.load_image(self.current_image)
|
||||
|
||||
# Update info label
|
||||
self._update_image_info()
|
||||
|
||||
logger.info(f"Loaded image: {file_path}")
|
||||
logger.info(f"Loaded image: {file_path} (DB ID: {self.current_image_id})")
|
||||
|
||||
except ImageLoadError as e:
|
||||
logger.error(f"Failed to load image: {e}")
|
||||
@@ -181,7 +194,7 @@ class AnnotationTab(QWidget):
|
||||
self.image_info_label.setText("No image loaded")
|
||||
return
|
||||
|
||||
zoom_percentage = self.image_display_widget.get_zoom_percentage()
|
||||
zoom_percentage = self.annotation_canvas.get_zoom_percentage()
|
||||
info_text = (
|
||||
f"File: {Path(self.current_image_path).name}\n"
|
||||
f"Size: {self.current_image.width}x{self.current_image.height} pixels\n"
|
||||
@@ -194,9 +207,36 @@ class AnnotationTab(QWidget):
|
||||
self.image_info_label.setText(info_text)
|
||||
|
||||
def _on_zoom_changed(self, zoom_scale: float):
|
||||
"""Handle zoom level changes from the image display widget."""
|
||||
"""Handle zoom level changes from the annotation canvas."""
|
||||
self._update_image_info()
|
||||
|
||||
def _on_annotation_drawn(self, points: list):
|
||||
"""Handle when an annotation stroke is drawn."""
|
||||
current_class = self.annotation_tools.get_current_class()
|
||||
|
||||
if not current_class:
|
||||
logger.warning("Annotation drawn but no object class selected")
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
"No Class Selected",
|
||||
"Please select an object class before drawing annotations.",
|
||||
)
|
||||
return
|
||||
|
||||
logger.info(
|
||||
f"Annotation drawn with {len(points)} points for class: {current_class['class_name']}"
|
||||
)
|
||||
# Future: Save annotation to database or export
|
||||
|
||||
def _on_class_selected(self, class_data: dict):
|
||||
"""Handle when an object class is selected."""
|
||||
logger.debug(f"Object class selected: {class_data['class_name']}")
|
||||
|
||||
def _on_clear_annotations(self):
|
||||
"""Handle clearing all annotations."""
|
||||
self.annotation_canvas.clear_annotations()
|
||||
logger.info("Cleared all annotations")
|
||||
|
||||
def _restore_state(self):
|
||||
"""Restore splitter positions from settings."""
|
||||
settings = QSettings("microscopy_app", "object_detection")
|
||||
|
||||
Reference in New Issue
Block a user