diff --git a/scripts/debug_tool/GUI_debug.py b/scripts/debug_tool/GUI_debug.py index 3ba5252e05e76ae06e9a75a142d82b04f20debb4..38938de4a8546a4b50f1f078c474b318b973352f 100644 --- a/scripts/debug_tool/GUI_debug.py +++ b/scripts/debug_tool/GUI_debug.py @@ -6,6 +6,7 @@ from tabs.config_tab import ConfigTab from tabs.shifter_tab import ShifterTab from tabs.depth_tab import DepthTab from tabs.material_tab import MaterialTab +from tabs.edge_net_tab import EdgeNetTab from utils.config_reader import ConfigReader class ModuleDebugGUI: @@ -31,6 +32,8 @@ class ModuleDebugGUI: self.shifter_tab = ShifterTab(self.notebook, self.config_reader) self.depth_tab = DepthTab(self.notebook, self.config_reader) self.material_tab = MaterialTab(self.notebook, self.config_reader) + self.edge_net_tab = EdgeNetTab(self.notebook, self.config_reader) + def run(self): self.window.mainloop() diff --git a/scripts/debug_tool/tabs/edge_net_tab.py b/scripts/debug_tool/tabs/edge_net_tab.py new file mode 100644 index 0000000000000000000000000000000000000000..8331e2eb05c0c2dc17df59311215b39c829c7a19 --- /dev/null +++ b/scripts/debug_tool/tabs/edge_net_tab.py @@ -0,0 +1,581 @@ + +import tkinter as tk +from tkinter import ttk, messagebox, filedialog +import os +from PIL import Image, ImageTk +import subprocess +import shutil + +class EdgeNetTab: + def __init__(self, notebook, config_reader): + self.tab = ttk.Frame(notebook) + notebook.add(self.tab, text='EdgeNet Processing') + + self.config_reader = config_reader + self.edge_net_dir = self.config_reader.directories['edgeNetDir'] + self.output_dir = self.config_reader.directories['outputDir'] + + # Updated default input directory + self.input_dir = os.path.join(self.edge_net_dir, "Data", "Input") + + # Option for including top + self.include_top = tk.BooleanVar(value=False) + + # Dictionary to store manual input paths + self.manual_inputs = { + 'depth_e.png': None, + 'rgb.png': None, + 'material.png': None + } + + 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) + + 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) + + # File input section + file_frame = ttk.LabelFrame(control_frame, text="Input Files", padding="5") + file_frame.pack(fill=tk.X, pady=5) + + # Enhance360 inputs + enhance_frame = ttk.LabelFrame(file_frame, text="enhance360.py inputs", padding="5") + enhance_frame.pack(fill=tk.X, pady=2) + + # depth_e.png input + depth_frame = ttk.Frame(enhance_frame) + depth_frame.pack(fill=tk.X, pady=2) + ttk.Label(depth_frame, text="depth_e.png:").pack(side=tk.LEFT, padx=5) + self.depth_label = ttk.Label(depth_frame, text="Using default") + self.depth_label.pack(side=tk.LEFT, padx=5) + ttk.Button( + depth_frame, + text="Select", + command=lambda: self.select_input_file("depth_e.png") + ).pack(side=tk.RIGHT, padx=5) + + # rgb.png input + rgb_frame = ttk.Frame(enhance_frame) + rgb_frame.pack(fill=tk.X, pady=2) + ttk.Label(rgb_frame, text="rgb.png:").pack(side=tk.LEFT, padx=5) + self.rgb_label = ttk.Label(rgb_frame, text="Using default") + self.rgb_label.pack(side=tk.LEFT, padx=5) + ttk.Button( + rgb_frame, + text="Select", + command=lambda: self.select_input_file("rgb.png") + ).pack(side=tk.RIGHT, padx=5) + + # Infer360 inputs + infer_frame = ttk.LabelFrame(file_frame, text="infer360.py inputs", padding="5") + infer_frame.pack(fill=tk.X, pady=2) + + # material.png input + material_frame = ttk.Frame(infer_frame) + material_frame.pack(fill=tk.X, pady=2) + ttk.Label(material_frame, text="material.png:").pack(side=tk.LEFT, padx=5) + self.material_label = ttk.Label(material_frame, text="Using default") + self.material_label.pack(side=tk.LEFT, padx=5) + ttk.Button( + material_frame, + text="Select", + command=lambda: self.select_input_file("material.png") + ).pack(side=tk.RIGHT, padx=5) + + # Reset inputs button + ttk.Button( + file_frame, + text="Reset to Default Inputs", + command=self.reset_inputs + ).pack(pady=5) + + # Include top checkbox + ttk.Checkbutton( + control_frame, + text="Include Top in Mesh", + variable=self.include_top + ).pack(anchor=tk.W, padx=5, pady=5) + + # Progress indicators + self.progress_frame = ttk.LabelFrame(control_frame, text="Progress", padding="5") + self.progress_frame.pack(fill=tk.X, pady=5) + + # EdgeNet progress + edge_frame = ttk.Frame(self.progress_frame) + edge_frame.pack(fill=tk.X, pady=2) + ttk.Label(edge_frame, text="EdgeNet:").pack(side=tk.LEFT, padx=5) + self.edge_status = ttk.Label(edge_frame, text="Not started") + self.edge_status.pack(side=tk.LEFT, padx=5) + + # Mesh splitting progress + split_frame = ttk.Frame(self.progress_frame) + split_frame.pack(fill=tk.X, pady=2) + ttk.Label(split_frame, text="Mesh Splitting:").pack(side=tk.LEFT, padx=5) + self.split_status = ttk.Label(split_frame, text="Not started") + self.split_status.pack(side=tk.LEFT, padx=5) + + # Blender flip progress + flip_frame = ttk.Frame(self.progress_frame) + flip_frame.pack(fill=tk.X, pady=2) + ttk.Label(flip_frame, text="Blender Flip:").pack(side=tk.LEFT, padx=5) + self.flip_status = ttk.Label(flip_frame, text="Not started") + self.flip_status.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="Run EdgeNet", + command=self.run_edge_net + ).pack(side=tk.LEFT, padx=5) + + ttk.Button( + button_frame, + text="Run Mesh Split", + command=self.run_mesh_split + ).pack(side=tk.LEFT, padx=5) + + ttk.Button( + button_frame, + text="Run Blender Flip", + command=self.run_blender_flip + ).pack(side=tk.LEFT, padx=5) + + ttk.Button( + button_frame, + text="Run All Steps", + command=self.run_all_steps + ).pack(side=tk.LEFT, padx=5) + + ttk.Button( + button_frame, + text="Clean Temp Files", + command=self.clean_temp_files + ).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_preview_panel(self, parent): + preview_frame = ttk.LabelFrame(parent, text="Process Previews", padding="5") + preview_frame.pack(fill=tk.BOTH, expand=True) + + # Enhanced depth preview + depth_frame = ttk.LabelFrame(preview_frame, text="Enhanced Depth") + depth_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + self.depth_preview = ttk.Label(depth_frame) + self.depth_preview.pack(padx=5, pady=5) + + # Mesh preview + mesh_frame = ttk.LabelFrame(preview_frame, text="Generated Mesh") + mesh_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + self.mesh_preview = ttk.Label(mesh_frame) + self.mesh_preview.pack(padx=5, pady=5) + + def select_input_file(self, file_type): + """Select a manual input file""" + filepath = filedialog.askopenfilename( + filetypes=[("PNG files", "*.png")] + ) + if filepath: + self.manual_inputs[file_type] = os.path.normpath(filepath) + # Update corresponding label + if file_type == "depth_e.png": + self.depth_label.config(text=os.path.basename(filepath)) + elif file_type == "rgb.png": + self.rgb_label.config(text=os.path.basename(filepath)) + elif file_type == "material.png": + self.material_label.config(text=os.path.basename(filepath)) + + self.update_status(f"Selected {file_type}: {filepath}") + + def reset_inputs(self): + """Reset all inputs to default""" + self.manual_inputs = { + 'depth_e.png': None, + 'rgb.png': None, + 'material.png': None + } + self.depth_label.config(text="Using default") + self.rgb_label.config(text="Using default") + self.material_label.config(text="Using default") + self.update_status("Reset all inputs to default") + + def get_input_path(self, file_type): + """Get the path for an input file, considering manual inputs""" + if self.manual_inputs[file_type]: + return self.manual_inputs[file_type] + return os.path.join(self.input_dir, file_type) # Using updated input_dir + + def copy_input_files(self): + """Copy manual input files to Input directory if needed""" + os.makedirs(self.input_dir, exist_ok=True) + + for file_type, manual_path in self.manual_inputs.items(): + if manual_path: + dest_path = os.path.join(self.input_dir, file_type) + try: + shutil.copy2(manual_path, dest_path) + self.update_status(f"Copied {file_type} to Input directory") + except Exception as e: + self.update_status(f"Error copying {file_type}: {str(e)}") + + def verify_enhance360_inputs(self): + """Verify input files for enhance360.py""" + required_files = { + 'depth_e.png': self.get_input_path('depth_e.png'), + 'rgb.png': self.get_input_path('rgb.png') + } + + self.update_status("Checking input files in: " + self.input_dir) + + missing_files = [] + for name, path in required_files.items(): + if not os.path.exists(path): + missing_files.append(f"{name} ({path})") + + if missing_files: + self.update_status("Missing required files for enhance360.py:") + for file in missing_files: + self.update_status(f"- {file}") + return False + + self.update_status("All required files present for enhance360.py") + return True + + def verify_infer360_inputs(self): + """Verify input files for infer360.py""" + required_files = { + 'enhanced_depth_e.png': os.path.join(self.input_dir, "enhanced_depth_e.png"), + 'material.png': os.path.join(self.input_dir, "material.png"), + 'rgb.png': os.path.join(self.input_dir, "rgb.png") + } + + missing_files = [] + for name, path in required_files.items(): + if not os.path.exists(path): + missing_files.append(f"{name} ({path})") + + if missing_files: + self.update_status("Missing required files for infer360.py:") + for file in missing_files: + self.update_status(f"- {file}") + return False + + self.update_status("All required files present for infer360.py") + return True + + def verify_mesh_split_inputs(self): + """Verify input files for mesh splitting""" + obj_file = os.path.join(self.edge_net_dir, "Input", "input_prediction.obj") + mtl_file = os.path.join(self.edge_net_dir, "Input", "input_prediction.mtl") + + missing_files = [] + if not os.path.exists(obj_file): + missing_files.append("input_prediction.obj") + if not os.path.exists(mtl_file): + missing_files.append("input_prediction.mtl") + + if missing_files: + self.update_status("Missing required files for mesh splitting:") + for file in missing_files: + self.update_status(f"- {file}") + return False + + self.update_status("All required files present for mesh splitting") + return True + + def verify_blender_flip_inputs(self): + """Verify input files for Blender flip""" + obj_file = os.path.join(self.output_dir, "Input_prediction_mesh.obj") + mtl_file = os.path.join(self.output_dir, "Input_prediction_mesh.mtl") + + missing_files = [] + if not os.path.exists(obj_file): + missing_files.append("Input_prediction_mesh.obj") + if not os.path.exists(mtl_file): + missing_files.append("Input_prediction_mesh.mtl") + + if missing_files: + self.update_status("Missing required files for Blender flip:") + for file in missing_files: + self.update_status(f"- {file}") + return False + + self.update_status("All required files present for Blender flip") + return True + + def run_edge_net(self): + try: + # Verify input files first + if not self.verify_enhance360_inputs(): + # Try to copy manual inputs if verification failed + self.update_status("Attempting to copy manual inputs...") + self.copy_input_files() + + # Verify again after copying + if not self.verify_enhance360_inputs(): + raise Exception("Missing required input files for enhance360.py") + + self.update_status("Running EdgeNet...") + self.edge_status.config(text="Running...", foreground="orange") + + # Change to EdgeNet directory + original_dir = os.getcwd() + os.chdir(self.edge_net_dir) + + # Run enhance360.py + self.update_status("Running enhance360.py...") + enhance_cmd = (f'wsl bash -c "source {self.config_reader.config["wslAnacondaDir"]}/activate' + f' {self.config_reader.config["edgeNetEnv"]} && ' + f'python enhance360.py Input depth_e.png rgb.png enhanced_depth_e.png"') + + self.update_status(f"Executing command:\n{enhance_cmd}") + + process = subprocess.Popen( + enhance_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True, + text=True + ) + stdout, stderr = process.communicate() + + if stdout: + self.update_status("enhance360.py output:\n" + stdout) + if stderr: + self.update_status("enhance360.py error:\n" + stderr) + + if process.returncode != 0: + raise Exception(f"enhance360.py failed: {stderr}") + + # Verify files for infer360.py + if not self.verify_infer360_inputs(): + raise Exception("Missing required input files for infer360.py") + + # Run infer360.py + self.update_status("Running infer360.py...") + include_top_flag = "--include_top y" if self.include_top.get() else "" + infer_cmd = (f'wsl bash -c "source {self.config_reader.config["wslAnacondaDir"]}/activate' + f' {self.config_reader.config["edgeNetEnv"]} && ' + f'python infer360.py Input enhanced_depth_e.png material.png rgb.png Input {include_top_flag}"') + + self.update_status(f"Executing command:\n{infer_cmd}") + + process = subprocess.Popen( + infer_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True, + text=True + ) + stdout, stderr = process.communicate() + + if stdout: + self.update_status("infer360.py output:\n" + stdout) + if stderr: + self.update_status("infer360.py error:\n" + stderr) + + if process.returncode != 0: + raise Exception(f"infer360.py failed: {stderr}") + + self.update_status("EdgeNet processing completed successfully") + self.edge_status.config(text="✓ Complete", foreground="green") + self.update_depth_preview() + + except Exception as e: + self.update_status(f"Error in EdgeNet: {str(e)}") + self.edge_status.config(text="✗ Failed", foreground="red") + messagebox.showerror("Error", f"EdgeNet failed: {str(e)}") + finally: + if original_dir: + os.chdir(original_dir) + + # Deactivate WSL environment + subprocess.run('wsl bash -c "conda deactivate"', shell=True) + + def run_mesh_split(self): + try: + # Verify input files first + if not self.verify_mesh_split_inputs(): + raise Exception("Missing required input files for mesh splitting") + + self.update_status("Running mesh splitting...") + self.split_status.config(text="Running...", foreground="orange") + + original_dir = os.getcwd() + os.chdir(self.edge_net_dir) + + cmd = f'call "{self.config_reader.config["condaDir"]}\\condabin\\activate.bat" {self.config_reader.config["materialEnv"]} && python "{self.edge_net_dir}\\replace.py"' + + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True, + text=True + ) + stdout, stderr = process.communicate() + + if process.returncode != 0: + raise Exception(f"Mesh splitting failed: {stderr}") + + # Verify output files were created + output_obj = os.path.join(self.output_dir, "Input_prediction_mesh.obj") + output_mtl = os.path.join(self.output_dir, "Input_prediction_mesh.mtl") + + if not (os.path.exists(output_obj) and os.path.exists(output_mtl)): + raise Exception("Mesh splitting did not generate expected output files") + + self.update_status("Mesh splitting completed successfully") + self.split_status.config(text="✓ Complete", foreground="green") + + except Exception as e: + self.update_status(f"Error in mesh splitting: {str(e)}") + self.split_status.config(text="✗ Failed", foreground="red") + messagebox.showerror("Error", f"Mesh splitting failed: {str(e)}") + finally: + if original_dir: + os.chdir(original_dir) + + def run_blender_flip(self): + try: + # Verify input files first + if not self.verify_blender_flip_inputs(): + raise Exception("Missing required input files for Blender flip") + + self.update_status("Running Blender flip...") + self.flip_status.config(text="Running...", foreground="orange") + + original_dir = os.getcwd() + os.chdir(self.edge_net_dir) + + cmd = (f'call "{self.config_reader.config["condaDir"]}\\condabin\\activate.bat" ' + f'{self.config_reader.config["unityEnv"]} && ' + f'python "{os.path.join(self.config_reader.directories["scriptDir"], "blenderFlip.py")}" ' + f'"{os.path.join(self.output_dir, "Input_prediction_mesh.obj")}"') + + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True, + text=True + ) + stdout, stderr = process.communicate() + + if process.returncode != 0: + raise Exception(f"Blender flip failed: {stderr}") + + # Verify output files were created + output_obj = os.path.join(self.output_dir, "final_output_scene_mesh.obj") + output_mtl = os.path.join(self.output_dir, "final_output_scene_mesh.mtl") + + if not (os.path.exists(output_obj) and os.path.exists(output_mtl)): + raise Exception("Blender flip did not generate expected output files") + + self.update_status("Blender flip completed successfully") + self.flip_status.config(text="✓ Complete", foreground="green") + + except Exception as e: + self.update_status(f"Error in Blender flip: {str(e)}") + self.flip_status.config(text="✗ Failed", foreground="red") + messagebox.showerror("Error", f"Blender flip failed: {str(e)}") + finally: + if original_dir: + os.chdir(original_dir) + def run_all_steps(self): + """Run all EdgeNet processing steps in sequence""" + try: + self.run_edge_net() + if self.edge_status.cget("text") == "✓ Complete": + self.run_mesh_split() + if self.split_status.cget("text") == "✓ Complete": + self.run_blender_flip() + + except Exception as e: + self.update_status(f"Error in processing pipeline: {str(e)}") + messagebox.showerror("Error", f"Processing pipeline failed: {str(e)}") + + def clean_temp_files(self): + """Clean up temporary files""" + try: + temp_files = [ + self.config_reader.file_paths['shiftedImage'], + self.config_reader.file_paths['monoDepthImage'] + ] + + for file_path in temp_files: + if os.path.exists(file_path): + os.remove(file_path) + self.update_status(f"Removed temporary file: {file_path}") + + self.update_status("Temporary files cleaned up") + + except Exception as e: + self.update_status(f"Error cleaning temporary files: {str(e)}") + messagebox.showerror("Error", f"Failed to clean temporary files: {str(e)}") + + def update_status(self, message): + self.status_text.insert(tk.END, f"{message}\n") + self.status_text.see(tk.END) + + def clear_status(self): + self.status_text.delete(1.0, tk.END) + + def update_depth_preview(self): + """Update the enhanced depth preview""" + preview_size = (300, 150) # Adjust size as needed + depth_path = os.path.join(self.edge_net_dir, "Input", "enhanced_depth_e.png") + + if os.path.exists(depth_path): + try: + depth_img = Image.open(depth_path) + depth_img.thumbnail(preview_size) + depth_photo = ImageTk.PhotoImage(depth_img) + self.depth_preview.configure(image=depth_photo) + self.depth_preview.image = depth_photo + except Exception as e: + self.update_status(f"Failed to load depth preview: {str(e)}") + + def update_mesh_preview(self): + """Update the mesh preview""" + # Note: This would require additional implementation to render mesh preview + # Could potentially use a screenshot or placeholder image + self.update_status("Mesh preview not implemented") \ No newline at end of file