diff --git a/scripts/debug_tool/GUI_debug.py b/scripts/debug_tool/GUI_debug.py index e599bc94348f228fbb659531a1f389b644d1b73b..670e565b89c522519e2f431080bad33db4b0419a 100644 --- a/scripts/debug_tool/GUI_debug.py +++ b/scripts/debug_tool/GUI_debug.py @@ -4,6 +4,7 @@ import os from tabs.config_tab import ConfigTab from tabs.shifter_tab import ShifterTab +from tabs.depth_tab import DepthTab from utils.config_reader import ConfigReader class ModuleDebugGUI(QMainWindow): @@ -36,6 +37,7 @@ class ModuleDebugGUI(QMainWindow): # Initialize tabs self.tabs.addTab(ConfigTab(self.config_reader), "Configuration") self.tabs.addTab(ShifterTab(self.config_reader), "Image Shifter") + self.tabs.addTab(DepthTab(self.config_reader), "MonoDepth Estimation") def main(): app = QApplication(sys.argv) diff --git a/scripts/debug_tool/tabs/depth_tab.py b/scripts/debug_tool/tabs/depth_tab.py index 62b81b2d065fe4343bc3069e33e39ea1af5a0108..30da9e51ae17ff8b1b2e3a9cddced0aa26365fb0 100644 --- a/scripts/debug_tool/tabs/depth_tab.py +++ b/scripts/debug_tool/tabs/depth_tab.py @@ -1,290 +1,158 @@ -import tkinter as tk -from tkinter import ttk, messagebox, filedialog +from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, + QGroupBox, QMessageBox) +from PyQt6.QtCore import Qt import os -from PIL import Image, ImageTk -import subprocess -import shutil -class DepthTab: - def __init__(self, notebook, config_reader): - self.tab = ttk.Frame(notebook) - notebook.add(self.tab, text='Depth Estimation') - +from utils.qt_widgets import (create_group_with_text, create_button_layout, + create_info_group, create_preview_group) +from utils.file_handlers import select_file, clean_directory, copy_file, run_command +from utils.image_handlers import update_preview + +class DepthTab(QWidget): + def __init__(self, config_reader): + super().__init__() self.config_reader = config_reader self.depth_input_path = None - # Update paths + # Initialize paths self.copy_dest_path = os.path.join( self.config_reader.directories['monoDepthDir'], 'rgb.jpg' ) self.depth_output_path = os.path.join( self.config_reader.directories['edgeNetDir'], - 'Data', - 'Input', - 'depth_e.png' + 'Data', 'Input', 'depth_e.png' ) self.input_dir = os.path.join( self.config_reader.directories['edgeNetDir'], - 'Data', - 'Input' + 'Data', 'Input' ) self.setup_ui() def setup_ui(self): - # Split into left and right frames - left_frame = ttk.Frame(self.tab) - left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5) - - right_frame = ttk.Frame(self.tab) - right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5) - - self.setup_control_panel(left_frame) - self.setup_preview_panel(right_frame) + main_layout = QHBoxLayout(self) + main_layout.setContentsMargins(10, 10, 10, 10) + + # Left panel (controls) + left_layout = QVBoxLayout() + self.setup_control_panel(left_layout) + main_layout.addLayout(left_layout) + + # Right panel (preview) + right_layout = QVBoxLayout() + self.setup_preview_panel(right_layout) + main_layout.addLayout(right_layout) - def setup_control_panel(self, parent): - # Control section - control_frame = ttk.LabelFrame(parent, text="Controls", padding="5") - control_frame.pack(fill=tk.X, pady=5) - - # Input file selection - input_frame = ttk.Frame(control_frame) - input_frame.pack(fill=tk.X, pady=5) - - ttk.Label(input_frame, text="Input file:").pack(side=tk.LEFT, padx=5) - self.input_label = ttk.Label(input_frame, text="No file selected") - self.input_label.pack(side=tk.LEFT, padx=5) - - # Copy destination display - copy_frame = ttk.Frame(control_frame) - copy_frame.pack(fill=tk.X, pady=5) - - ttk.Label(copy_frame, text="Will be copied to:").pack(side=tk.LEFT, padx=5) - self.copy_dest_label = ttk.Label(copy_frame, text=self.copy_dest_path) - self.copy_dest_label.pack(side=tk.LEFT, padx=5) - - # Output path display - output_frame = ttk.Frame(control_frame) - output_frame.pack(fill=tk.X, pady=5) - - ttk.Label(output_frame, text="Depth map will be saved to:").pack(side=tk.LEFT, padx=5) - self.output_label = ttk.Label(output_frame, text=self.depth_output_path) - self.output_label.pack(side=tk.LEFT, padx=5) - - # Control buttons - button_frame = ttk.Frame(control_frame) - button_frame.pack(fill=tk.X, pady=5) - - ttk.Button( - button_frame, - text="Select Input", - command=self.select_depth_input - ).pack(side=tk.LEFT, padx=5) - - ttk.Button( - button_frame, - text="Clean Input Directory", - command=self.clean_input_directory - ).pack(side=tk.LEFT, padx=5) - - ttk.Button( - button_frame, - text="Copy File", - command=self.copy_for_depth - ).pack(side=tk.LEFT, padx=5) - - self.run_depth_btn = ttk.Button( - button_frame, - text="Run Depth Estimation", - command=self.run_depth_estimation, - state='disabled' # Initially disabled until file is copied - ) - self.run_depth_btn.pack(side=tk.LEFT, padx=5) - - ttk.Button( - button_frame, - text="Clear Status", - command=self.clear_status - ).pack(side=tk.RIGHT, padx=5) - - # Status section - status_frame = ttk.LabelFrame(parent, text="Status", padding="5") - status_frame.pack(fill=tk.BOTH, expand=True, pady=5) - - # Add scrollbar to status - status_scroll = ttk.Scrollbar(status_frame) - status_scroll.pack(side=tk.RIGHT, fill=tk.Y) - - self.status_text = tk.Text( - status_frame, - height=10, - wrap=tk.WORD, - yscrollcommand=status_scroll.set - ) - self.status_text.pack(fill=tk.BOTH, expand=True) - status_scroll.config(command=self.status_text.yview) + def setup_control_panel(self, layout): + # Info display + info_rows = [ + ("Input file:", "No file selected"), + ("Copy destination:", self.copy_dest_path), + ("Depth map output:", self.depth_output_path) + ] + info_group, self.info_labels = create_info_group("Controls", info_rows) + layout.addWidget(info_group) + + # Buttons + buttons = [ + ("Select Input", self.handle_file_select, 'left'), + ("Clean Data/Input Dir", self.clean_input_dir, 'left'), + ("Remove rgb.png from dest path", self.remove_rgb, 'left'), + ("Copy File", self.copy_file, 'left'), + ("Run Depth Est.", self.run_depth_estimation, 'left'), + ("Clear Status", lambda: self.status_text.clear(), 'right') + ] + layout.addLayout(create_button_layout(*buttons)) + + # Status display + status_group, self.status_text = create_group_with_text("Status", 150) + layout.addWidget(status_group) - def setup_preview_panel(self, parent): - preview_frame = ttk.LabelFrame(parent, text="Image Preview", padding="5") - preview_frame.pack(fill=tk.BOTH, expand=True) - - # Input image preview - input_preview_frame = ttk.LabelFrame(preview_frame, text="RGB Image") - input_preview_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5) + def setup_preview_panel(self, layout): + preview_group = QGroupBox("Image Previews") + preview_layout = QHBoxLayout(preview_group) - self.input_preview = ttk.Label(input_preview_frame) - self.input_preview.pack(padx=5, pady=5) + input_group, self.input_preview = create_preview_group("RGB Image") + output_group, self.output_preview = create_preview_group("Depth Map") - # Output depth preview - output_preview_frame = ttk.LabelFrame(preview_frame, text="Depth Map") - output_preview_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5) - - self.output_preview = ttk.Label(output_preview_frame) - self.output_preview.pack(padx=5, pady=5) - - def clean_input_directory(self): - """Clean the edgenet input directory of any existing files""" - try: - if not os.path.exists(self.input_dir): - os.makedirs(self.input_dir) - self.update_status(f"Created input directory: {self.input_dir}") - return - - # List all files in the directory - files = os.listdir(self.input_dir) - if not files: - self.update_status("Input directory is already empty") - return - - # Remove each file - for file in files: - file_path = os.path.join(self.input_dir, file) - try: - if os.path.isfile(file_path): - os.remove(file_path) - elif os.path.isdir(file_path): - shutil.rmtree(file_path) - except Exception as e: - self.update_status(f"Error removing {file}: {str(e)}") - - self.update_status("Input directory cleaned successfully") - - # Clear the output preview as the depth map is now removed - self.output_preview.configure(image='') - - except Exception as e: - error_msg = f"Failed to clean input directory: {str(e)}" - self.update_status(error_msg) - messagebox.showerror("Error", error_msg) + preview_layout.addWidget(input_group) + preview_layout.addWidget(output_group) + layout.addWidget(preview_group) - def select_depth_input(self): - filepath = filedialog.askopenfilename( - filetypes=[("Image files", "*.png *.jpg *.jpeg")] + def handle_file_select(self): + file_path = select_file( + self, + "Select Input Image", + "Images (*.png *.jpg *.jpeg)" ) - if filepath: - self.depth_input_path = os.path.normpath(filepath) - self.input_label.config(text=os.path.basename(filepath)) - self.update_status(f"Selected input file: {filepath}") - self.update_depth_preview() - - # Reset button state - self.run_depth_btn.config(state='disabled') + + if file_path: + self.depth_input_path = file_path + self.info_labels["Input file:"].setText(os.path.basename(file_path)) + self.update_status(f"Selected input file: {file_path}") + update_preview(self.input_preview, file_path, + error_callback=self.update_status) - def update_status(self, message): - self.status_text.insert(tk.END, f"{message}\n") - self.status_text.see(tk.END) + def remove_rgb(self): + rgb_path = self.copy_dest_path + if os.path.exists(rgb_path): + os.remove(rgb_path) + self.update_status("Removed rgb.png") + else: + self.update_status("rgb.png not found") - def clear_status(self): - self.status_text.delete(1.0, tk.END) + def clean_input_dir(self): + success = clean_directory(self.input_dir, self.update_status) + if success: + self.output_preview.clear() + else: + QMessageBox.critical(self, "Error", "Failed to clean input directory") - def copy_for_depth(self): + def copy_file(self): if not self.depth_input_path: - messagebox.showwarning("Warning", "Please select an input file first") + QMessageBox.warning(self, "Warning", "Please select an input file first") + return + + # Copy to monodepth directory + if not copy_file(self.depth_input_path, self.copy_dest_path, self.update_status): + QMessageBox.critical(self, "Error", "Failed to copy file to monodepth directory") return - - try: - # Create directories if they don't exist - os.makedirs(self.config_reader.directories['monoDepthDir'], exist_ok=True) - os.makedirs(self.input_dir, exist_ok=True) - - # Copy file - shutil.copy2(self.depth_input_path, self.copy_dest_path) - - # Also copy to edgenet input directory as rgb.png - rgb_copy_path = os.path.join(self.input_dir, 'rgb.png') - shutil.copy2(self.depth_input_path, rgb_copy_path) - - self.update_status(f"Successfully copied file to:\n{self.copy_dest_path}\n{rgb_copy_path}") - self.run_depth_btn.config(state='normal') # Enable depth estimation button - - except Exception as e: - error_msg = f"Failed to copy file: {str(e)}" - self.update_status(error_msg) - messagebox.showerror("Error", error_msg) + + # Copy to edgenet directory + edge_rgb_path = os.path.join(self.input_dir, 'rgb.png') + if not copy_file(self.depth_input_path, edge_rgb_path, self.update_status): + QMessageBox.critical(self, "Error", "Failed to copy file to edgenet directory") def run_depth_estimation(self): try: self.update_status("Running depth estimation...") - # Change to mono depth directory + # Change to mono depth directory and run script original_dir = os.getcwd() os.chdir(self.config_reader.directories['monoDepthDir']) - # Run PowerShell script - process = subprocess.Popen( - ["powershell.exe", "-File", "masterscript.ps1"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True + success, output = run_command( + self, + "powershell.exe -File masterscript.ps1", + self.update_status ) - stdout, stderr = process.communicate() - # Change back to original directory os.chdir(original_dir) - if process.returncode == 0: - self.update_status("Depth estimation completed successfully") - if stdout: - self.update_status("Output:\n" + stdout) - - # Check for depth map in the correct location - if os.path.exists(self.depth_output_path): - self.update_status(f"Depth map generated at: {self.depth_output_path}") - self.update_depth_preview() - else: - self.update_status("Warning: Depth map not found at expected location") - else: - self.update_status("Depth estimation failed with error:\n" + stderr) - messagebox.showerror("Error", f"Command failed:\n\n{stderr}") + if success and os.path.exists(self.depth_output_path): + self.update_status(f"Depth map generated at: {self.depth_output_path}") + update_preview(self.output_preview, self.depth_output_path, + error_callback=self.update_status) except Exception as e: error_msg = f"Failed to run depth estimation: {str(e)}" self.update_status(error_msg) - messagebox.showerror("Error", error_msg) + QMessageBox.critical(self, "Error", error_msg) - def update_depth_preview(self): - preview_size = (300, 300) # Adjust size as needed - - # Update input preview - if self.depth_input_path and os.path.exists(self.depth_input_path): - try: - input_img = Image.open(self.depth_input_path) - input_img.thumbnail(preview_size) - input_photo = ImageTk.PhotoImage(input_img) - self.input_preview.configure(image=input_photo) - self.input_preview.image = input_photo - except Exception as e: - self.update_status(f"Failed to load input preview: {str(e)}") - - # Update depth map preview if exists - if os.path.exists(self.depth_output_path): - try: - depth_img = Image.open(self.depth_output_path) - depth_img.thumbnail(preview_size) - depth_photo = ImageTk.PhotoImage(depth_img) - self.output_preview.configure(image=depth_photo) - self.output_preview.image = depth_photo - except Exception as e: - self.update_status(f"Failed to load depth map preview: {str(e)}") \ No newline at end of file + def update_status(self, message): + self.status_text.append(message) + # Scroll to bottom + scrollbar = self.status_text.verticalScrollBar() + scrollbar.setValue(scrollbar.maximum()) \ No newline at end of file diff --git a/scripts/debug_tool/utils/file_handlers.py b/scripts/debug_tool/utils/file_handlers.py index 9746b2feeb45d4d7ae00a80da8a0c7514b931ff2..43486fbbed9abcafa154c03226ddebaf51cf24b1 100644 --- a/scripts/debug_tool/utils/file_handlers.py +++ b/scripts/debug_tool/utils/file_handlers.py @@ -2,6 +2,7 @@ from PyQt6.QtWidgets import QFileDialog, QMessageBox import subprocess import os +import shutil def select_file(parent, title="Select File", file_types="All Files (*)", initial_dir=None): """ @@ -126,4 +127,70 @@ def run_command(parent, cmd, status_callback=None): if status_callback: status_callback(error_msg) QMessageBox.critical(parent, "Error", error_msg) - return False, error_msg \ No newline at end of file + return False, error_msg + +def clean_directory(directory_path, status_callback=None): + """ + Cleans all files in a directory. + + Args: + directory_path: Directory to clean + status_callback: Optional callback for status updates + Returns: + bool: Success status + """ + try: + if not os.path.exists(directory_path): + os.makedirs(directory_path) + if status_callback: + status_callback(f"Created directory: {directory_path}") + return True + + files = os.listdir(directory_path) + if not files: + if status_callback: + status_callback("Directory is already empty") + return True + + for file in files: + file_path = os.path.join(directory_path, file) + try: + if os.path.isfile(file_path): + os.remove(file_path) + elif os.path.isdir(file_path): + shutil.rmtree(file_path) + except Exception as e: + if status_callback: + status_callback(f"Error removing {file}: {str(e)}") + return False + + if status_callback: + status_callback("Directory cleaned successfully") + return True + + except Exception as e: + if status_callback: + status_callback(f"Failed to clean directory: {str(e)}") + return False + +def copy_file(src, dest, status_callback=None): + """ + Copies a file to destination. + + Args: + src: Source file path + dest: Destination file path + status_callback: Optional callback for status updates + Returns: + bool: Success status + """ + try: + os.makedirs(os.path.dirname(dest), exist_ok=True) + shutil.copy2(src, dest) + if status_callback: + status_callback(f"Copied file to: {dest}") + return True + except Exception as e: + if status_callback: + status_callback(f"Failed to copy file: {str(e)}") + return False