Fixing bounding box drawing
This commit is contained in:
@@ -90,6 +90,10 @@ class AnnotationTab(QWidget):
|
|||||||
self.annotation_tools.polyline_pen_width_changed.connect(
|
self.annotation_tools.polyline_pen_width_changed.connect(
|
||||||
self.annotation_canvas.set_polyline_pen_width
|
self.annotation_canvas.set_polyline_pen_width
|
||||||
)
|
)
|
||||||
|
# Show / hide bounding boxes
|
||||||
|
self.annotation_tools.show_bboxes_changed.connect(
|
||||||
|
self.annotation_canvas.set_show_bboxes
|
||||||
|
)
|
||||||
# RDP simplification controls
|
# RDP simplification controls
|
||||||
self.annotation_tools.simplify_on_finish_changed.connect(
|
self.annotation_tools.simplify_on_finish_changed.connect(
|
||||||
self._on_simplify_on_finish_changed
|
self._on_simplify_on_finish_changed
|
||||||
|
|||||||
@@ -146,12 +146,17 @@ class AnnotationCanvasWidget(QWidget):
|
|||||||
self.polyline_enabled = False
|
self.polyline_enabled = False
|
||||||
self.polyline_pen_color = QColor(255, 0, 0, 128) # Default red with 50% alpha
|
self.polyline_pen_color = QColor(255, 0, 0, 128) # Default red with 50% alpha
|
||||||
self.polyline_pen_width = 3
|
self.polyline_pen_width = 3
|
||||||
|
self.show_bboxes: bool = True # Control visibility of bounding boxes
|
||||||
|
|
||||||
# Current stroke and stored polylines (in image coordinates, pixel units)
|
# Current stroke and stored polylines (in image coordinates, pixel units)
|
||||||
self.current_stroke: List[Tuple[float, float]] = []
|
self.current_stroke: List[Tuple[float, float]] = []
|
||||||
self.polylines: List[List[Tuple[float, float]]] = []
|
self.polylines: List[List[Tuple[float, float]]] = []
|
||||||
self.stroke_meta: List[Dict[str, Any]] = [] # per-polyline style (color, width)
|
self.stroke_meta: List[Dict[str, Any]] = [] # per-polyline style (color, width)
|
||||||
|
|
||||||
|
# Stored bounding boxes in normalized coordinates (x_min, y_min, x_max, y_max)
|
||||||
|
self.bboxes: List[List[float]] = []
|
||||||
|
self.bbox_meta: List[Dict[str, Any]] = [] # per-bbox style (color, width)
|
||||||
|
|
||||||
# Legacy collection of strokes in normalized coordinates (kept for API compatibility)
|
# Legacy collection of strokes in normalized coordinates (kept for API compatibility)
|
||||||
self.all_strokes: List[dict] = []
|
self.all_strokes: List[dict] = []
|
||||||
|
|
||||||
@@ -219,6 +224,8 @@ class AnnotationCanvasWidget(QWidget):
|
|||||||
self.current_stroke = []
|
self.current_stroke = []
|
||||||
self.polylines = []
|
self.polylines = []
|
||||||
self.stroke_meta = []
|
self.stroke_meta = []
|
||||||
|
self.bboxes = []
|
||||||
|
self.bbox_meta = []
|
||||||
self.is_drawing = False
|
self.is_drawing = False
|
||||||
if self.annotation_pixmap:
|
if self.annotation_pixmap:
|
||||||
self.annotation_pixmap.fill(Qt.transparent)
|
self.annotation_pixmap.fill(Qt.transparent)
|
||||||
@@ -406,7 +413,7 @@ class AnnotationCanvasWidget(QWidget):
|
|||||||
self._redraw_annotations()
|
self._redraw_annotations()
|
||||||
|
|
||||||
def _redraw_annotations(self):
|
def _redraw_annotations(self):
|
||||||
"""Redraw all stored polylines onto the annotation pixmap."""
|
"""Redraw all stored polylines and (optionally) bounding boxes onto the annotation pixmap."""
|
||||||
if self.annotation_pixmap is None:
|
if self.annotation_pixmap is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -414,6 +421,8 @@ class AnnotationCanvasWidget(QWidget):
|
|||||||
self.annotation_pixmap.fill(Qt.transparent)
|
self.annotation_pixmap.fill(Qt.transparent)
|
||||||
|
|
||||||
painter = QPainter(self.annotation_pixmap)
|
painter = QPainter(self.annotation_pixmap)
|
||||||
|
|
||||||
|
# Draw polylines
|
||||||
for polyline, meta in zip(self.polylines, self.stroke_meta):
|
for polyline, meta in zip(self.polylines, self.stroke_meta):
|
||||||
pen_color: QColor = meta.get("color", self.polyline_pen_color)
|
pen_color: QColor = meta.get("color", self.polyline_pen_color)
|
||||||
width: int = meta.get("width", self.polyline_pen_width)
|
width: int = meta.get("width", self.polyline_pen_width)
|
||||||
@@ -427,6 +436,37 @@ class AnnotationCanvasWidget(QWidget):
|
|||||||
painter.setPen(pen)
|
painter.setPen(pen)
|
||||||
for (x1, y1), (x2, y2) in zip(polyline[:-1], polyline[1:]):
|
for (x1, y1), (x2, y2) in zip(polyline[:-1], polyline[1:]):
|
||||||
painter.drawLine(int(x1), int(y1), int(x2), int(y2))
|
painter.drawLine(int(x1), int(y1), int(x2), int(y2))
|
||||||
|
|
||||||
|
# Draw bounding boxes (dashed) if enabled
|
||||||
|
if self.show_bboxes and self.original_pixmap is not None and self.bboxes:
|
||||||
|
img_width = float(self.original_pixmap.width())
|
||||||
|
img_height = float(self.original_pixmap.height())
|
||||||
|
|
||||||
|
for bbox, meta in zip(self.bboxes, self.bbox_meta):
|
||||||
|
if len(bbox) != 4:
|
||||||
|
continue
|
||||||
|
|
||||||
|
x_min_norm, y_min_norm, x_max_norm, y_max_norm = bbox
|
||||||
|
x_min = int(x_min_norm * img_width)
|
||||||
|
y_min = int(y_min_norm * img_height)
|
||||||
|
x_max = int(x_max_norm * img_width)
|
||||||
|
y_max = int(y_max_norm * img_height)
|
||||||
|
|
||||||
|
rect_width = x_max - x_min
|
||||||
|
rect_height = y_max - y_min
|
||||||
|
|
||||||
|
pen_color: QColor = meta.get("color", QColor(255, 0, 0, 128))
|
||||||
|
width: int = meta.get("width", self.polyline_pen_width)
|
||||||
|
pen = QPen(
|
||||||
|
pen_color,
|
||||||
|
width,
|
||||||
|
Qt.DashLine,
|
||||||
|
Qt.SquareCap,
|
||||||
|
Qt.MiterJoin,
|
||||||
|
)
|
||||||
|
painter.setPen(pen)
|
||||||
|
painter.drawRect(x_min, y_min, rect_width, rect_height)
|
||||||
|
|
||||||
painter.end()
|
painter.end()
|
||||||
|
|
||||||
self._update_display()
|
self._update_display()
|
||||||
@@ -647,7 +687,7 @@ class AnnotationCanvasWidget(QWidget):
|
|||||||
Draw a bounding box from database coordinates onto the annotation canvas.
|
Draw a bounding box from database coordinates onto the annotation canvas.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
bbox: Bounding box as [y_min_norm, x_min_norm, y_max_norm, x_max_norm]
|
bbox: Bounding box as [x_min_norm, y_min_norm, x_max_norm, y_max_norm]
|
||||||
in normalized coordinates (0-1)
|
in normalized coordinates (0-1)
|
||||||
color: Color hex string (e.g., '#FF0000')
|
color: Color hex string (e.g., '#FF0000')
|
||||||
width: Line width in pixels
|
width: Line width in pixels
|
||||||
@@ -662,8 +702,7 @@ class AnnotationCanvasWidget(QWidget):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Convert normalized coordinates to image coordinates
|
# Convert normalized coordinates to image coordinates (for logging/debug)
|
||||||
# bbox format: [y_min_norm, x_min_norm, y_max_norm, x_max_norm]
|
|
||||||
img_width = self.original_pixmap.width()
|
img_width = self.original_pixmap.width()
|
||||||
img_height = self.original_pixmap.height()
|
img_height = self.original_pixmap.height()
|
||||||
|
|
||||||
@@ -677,29 +716,35 @@ class AnnotationCanvasWidget(QWidget):
|
|||||||
logger.debug(f" Image size: {img_width}x{img_height}")
|
logger.debug(f" Image size: {img_width}x{img_height}")
|
||||||
logger.debug(f" Pixel coords: ({x_min}, {y_min}) to ({x_max}, {y_max})")
|
logger.debug(f" Pixel coords: ({x_min}, {y_min}) to ({x_max}, {y_max})")
|
||||||
|
|
||||||
# Draw bounding box on annotation pixmap
|
# Store bounding box (normalized) and its style; actual drawing happens
|
||||||
painter = QPainter(self.annotation_pixmap)
|
# in _redraw_annotations() together with all polylines.
|
||||||
pen_color = QColor(color)
|
pen_color = QColor(color)
|
||||||
pen_color.setAlpha(128) # Add semi-transparency
|
pen_color.setAlpha(128) # Add semi-transparency
|
||||||
pen = QPen(pen_color, width, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin)
|
self.bboxes.append(
|
||||||
painter.setPen(pen)
|
[float(x_min_norm), float(y_min_norm), float(x_max_norm), float(y_max_norm)]
|
||||||
|
)
|
||||||
# Draw rectangle
|
self.bbox_meta.append({"color": pen_color, "width": int(width)})
|
||||||
rect_width = x_max - x_min
|
|
||||||
rect_height = y_max - y_min
|
|
||||||
painter.drawRect(x_min, y_min, rect_width, rect_height)
|
|
||||||
|
|
||||||
painter.end()
|
|
||||||
|
|
||||||
# Store in all_strokes for consistency
|
# Store in all_strokes for consistency
|
||||||
self.all_strokes.append(
|
self.all_strokes.append(
|
||||||
{"bbox": bbox, "color": color, "alpha": 128, "width": width}
|
{"bbox": bbox, "color": color, "alpha": 128, "width": width}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update display
|
# Redraw overlay (polylines + all bounding boxes)
|
||||||
self._update_display()
|
self._redraw_annotations()
|
||||||
logger.debug(f"Drew saved bounding box in color {color}")
|
logger.debug(f"Drew saved bounding box in color {color}")
|
||||||
|
|
||||||
|
def set_show_bboxes(self, show: bool):
|
||||||
|
"""
|
||||||
|
Enable or disable drawing of bounding boxes.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
show: If True, draw bounding boxes; if False, hide them.
|
||||||
|
"""
|
||||||
|
self.show_bboxes = bool(show)
|
||||||
|
logger.debug(f"Set show_bboxes to {self.show_bboxes}")
|
||||||
|
self._redraw_annotations()
|
||||||
|
|
||||||
def keyPressEvent(self, event: QKeyEvent):
|
def keyPressEvent(self, event: QKeyEvent):
|
||||||
"""Handle keyboard events for zooming."""
|
"""Handle keyboard events for zooming."""
|
||||||
if event.key() in (Qt.Key_Plus, Qt.Key_Equal):
|
if event.key() in (Qt.Key_Plus, Qt.Key_Equal):
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ class AnnotationToolsWidget(QWidget):
|
|||||||
polyline_pen_width_changed = Signal(int)
|
polyline_pen_width_changed = Signal(int)
|
||||||
simplify_on_finish_changed = Signal(bool)
|
simplify_on_finish_changed = Signal(bool)
|
||||||
simplify_epsilon_changed = Signal(float)
|
simplify_epsilon_changed = Signal(float)
|
||||||
|
# Toggle visibility of bounding boxes on the canvas
|
||||||
|
show_bboxes_changed = Signal(bool)
|
||||||
class_selected = Signal(dict)
|
class_selected = Signal(dict)
|
||||||
class_color_changed = Signal()
|
class_color_changed = Signal()
|
||||||
clear_annotations_requested = Signal()
|
clear_annotations_requested = Signal()
|
||||||
@@ -170,6 +172,12 @@ class AnnotationToolsWidget(QWidget):
|
|||||||
actions_group = QGroupBox("Actions")
|
actions_group = QGroupBox("Actions")
|
||||||
actions_layout = QVBoxLayout()
|
actions_layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# Show / hide bounding boxes
|
||||||
|
self.show_bboxes_checkbox = QCheckBox("Show bounding boxes")
|
||||||
|
self.show_bboxes_checkbox.setChecked(True)
|
||||||
|
self.show_bboxes_checkbox.stateChanged.connect(self._on_show_bboxes_toggle)
|
||||||
|
actions_layout.addWidget(self.show_bboxes_checkbox)
|
||||||
|
|
||||||
self.clear_btn = QPushButton("Clear All Annotations")
|
self.clear_btn = QPushButton("Clear All Annotations")
|
||||||
self.clear_btn.clicked.connect(self._on_clear_annotations)
|
self.clear_btn.clicked.connect(self._on_clear_annotations)
|
||||||
actions_layout.addWidget(self.clear_btn)
|
actions_layout.addWidget(self.clear_btn)
|
||||||
@@ -219,12 +227,12 @@ class AnnotationToolsWidget(QWidget):
|
|||||||
self.polyline_enabled = checked
|
self.polyline_enabled = checked
|
||||||
|
|
||||||
if checked:
|
if checked:
|
||||||
self.polyline_toggle_btn.setText("Start Drawing Polyline")
|
self.polyline_toggle_btn.setText("Stop Drawing Polyline")
|
||||||
self.polyline_toggle_btn.setStyleSheet(
|
self.polyline_toggle_btn.setStyleSheet(
|
||||||
"QPushButton { background-color: #4CAF50; }"
|
"QPushButton { background-color: #4CAF50; }"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.polyline_toggle_btn.setText("Stop drawing Polyline")
|
self.polyline_toggle_btn.setText("Start Drawing Polyline")
|
||||||
self.polyline_toggle_btn.setStyleSheet("")
|
self.polyline_toggle_btn.setStyleSheet("")
|
||||||
|
|
||||||
self.polyline_enabled_changed.emit(self.polyline_enabled)
|
self.polyline_enabled_changed.emit(self.polyline_enabled)
|
||||||
@@ -247,6 +255,12 @@ class AnnotationToolsWidget(QWidget):
|
|||||||
self.simplify_epsilon_changed.emit(epsilon)
|
self.simplify_epsilon_changed.emit(epsilon)
|
||||||
logger.debug(f"Simplification epsilon changed to {epsilon}")
|
logger.debug(f"Simplification epsilon changed to {epsilon}")
|
||||||
|
|
||||||
|
def _on_show_bboxes_toggle(self, state: int):
|
||||||
|
"""Handle 'Show bounding boxes' checkbox toggle."""
|
||||||
|
show = bool(state)
|
||||||
|
self.show_bboxes_changed.emit(show)
|
||||||
|
logger.debug(f"Show bounding boxes set to {show}")
|
||||||
|
|
||||||
def _on_color_picker(self):
|
def _on_color_picker(self):
|
||||||
"""Open color picker dialog and update the selected object's class color."""
|
"""Open color picker dialog and update the selected object's class color."""
|
||||||
if not self.current_class:
|
if not self.current_class:
|
||||||
|
|||||||
Reference in New Issue
Block a user