diff --git a/scripts/GUI.py b/scripts/GUI.py index bf8aecdd96476f2eee85522eb57d51f846253f8f..5a574f7f9cd0334e1727a63b9787264045aac9b6 100644 --- a/scripts/GUI.py +++ b/scripts/GUI.py @@ -1,196 +1,64 @@ -import tkinter as tk -import tkinter.filedialog -import subprocess +from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, + QTabWidget, QMessageBox, QGroupBox, QLabel) +from PyQt6.QtCore import Qt import sys -import time -from threading import Thread -import shutil import os -# Get the directory of the current script +# Add debug_tool to Python path for importing utilities SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) -# Get the root directory (AVVR-Pipeline-Internship) ROOT_DIR = os.path.dirname(SCRIPT_DIR) - -file_path = None -createDepth = "0" - -def shift_image_selection(): - # This function can be used if you want to perform any action when the checkbox is clicked - pass - -def copy_intermediary_outputs(): - source_folder = os.path.join(ROOT_DIR, "edgenet-360", "Data", "Input") - destination_folder = os.path.join(ROOT_DIR, "edgenet-360", "Output") - files_to_copy = ["depth_e.png", "enhanced_depth_e.png", "material.png", "rgb.png"] - - for file_name in files_to_copy: - source_path = os.path.join(source_folder, file_name) - destination_path = os.path.join(destination_folder, file_name) - try: - shutil.copy(source_path, destination_path) - print(f"Copied {file_name} to {destination_folder}") - except FileNotFoundError: - print(f"Warning: {file_name} not found in {source_folder}") - -def select_Image(event): - global file_path - file_path = tkinter.filedialog.askopenfilename() - file_path = os.path.normpath(file_path) - select_button.configure(text="Selected", bg="red") - label.configure(text="Image is selected. Press run to create scene.") - -def depthmap_creation(): - print("Manually upload depth map: ", uploadDepthCheck.get()) - - if uploadDepthCheck.get() == 1: # if manually upload checked - check.set(0) # disable auto generation of depth map - upload_depth_path = tkinter.filedialog.askopenfilename(title="Select a depth map", filetypes=[("PNG files", "*.png")]) #dialog box to upload depth map +DEBUG_TOOL_DIR = os.path.join(SCRIPT_DIR, 'debug_tool') +sys.path.append(DEBUG_TOOL_DIR) + +from debug_tool.utils.config_reader import ConfigReader +from simple_tab import SimpleTab +from advanced_tab import AdvancedTab + +class VRSceneCreatorGUI(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("Immersive VR Scene Creator") + self.setGeometry(100, 100, 1600, 800) - if upload_depth_path: - print(f"Uploaded depth map: {upload_depth_path}") - #TODO implement Mona's monodepth upload - else: - print("No depth map selected") - check.set(1) # if no depth map selected, enable auto generation of depth map - depth_check.deselect() # uncheck the depth map check box - else: - check.set(1) # if manually upload unchecked, enable auto generation of depth map - upload_depth_path = None - print("Removed uploaded depth map") + # Initialize paths + self.SCRIPT_DIR = SCRIPT_DIR + self.ROOT_DIR = ROOT_DIR + self.DEBUG_TOOL_DIR = DEBUG_TOOL_DIR -def stanfordRoom_selection(): - if checkStanford.get() == 1: - global stanford_frame - stanford_frame = tk.Frame(window) - stanford_frame.pack(fill=tk.X, padx=5, pady=5) - global labelRoomArea - labelRoomArea = tk.Label(stanford_frame, text="Please Input Room Area: ") - labelRoomArea.pack(side="left") - global stanford_text - stanford_text = tk.Entry(stanford_frame) - stanford_text.pack(side="left", fill=tk.X, expand=True) - else: - stanford_frame.pack_forget() - - select_button.pack(side="top", fill=tk.X, expand=True, padx=5, pady=5) - run_button.pack(side="top", fill=tk.X, expand=True, padx=5, pady=5) - -def run_Image(event): - if checkStanford.get() == 0: - label.configure(text="Pipeline is running. Creating scene...", height=15) - else: - label.configure(text="Pipeline is running for Stanford2D3D dataset. Creating scene...", height=15) - labelRoomArea.configure(text="Room Area Running : ") - stanford_text.configure(state="disabled") - - select_button.pack_forget() - run_button.pack_forget() - depth_check.pack_forget() - include_top_check.pack_forget() - stanford_check.pack_forget() - shift_image_check.pack_forget() - threading() - -def runProcess(): - global file_path - include_top_option = "y" if include_top.get() == 1 else "" - shift_image_option = "y" if shift_image.get() == 1 else "" - - try: - if checkStanford.get() == 0: - combined_bat = os.path.join(SCRIPT_DIR, "combined.bat") - print(f"Attempting to run: {combined_bat}") - print(f"With arguments: {file_path}, {str(check.get())}, {include_top_option}, {shift_image_option}") - - # depth map check - if check.get() == 1: - print("Auto depth map") - else: - print("Manual depth map") - - p = subprocess.Popen( - [combined_bat, file_path, str(check.get()), include_top_option, shift_image_option], - stdout=sys.stdout) - p.communicate() - - else: - temp = os.path.split(file_path) - suffices = temp[-1].split("_") - camera_pos = str(suffices[1]) - room_name = suffices[2] + "_" + suffices[3] - room_area = stanford_text.get() - - print(room_area, room_name, camera_pos) - combined_stanford_bat = os.path.join(SCRIPT_DIR, "combined_stanford.bat") - p = subprocess.Popen( - [combined_stanford_bat, file_path, camera_pos, str(room_area), room_name], - stdout=sys.stdout) - p.communicate() - - copy_intermediary_outputs() - label.configure(text="Pipeline execution complete, check output folder.") + # Read configuration + self.config_reader = ConfigReader(self.DEBUG_TOOL_DIR, self.ROOT_DIR) + + # Setup UI + self.setup_ui() - except Exception as e: - print(f"An error occurred: {e}") - label.configure(text=f"An error occurred: {e}") - - try: - labelRoomArea.pack_forget() - stanford_text.pack_forget() - except Exception as e: - print(e) - -def threading(): - thread1 = Thread(target=runProcess) - thread1.start() - -window = tk.Tk() -window.title("Immersive VR scene creator") - -check = tk.IntVar() -check.set(1) #automatically generate depth map as default -uploadDepthCheck = tk.IntVar() # added uploadDepthCheck vaiable: 0 = automatically upload depth map, 1 = manually upload depth map - -checkStanford = tk.IntVar() -include_top = tk.IntVar() -shift_image = tk.IntVar() -label = tk.Label( - text="Please Input a RGB image for scene creation", - foreground="black", - background="white", - width=50, - height=10, -) - -select_button = tk.Button( - text="Select", - width=50, - height=5, - bg="green", - fg="white", -) - -run_button = tk.Button( - text="Run", - width=50, - height=5, - bg="green", - fg="white", -) -depth_check = tk.Checkbutton(window, text='Upload a depth map(360 MonoDepth)',variable=uploadDepthCheck, onvalue=1, offvalue=0, command=depthmap_creation) -stanford_check = tk.Checkbutton(window, text='Run for stanford2D3D dataset',variable=checkStanford, onvalue=1, offvalue=0,command=stanfordRoom_selection ) -include_top_check = tk.Checkbutton(window, text='Include Top in Mesh', variable=include_top, onvalue=1, offvalue=0) -shift_image_check = tk.Checkbutton(window, text='Shift input image', variable=shift_image, onvalue=1, offvalue=0, command=shift_image_selection) -label.pack() -depth_check.pack() -stanford_check.pack() -include_top_check.pack() -shift_image_check.pack() -select_button.pack() -run_button.pack() + def setup_ui(self): + # Create main widget and layout + main_widget = QWidget() + self.setCentralWidget(main_widget) + layout = QVBoxLayout(main_widget) + + # Add title/header + header_label = QLabel("Immersive VR Scene Creator") + header_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + header_label.setStyleSheet("font-size: 24px; font-weight: bold; margin: 10px;") + layout.addWidget(header_label) + + # Create tab widget + self.tabs = QTabWidget() + layout.addWidget(self.tabs) + + # Initialize tabs + self.simple_tab = SimpleTab(self.config_reader) + self.advanced_tab = AdvancedTab(self.config_reader) + + self.tabs.addTab(self.simple_tab, "Simple Pipeline") + self.tabs.addTab(self.advanced_tab, "Advanced Options") -select_button.bind('<Button-1>', select_Image) -run_button.bind('<Button-1>', run_Image) +def main(): + app = QApplication(sys.argv) + window = VRSceneCreatorGUI() + window.show() + sys.exit(app.exec()) -window.mainloop() \ No newline at end of file +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/advanced_tab.py b/scripts/advanced_tab.py new file mode 100644 index 0000000000000000000000000000000000000000..85617ddda6d7d7060fbcb257791487574458d39f --- /dev/null +++ b/scripts/advanced_tab.py @@ -0,0 +1,97 @@ +from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, + QGroupBox, QScrollArea) +from PyQt6.QtCore import Qt +import os +import sys + +# Import utilities from debug_tool +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 + +class AdvancedTab(QWidget): + def __init__(self, config_reader): + super().__init__() + self.config_reader = config_reader + + # Initialize paths + self.mono_depth_dir = self.config_reader.directories['monoDepthDir'] + self.edge_net_dir = self.config_reader.directories['edgeNetDir'] + self.material_dir = self.config_reader.directories['materialRecogDir'] + + self.setup_ui() + + def setup_ui(self): + # Create main layout + main_layout = QVBoxLayout(self) + + # Create scroll area for module controls + scroll = QScrollArea() + scroll.setWidgetResizable(True) + scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + + # Container for module controls + container = QWidget() + container_layout = QVBoxLayout(container) + + # Add module control groups + self.add_module_controls(container_layout) + + scroll.setWidget(container) + main_layout.addWidget(scroll) + + def add_module_controls(self, layout): + # Status section for overall progress + status_group, self.status_text = create_group_with_text("Pipeline Status", 100) + layout.addWidget(status_group) + + # Image Shifter Controls + shifter_group = QGroupBox("Image Shifter") + shifter_layout = QVBoxLayout(shifter_group) + # Add shifter specific controls + layout.addWidget(shifter_group) + + # Depth Estimation Controls + depth_group = QGroupBox("Depth Estimation") + depth_layout = QVBoxLayout(depth_group) + # Add depth estimation specific controls + layout.addWidget(depth_group) + + # Material Recognition Controls + material_group = QGroupBox("Material Recognition") + material_layout = QVBoxLayout(material_group) + # Add material recognition specific controls + layout.addWidget(material_group) + + # EdgeNet Controls + edgenet_group = QGroupBox("EdgeNet") + edgenet_layout = QVBoxLayout(edgenet_group) + # Add EdgeNet specific controls + layout.addWidget(edgenet_group) + + # Mesh Processing Controls + mesh_group = QGroupBox("Mesh Processing") + mesh_layout = QVBoxLayout(mesh_group) + # Add mesh processing specific controls + layout.addWidget(mesh_group) + + # Preview section + preview_group = QGroupBox("Preview") + preview_layout = QHBoxLayout(preview_group) + + input_group, self.input_preview = create_preview_group("Input") + output_group, self.output_preview = create_preview_group("Output") + + preview_layout.addWidget(input_group) + preview_layout.addWidget(output_group) + layout.addWidget(preview_group) + + # Add some spacing at the bottom + layout.addStretch() + + 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/tabs/edge_net_tab.py b/scripts/debug_tool/tabs/edge_net_tab.py index dc888bed9a226847193e7eecfc4546fc2c1e37dc..1df96d36301af3681d1e7bffa4a868dc89eb3e4e 100644 --- a/scripts/debug_tool/tabs/edge_net_tab.py +++ b/scripts/debug_tool/tabs/edge_net_tab.py @@ -236,8 +236,8 @@ class EdgeNetTab(QWidget): self.current_thread.progress.connect(self.update_status) self.current_thread.start() - def clean_output_directory(self): - if show_confirmation_dialog(self, "Confirm Clean", "Clean output directory?"): + def clean_output_directory(self, silent=False): + if silent or show_confirmation_dialog(self, "Confirm Clean", "Clean output directory?"): if clean_directory(self.output_dir, self.update_status): self.update_status("Output directory cleaned") update_status_indicator(self.split_status, "Not started") diff --git a/scripts/debug_tool/tabs/material_tab.py b/scripts/debug_tool/tabs/material_tab.py index b38111daed8eeae8998acc77a474f9f2c6a22683..d7213229c5109f2b7c0342ffe5f4803b630fb12d 100644 --- a/scripts/debug_tool/tabs/material_tab.py +++ b/scripts/debug_tool/tabs/material_tab.py @@ -1,7 +1,7 @@ # tabs/material_tab.py from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, QMessageBox, QTabWidget) -from PyQt6.QtCore import Qt +from PyQt6.QtCore import Qt, QThread, pyqtSignal import os from utils.qt_widgets import (create_group_with_text, create_button_layout, @@ -13,6 +13,11 @@ from utils.image_handlers import (update_preview, update_face_previews, clear_previews) class MaterialTab(QWidget): + # Add signals for UI updates + status_update = pyqtSignal(str) + progress_update = pyqtSignal(str, str) # (status_name, new_status) + preview_update = pyqtSignal(str, str) # (preview_type, file_path) + def __init__(self, config_reader): super().__init__() self.config_reader = config_reader @@ -26,6 +31,11 @@ class MaterialTab(QWidget): self.setup_ui() self.verify_checkpoint() + + # Connect signals to UI update methods + self.status_update.connect(self._update_status) + self.progress_update.connect(self._update_progress) + self.preview_update.connect(self._update_preview) def setup_ui(self): main_layout = QHBoxLayout(self) @@ -134,12 +144,13 @@ class MaterialTab(QWidget): error_callback=self.update_status) self.reset_status_indicators() - def clean_working_dir(self): + def clean_working_dir(self, silent=False): dirs_to_clean = [self.cubemap_dir, self.material_output_dir] for directory in dirs_to_clean: if not clean_directory(directory, self.update_status): - QMessageBox.critical(self, "Error", f"Failed to clean directory: {directory}") + if not silent: + QMessageBox.critical(self, "Error", f"Failed to clean directory: {directory}") return clear_previews( @@ -152,34 +163,36 @@ class MaterialTab(QWidget): def run_split_360(self): if not self.input_file_path: - QMessageBox.warning(self, "Warning", "Please select an input file first") - return + self.status_update.emit("No input file selected") + return False - self.update_status("Running 360 image splitting...") - update_status_indicator(self.split_status, "Running") + self.status_update.emit("Running 360 image splitting...") + self.progress_update.emit("split", "Running") original_dir = os.getcwd() os.chdir(self.material_recog_dir) cmd = f'''call "{self.config_reader.config["condaDir"]}\\condabin\\activate.bat" {self.config_reader.config["materialEnv"]} && python split_img.py "{self.input_file_path}" && call "{self.config_reader.config["condaDir"]}\\condabin\\deactivate.bat"''' - success, _ = run_command(self, cmd, self.update_status) + success, _ = run_command(None, cmd, lambda msg: self.status_update.emit(msg)) os.chdir(original_dir) if success: - update_status_indicator(self.split_status, "Complete") - self.update_face_previews('rgb') + self.progress_update.emit("split", "Complete") + self.preview_update.emit("rgb", "") # Empty string triggers face preview update + return True else: - update_status_indicator(self.split_status, "Failed") + self.progress_update.emit("split", "Failed") + return False def run_material_recognition(self): if not os.path.exists(self.cubemap_dir): - QMessageBox.warning(self, "Warning", "Please run Split 360 first") - return + self.status_update.emit("Please run Split 360 first") + return False - self.update_status("Running material recognition...") - update_status_indicator(self.recognition_status, "Running") + self.status_update.emit("Running material recognition...") + self.progress_update.emit("recognition", "Running") original_dir = os.getcwd() os.chdir(self.material_recog_dir) @@ -193,50 +206,59 @@ class MaterialTab(QWidget): f'--infer "{self.material_recog_dir}/cubemap_faces/"' ) - success, _ = run_command(self, cmd, self.update_status) + success, _ = run_command(None, cmd, lambda msg: self.status_update.emit(msg)) os.chdir(original_dir) if success: - update_status_indicator(self.recognition_status, "Complete") - self.update_face_previews('material') + self.progress_update.emit("recognition", "Complete") + self.preview_update.emit("material", "") + return True else: - update_status_indicator(self.recognition_status, "Failed") + self.progress_update.emit("recognition", "Failed") + return False def run_combine(self): - self.update_status("Running combine step...") - update_status_indicator(self.combine_status, "Running") + self.status_update.emit("Running combine step...") + self.progress_update.emit("combine", "Running") original_dir = os.getcwd() os.chdir(self.material_recog_dir) cmd = f'''call "{self.config_reader.config["condaDir"]}\\condabin\\activate.bat" {self.config_reader.config["materialEnv"]} && python combine_img.py && call "{self.config_reader.config["condaDir"]}\\condabin\\deactivate.bat"''' - success, _ = run_command(self, cmd, self.update_status) + success, _ = run_command(None, cmd, lambda msg: self.status_update.emit(msg)) os.chdir(original_dir) if success: - update_status_indicator(self.combine_status, "Complete") + self.progress_update.emit("combine", "Complete") output_path = os.path.join( self.config_reader.directories['edgeNetDir'], 'Data', 'Input', 'material.png' ) - update_preview(self.output_preview, output_path, - error_callback=self.update_status) + self.preview_update.emit("output", output_path) + return True else: - update_status_indicator(self.combine_status, "Failed") + self.progress_update.emit("combine", "Failed") + return False def run_all_steps(self): + """Main processing method that can be called from any thread""" if not self.input_file_path: - QMessageBox.warning(self, "Warning", "Please select an input file first") - return + self.status_update.emit("Please select an input file first") + return False + + if not self.run_split_360(): + return False + + if not self.run_material_recognition(): + return False + + if not self.run_combine(): + return False - self.run_split_360() - if get_status_text(self.split_status) == "Complete": - self.run_material_recognition() - if get_status_text(self.recognition_status) == "Complete": - self.run_combine() + return True def update_status(self, message): self.status_text.append(message) @@ -268,4 +290,27 @@ class MaterialTab(QWidget): self.info_labels["Checkpoint:"].setText("✓ Found" if exists else "✗ Missing") self.info_labels["Checkpoint:"].setStyleSheet( "color: green" if exists else "color: red") - return exists \ No newline at end of file + return exists + + # Private UI update methods - only called via signals in main thread + def _update_status(self, message): + self.status_text.append(message) + scrollbar = self.status_text.verticalScrollBar() + scrollbar.setValue(scrollbar.maximum()) + + def _update_progress(self, status_name, new_status): + if status_name == "split": + update_status_indicator(self.split_status, new_status) + elif status_name == "recognition": + update_status_indicator(self.recognition_status, new_status) + elif status_name == "combine": + update_status_indicator(self.combine_status, new_status) + + def _update_preview(self, preview_type, file_path): + if preview_type == "rgb": + self.update_face_previews("rgb") + elif preview_type == "material": + self.update_face_previews("material") + elif preview_type == "output": + update_preview(self.output_preview, file_path, + error_callback=lambda msg: self.status_update.emit(msg)) \ No newline at end of file diff --git a/scripts/debug_tool/utils/config_reader.py b/scripts/debug_tool/utils/config_reader.py index 6101e9127f52cf234a1cba1cbd1246122b76af44..7c2aa77dce363ece6d95a53f530dcab5013ca681 100644 --- a/scripts/debug_tool/utils/config_reader.py +++ b/scripts/debug_tool/utils/config_reader.py @@ -13,17 +13,28 @@ class ConfigReader: def read_config(self): config = {} - # Use config.ini from scripts directory + # First try normal path config_path = os.path.join(self.PIPELINE_DIR, "config.ini") + try: with open(config_path, 'r') as f: for line in f: if '=' in line: key, value = line.strip().split('=', 1) config[key.strip()] = value.strip() + return config except FileNotFoundError: - raise Exception(f"config.ini not found in {self.PIPELINE_DIR}") - return config + # If not found, try scripts directory + scripts_config = os.path.join(self.ROOT_DIR, "scripts", "config.ini") + try: + with open(scripts_config, 'r') as f: + for line in f: + if '=' in line: + key, value = line.strip().split('=', 1) + config[key.strip()] = value.strip() + return config + except FileNotFoundError: + raise Exception(f"config.ini not found in {self.PIPELINE_DIR} or {os.path.dirname(scripts_config)}") def setup_directories(self): return { diff --git a/scripts/old_GUI.py b/scripts/old_GUI.py new file mode 100644 index 0000000000000000000000000000000000000000..bf8aecdd96476f2eee85522eb57d51f846253f8f --- /dev/null +++ b/scripts/old_GUI.py @@ -0,0 +1,196 @@ +import tkinter as tk +import tkinter.filedialog +import subprocess +import sys +import time +from threading import Thread +import shutil +import os + +# Get the directory of the current script +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +# Get the root directory (AVVR-Pipeline-Internship) +ROOT_DIR = os.path.dirname(SCRIPT_DIR) + +file_path = None +createDepth = "0" + +def shift_image_selection(): + # This function can be used if you want to perform any action when the checkbox is clicked + pass + +def copy_intermediary_outputs(): + source_folder = os.path.join(ROOT_DIR, "edgenet-360", "Data", "Input") + destination_folder = os.path.join(ROOT_DIR, "edgenet-360", "Output") + files_to_copy = ["depth_e.png", "enhanced_depth_e.png", "material.png", "rgb.png"] + + for file_name in files_to_copy: + source_path = os.path.join(source_folder, file_name) + destination_path = os.path.join(destination_folder, file_name) + try: + shutil.copy(source_path, destination_path) + print(f"Copied {file_name} to {destination_folder}") + except FileNotFoundError: + print(f"Warning: {file_name} not found in {source_folder}") + +def select_Image(event): + global file_path + file_path = tkinter.filedialog.askopenfilename() + file_path = os.path.normpath(file_path) + select_button.configure(text="Selected", bg="red") + label.configure(text="Image is selected. Press run to create scene.") + +def depthmap_creation(): + print("Manually upload depth map: ", uploadDepthCheck.get()) + + if uploadDepthCheck.get() == 1: # if manually upload checked + check.set(0) # disable auto generation of depth map + upload_depth_path = tkinter.filedialog.askopenfilename(title="Select a depth map", filetypes=[("PNG files", "*.png")]) #dialog box to upload depth map + + if upload_depth_path: + print(f"Uploaded depth map: {upload_depth_path}") + #TODO implement Mona's monodepth upload + else: + print("No depth map selected") + check.set(1) # if no depth map selected, enable auto generation of depth map + depth_check.deselect() # uncheck the depth map check box + else: + check.set(1) # if manually upload unchecked, enable auto generation of depth map + upload_depth_path = None + print("Removed uploaded depth map") + +def stanfordRoom_selection(): + if checkStanford.get() == 1: + global stanford_frame + stanford_frame = tk.Frame(window) + stanford_frame.pack(fill=tk.X, padx=5, pady=5) + global labelRoomArea + labelRoomArea = tk.Label(stanford_frame, text="Please Input Room Area: ") + labelRoomArea.pack(side="left") + global stanford_text + stanford_text = tk.Entry(stanford_frame) + stanford_text.pack(side="left", fill=tk.X, expand=True) + else: + stanford_frame.pack_forget() + + select_button.pack(side="top", fill=tk.X, expand=True, padx=5, pady=5) + run_button.pack(side="top", fill=tk.X, expand=True, padx=5, pady=5) + +def run_Image(event): + if checkStanford.get() == 0: + label.configure(text="Pipeline is running. Creating scene...", height=15) + else: + label.configure(text="Pipeline is running for Stanford2D3D dataset. Creating scene...", height=15) + labelRoomArea.configure(text="Room Area Running : ") + stanford_text.configure(state="disabled") + + select_button.pack_forget() + run_button.pack_forget() + depth_check.pack_forget() + include_top_check.pack_forget() + stanford_check.pack_forget() + shift_image_check.pack_forget() + threading() + +def runProcess(): + global file_path + include_top_option = "y" if include_top.get() == 1 else "" + shift_image_option = "y" if shift_image.get() == 1 else "" + + try: + if checkStanford.get() == 0: + combined_bat = os.path.join(SCRIPT_DIR, "combined.bat") + print(f"Attempting to run: {combined_bat}") + print(f"With arguments: {file_path}, {str(check.get())}, {include_top_option}, {shift_image_option}") + + # depth map check + if check.get() == 1: + print("Auto depth map") + else: + print("Manual depth map") + + p = subprocess.Popen( + [combined_bat, file_path, str(check.get()), include_top_option, shift_image_option], + stdout=sys.stdout) + p.communicate() + + else: + temp = os.path.split(file_path) + suffices = temp[-1].split("_") + camera_pos = str(suffices[1]) + room_name = suffices[2] + "_" + suffices[3] + room_area = stanford_text.get() + + print(room_area, room_name, camera_pos) + combined_stanford_bat = os.path.join(SCRIPT_DIR, "combined_stanford.bat") + p = subprocess.Popen( + [combined_stanford_bat, file_path, camera_pos, str(room_area), room_name], + stdout=sys.stdout) + p.communicate() + + copy_intermediary_outputs() + label.configure(text="Pipeline execution complete, check output folder.") + + except Exception as e: + print(f"An error occurred: {e}") + label.configure(text=f"An error occurred: {e}") + + try: + labelRoomArea.pack_forget() + stanford_text.pack_forget() + except Exception as e: + print(e) + +def threading(): + thread1 = Thread(target=runProcess) + thread1.start() + +window = tk.Tk() +window.title("Immersive VR scene creator") + +check = tk.IntVar() +check.set(1) #automatically generate depth map as default +uploadDepthCheck = tk.IntVar() # added uploadDepthCheck vaiable: 0 = automatically upload depth map, 1 = manually upload depth map + +checkStanford = tk.IntVar() +include_top = tk.IntVar() +shift_image = tk.IntVar() +label = tk.Label( + text="Please Input a RGB image for scene creation", + foreground="black", + background="white", + width=50, + height=10, +) + +select_button = tk.Button( + text="Select", + width=50, + height=5, + bg="green", + fg="white", +) + +run_button = tk.Button( + text="Run", + width=50, + height=5, + bg="green", + fg="white", +) +depth_check = tk.Checkbutton(window, text='Upload a depth map(360 MonoDepth)',variable=uploadDepthCheck, onvalue=1, offvalue=0, command=depthmap_creation) +stanford_check = tk.Checkbutton(window, text='Run for stanford2D3D dataset',variable=checkStanford, onvalue=1, offvalue=0,command=stanfordRoom_selection ) +include_top_check = tk.Checkbutton(window, text='Include Top in Mesh', variable=include_top, onvalue=1, offvalue=0) +shift_image_check = tk.Checkbutton(window, text='Shift input image', variable=shift_image, onvalue=1, offvalue=0, command=shift_image_selection) +label.pack() +depth_check.pack() +stanford_check.pack() +include_top_check.pack() +shift_image_check.pack() +select_button.pack() +run_button.pack() + +select_button.bind('<Button-1>', select_Image) +run_button.bind('<Button-1>', run_Image) + +window.mainloop() \ No newline at end of file diff --git a/scripts/simple_tab.py b/scripts/simple_tab.py new file mode 100644 index 0000000000000000000000000000000000000000..a80dae8cb91a799c6aa706b9f5ad3b4a02fc18da --- /dev/null +++ b/scripts/simple_tab.py @@ -0,0 +1,285 @@ +from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, + QGroupBox, QCheckBox, QMessageBox, QPushButton, + QProgressBar) +from PyQt6.QtCore import Qt, QThread, pyqtSignal +import os +import sys + +# Import utilities from debug_tool +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 + +# Import existing module implementations +from debug_tool.tabs.shifter_tab import ShifterTab +from debug_tool.tabs.depth_tab import DepthTab +from debug_tool.tabs.material_tab import MaterialTab +from debug_tool.tabs.edge_net_tab import EdgeNetTab + +class PipelineWorker(QThread): + progress = pyqtSignal(str) + finished = pyqtSignal(bool, str) + + def __init__(self, tab_instance): + super().__init__() + self.tab = tab_instance + + def run(self): + try: + self.run_pipeline() + self.finished.emit(True, "Pipeline completed successfully!") + except Exception as e: + print(f"Pipeline failed with error: {str(e)}") # Debug print + self.finished.emit(False, f"Pipeline failed: {str(e)}") + + def clean_temp_files(self): + self.tab.depth.clean_input_dir() # This one doesn't have dialogs + self.progress.emit("Cleaning input directory...") + self.tab.depth.remove_rgb() + self.progress.emit("Removing RGB image from 360monodepthexecution...") + self.tab.material.clean_working_dir(silent=True) + self.progress.emit("Cleaning Dynamic-Backward-Attention-Transformer temp directory...") + self.tab.edge_net.clean_output_directory(silent=True) + self.progress.emit("Cleaning EdgeNet output directory...") + + def copy_file(self): + self.progress.emit("Copying input file to scripts/360monodepthexecution...") + self.tab.depth.copy_file() + + def shift_image(self): + print("Starting shift_image") # Debug print + if not self.tab.shift_image_check.isChecked(): + self.progress.emit("Skipping image shift...") + return + self.progress.emit("Shifting input image...") + self.tab.shifter.run_shifter() + print("Completed shift_image") # Debug print + + def run_depth_estimation(self): + print("Starting depth_estimation") # Debug print + self.progress.emit("Running depth estimation...") + self.tab.depth.run_depth_estimation() + print("Completed depth_estimation") # Debug print + + def run_material_recognition(self): + print("Starting material_recognition") + self.progress.emit("Running material recognition...") + + try: + print(f"Input file path: {self.tab.material.input_file_path}") + + print("Running split 360...") + success = self.tab.material.run_split_360() + if not success: + raise Exception("Split 360 failed") + + print("Running material recognition...") + success = self.tab.material.run_material_recognition() + if not success: + raise Exception("Material recognition step failed") + + print("Starting combine step...") + print(f"Current working directory: {os.getcwd()}") + print(f"Material recognition directory: {self.tab.material.material_recog_dir}") + print(f"Checking if cubemap directory exists: {os.path.exists(self.tab.material.cubemap_dir)}") + print(f"Checking if material output directory exists: {os.path.exists(self.tab.material.material_output_dir)}") + print("Files in cubemap directory:") + if os.path.exists(self.tab.material.cubemap_dir): + print("\n".join(os.listdir(self.tab.material.cubemap_dir))) + print("Files in material output directory:") + if os.path.exists(self.tab.material.material_output_dir): + print("\n".join(os.listdir(self.tab.material.material_output_dir))) + + success = self.tab.material.run_combine() + if not success: + raise Exception("Combine step failed") + + except Exception as e: + print(f"Material recognition error: {str(e)}") + raise + + print("Completed material_recognition") + + def run_edge_net(self): + print("Starting edge_net") + self.progress.emit("Running EdgeNet enhance360.py and infer360.py...") + + try: + self.tab.edge_net.include_top = self.tab.include_top_check.isChecked() + self.tab.edge_net._run_edge_net_process() + print("Completed edge_net") + except Exception as e: + print(f"EdgeNet failed: {str(e)}") + raise + + def run_post_processing(self): + self.progress.emit("Running post-processing...") + self.tab.edge_net._run_mesh_split_process() + self.progress.emit("Completed mesh split (replace.py)") + self.tab.edge_net._run_blender_flip_process() + self.progress.emit("Completed blender flip (blenderFlip.py)") + self.progress.emit("Post-processing completed!") + + def run_pipeline(self): + self.clean_temp_files() + self.copy_file() + self.shift_image() + self.run_depth_estimation() + self.run_material_recognition() + self.run_edge_net() + self.run_post_processing() + self.progress.emit("Pipeline completed!") + +class SimpleTab(QWidget): + def __init__(self, config_reader): + super().__init__() + self.config_reader = config_reader + self.input_path = None + self.pipeline_thread = None + + # Initialize module instances + self.shifter = ShifterTab(self.config_reader) + self.depth = DepthTab(self.config_reader) + self.material = MaterialTab(self.config_reader) + self.edge_net = EdgeNetTab(self.config_reader) + + # Hide their UIs as we'll use our own + self.shifter.hide() + self.depth.hide() + self.material.hide() + self.edge_net.hide() + + self.setup_ui() + + def setup_ui(self): + layout = QVBoxLayout(self) + + # Controls section + controls_group = QGroupBox("Pipeline Controls") + controls_layout = QVBoxLayout(controls_group) + + # Info display + info_rows = [ + ("Input Image:", "No file selected"), + ("Status:", "Ready") + ] + self.info_group, self.info_labels = create_info_group("Information", info_rows) + controls_layout.addWidget(self.info_group) + + # Options + options_layout = QHBoxLayout() + + self.include_top_check = QCheckBox("Include Top in Mesh") + options_layout.addWidget(self.include_top_check) + + self.shift_image_check = QCheckBox("Shift Input Image") + options_layout.addWidget(self.shift_image_check) + + controls_layout.addLayout(options_layout) + + # Progress Bar + self.progress_bar = QProgressBar() + self.progress_bar.setMinimum(0) + self.progress_bar.setMaximum(0) # Makes it an indefinite progress bar + self.progress_bar.hide() # Hidden by default + controls_layout.addWidget(self.progress_bar) + + # Buttons + self.run_pipeline_btn = QPushButton("Run Pipeline") + self.run_pipeline_btn.clicked.connect(self.run_full_pipeline) + self.run_pipeline_btn.setEnabled(False) # Disabled by default + + buttons_layout = QHBoxLayout() + select_btn = QPushButton("Select Input Image") + select_btn.clicked.connect(self.handle_file_select) + + buttons_layout.addWidget(select_btn) + buttons_layout.addWidget(self.run_pipeline_btn) + controls_layout.addLayout(buttons_layout) + + layout.addWidget(controls_group) + + # Status section + status_group, self.status_text = create_group_with_text("Pipeline Status", 150) + layout.addWidget(status_group) + + # Preview section + preview_group = QGroupBox("Preview") + preview_layout = QHBoxLayout(preview_group) + + input_group, self.input_preview = create_preview_group("Input Image") + output_group, self.output_preview = create_preview_group("Current Output") + + preview_layout.addWidget(input_group) + preview_layout.addWidget(output_group) + layout.addWidget(preview_group) + + def handle_file_select(self): + + file_path = select_file( + self, + "Select Input Image", + "Images (*.png *.jpg *.jpeg)", + initial_dir=self.config_reader.directories['edgeNetDir'] + '/Data' + ) + + if file_path: + self.input_path = file_path + self.info_labels["Input Image:"].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) + + # Enable the run pipeline button + self.run_pipeline_btn.setEnabled(True) + + # Provide input path to all modules + self.shifter.input_file_path = file_path + self.depth.depth_input_path = file_path + self.material.input_file_path = file_path + # self.edge_net.input_path = file_path # edgenet have default input path + + def run_full_pipeline(self): + if not self.input_path: + QMessageBox.warning(self, "Warning", "Please select an input file first") + return + + if self.pipeline_thread and self.pipeline_thread.isRunning(): + QMessageBox.warning(self, "Warning", "Pipeline is already running") + return + + # Show progress bar and update status + self.progress_bar.show() + self.run_pipeline_btn.setEnabled(False) + + self.pipeline_thread = PipelineWorker(self) + + # Connect signals + self.pipeline_thread.progress.connect(self.update_status) + self.pipeline_thread.finished.connect(self.pipeline_completed) + + # Disable controls while running + self.setEnabled(False) + self.progress_bar.setEnabled(True) # Keep progress bar enabled + + # Start the pipeline + self.pipeline_thread.start() + + def pipeline_completed(self, success, message): + self.setEnabled(True) + self.progress_bar.hide() + self.run_pipeline_btn.setEnabled(True) + self.update_status(message) + + if success: + QMessageBox.information(self, "Success", "Pipeline completed successfully!") + else: + QMessageBox.critical(self, "Error", f"Pipeline failed: {message}") + + def update_status(self, message): + self.status_text.append(message) + self.info_labels["Status:"].setText(message.split("...")[-1] if "..." in message else message) + # Scroll to bottom + scrollbar = self.status_text.verticalScrollBar() + scrollbar.setValue(scrollbar.maximum()) \ No newline at end of file