diff --git a/scripts/debug_tool/utils/clickable_label.py b/scripts/debug_tool/utils/clickable_label.py new file mode 100644 index 0000000000000000000000000000000000000000..26516da6b37752dba65fb50602874da2310a683a --- /dev/null +++ b/scripts/debug_tool/utils/clickable_label.py @@ -0,0 +1,49 @@ +from PyQt6.QtWidgets import QInputDialog, QLabel +from PyQt6.QtCore import Qt, pyqtSignal +from PyQt6.QtGui import QPainter, QPen, QColor +from collections import deque + +class ClickableLabel(QLabel): + point_added = pyqtSignal() + def __init__(self, parent=None): + super().__init__(parent) + self.points = deque(maxlen=2) # Initialize a deque with a maximum length of 2 + + def mousePressEvent(self, event): + if event.button() == Qt.MouseButton.LeftButton: + x = event.position().x() + y = event.position().y() + number, ok = QInputDialog.getDouble(self, "Input Distance", "Enter a number (meters):", min=0) + if ok: + self.points.append((x, y, number)) + print(f"Stored: x={x}, y={y}, number={number}") + self.point_added.emit() + + def paintEvent(self, event): + super().paintEvent(event) + painter = QPainter(self) + pen = QPen(QColor("red")) + pen.setWidth(2) + painter.setPen(pen) + for i, point in enumerate(self.points): + x, y, _ = point + painter.drawEllipse(int(x) - 5, int(y) - 5, 10, 10) # Draw a hollow circle + painter.drawText(int(x) + 10, int(y), str(i + 1)) # Draw the number next to the circle + + + def get_points(self): + """ + Returns the stored points from the image. + + Returns: + list: List of tuples (x, y, number) + """ + return list(self.points) # Convert deque to list before returning + + def clear_points(self): + """ + Clears the stored points from the image. + """ + self.points.clear() + self.update() + self.point_added.emit() \ No newline at end of file diff --git a/scripts/simple_tab.py b/scripts/simple_tab.py index 9a4554c4dfb2c43a963f7ec252fbec908f5872b5..bdf7638ec1cbf2e6bf131e925a40b0d4542d49dd 100644 --- a/scripts/simple_tab.py +++ b/scripts/simple_tab.py @@ -1,6 +1,6 @@ from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QCheckBox, QMessageBox, QPushButton, - QProgressBar,QComboBox,QLabel) + QProgressBar, QComboBox, QLabel, QSizePolicy, QScrollArea) from PyQt6.QtCore import Qt, QThread, pyqtSignal, QTimer import os import sys @@ -9,7 +9,8 @@ import sys from debug_tool.utils.qt_widgets import (create_group_with_text, create_button_layout, create_info_group, create_preview_group) from debug_tool.utils.file_handlers import select_file, clean_directory, copy_file, run_command -from debug_tool.utils.image_handlers import update_preview +from debug_tool.utils.image_handlers import update_preview, load_and_resize_image, convert_cv_to_pixmap +from debug_tool.utils.clickable_label import ClickableLabel # Import existing module implementations from debug_tool.tabs.shifter_tab import ShifterTab @@ -150,13 +151,23 @@ class SimpleTab(QWidget): self.material.hide() self.edge_net.hide() + # Scroll Area + self.scroll_area = QScrollArea(self) + self.scroll_area.setWidgetResizable(True) + + self.scroll_content = QWidget() + self.file_selected = False self.flash_timer = QTimer(self) self.flash_timer.timeout.connect(self.toggle_flash) - self.setup_ui() + self.setup_ui(self.scroll_content) + + self.scroll_area.setWidget(self.scroll_content) + layout=QVBoxLayout(self) + layout.addWidget(self.scroll_area) - def setup_ui(self): - layout = QVBoxLayout(self) + def setup_ui(self,parent_widget): + layout = QVBoxLayout(parent_widget) # Controls section controls_group = QGroupBox("Pipeline Controls") @@ -164,7 +175,7 @@ class SimpleTab(QWidget): QGroupBox { font-weight: bold; border: 2px solid grey; - border-radius: 10px; + border-radius: 20px; margin-top: 10px; background-color: #3e3e3e; padding: 15px; @@ -330,6 +341,67 @@ class SimpleTab(QWidget): controls_layout.addLayout(buttons_layout) layout.addWidget(controls_group) + + # Image Distance section + self.image_distance_group = QGroupBox("Image Distance") + distance_layout = QVBoxLayout(self.image_distance_group) + info_label = QLabel("Please select two point on the image and input the distance from the camera to that point.") + self.counter_label = QLabel("(0/2)") + self.distance_preview = ClickableLabel() + self.distance_preview.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.counter_label.setAlignment(Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignCenter) + self.points_info_label = QLabel() # Label to display points and distances + self.points_info_label.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignCenter) + self.image_distance_group.setStyleSheet(""" + QGroupBox { + font-weight: bold; + border: 2px solid grey; + border-radius: 20px; + margin-top: 10px; + background-color: #3e3e3e; + padding: 20px; + } + QGroupBox::title { + margin: 10px; + background-color: transparent; + color: white; + } + QLabel { + margin: 5px; + background-color: #3e3e3e; + color: white; + } + """) + # Center the ClickableLabel within its parent layout + distance_preview_layout = QHBoxLayout() + distance_preview_layout.addStretch() + distance_preview_layout.addWidget(self.distance_preview) + distance_preview_layout.addStretch() + + self.distance_reset_btn = QPushButton("Reset Points") + self.distance_reset_btn.clicked.connect(self.distance_preview.clear_points) + self.distance_reset_btn.setFixedSize(150, 40) + self.distance_reset_btn.setStyleSheet(""" + QPushButton { + margin: 5px; + padding: 5px; + border-radius: 10px; + } + """) + distance_btn_layout = QHBoxLayout() + distance_btn_layout.addStretch() + distance_btn_layout.addWidget(self.distance_reset_btn) + distance_btn_layout.addStretch() + + distance_layout.addWidget(info_label) + distance_layout.addLayout(distance_preview_layout) + distance_layout.addWidget(self.points_info_label) + distance_layout.addWidget(self.counter_label) + distance_layout.addWidget(self.distance_reset_btn) + self.image_distance_group.hide() + layout.addWidget(self.image_distance_group) + + self.distance_preview.point_added.connect(self.update_counter_Label) # Status section status_group, self.status_text = create_group_with_text("Pipeline Status", 300) @@ -337,7 +409,7 @@ class SimpleTab(QWidget): QGroupBox { font-weight: bold; border: 2px solid grey; - border-radius: 10px; + border-radius: 20px; margin-top: 10px; background-color: #3e3e3e; padding: 20px; @@ -369,7 +441,7 @@ class SimpleTab(QWidget): QGroupBox { font-weight: bold; border: 2px solid grey; - border-radius: 10px; + border-radius: 20px; margin-top: 10px; background-color: #3e3e3e; padding: 20px; @@ -446,9 +518,15 @@ class SimpleTab(QWidget): self.update_status(f"Selected input file: {file_path}") update_preview(self.input_preview, file_path, error_callback=self.update_status) - + update_preview(self.distance_preview,file_path,max_size=1500) + pixmap = load_and_resize_image(file_path, 1500) + pixmap = convert_cv_to_pixmap(pixmap) + self.distance_preview.setFixedSize(pixmap.size()) + self.image_distance_group.show() + self.update_status("Waiting for distance points...") + self.info_labels["Status:"].setText("Waiting for distance points...") # Enable the run pipeline button - self.run_pipeline_btn.setEnabled(True) + #self.run_pipeline_btn.setEnabled(True) # Provide input path to all modules self.shifter.input_file_path = file_path @@ -471,6 +549,21 @@ class SimpleTab(QWidget): else: self.select_btn.setStyleSheet("QPushButton { margin: 5px; padding: 5px; background-color: DarkOrange; border-radius: 10px;}") + def update_counter_Label(self): + count = len(self.distance_preview.get_points()) + self.counter_label.setText(f"({count}/2)") + points_info = "\n".join([f"Point {i+1}: (x={x:.2f}, y={y:.2f}), Distance: {distance:.2f} meters)" + for i, (x, y, distance) in enumerate(self.distance_preview.get_points())]) + self.points_info_label.setText(points_info) + + #enable run pipeline button if 2 points are selected + if count == 2: + self.run_pipeline_btn.setEnabled(True) + self.update_status("Distance points selected. Ready to run pipeline.") + else: + #pass + self.run_pipeline_btn.setEnabled(False) + def run_full_pipeline(self): if not self.input_path: QMessageBox.warning(self, "Warning", "Please select an input file first") @@ -494,6 +587,14 @@ class SimpleTab(QWidget): self.setEnabled(False) self.progress_bar.setEnabled(True) # Keep progress bar enabled + #TODO: Add model selection for EdgeNet or MDBNet + # Set the SSC model + self.selected_model = self.ssc_model_combo.currentText() + + #TODO: Add distance points to the pipeline for depth estimation + # Get the distance points + self.distance_points = self.distance_preview.get_points() + # Start the pipeline self.pipeline_thread.start()