From c3c8e9f1415a054ebcab755c52a9c06e120569ff Mon Sep 17 00:00:00 2001 From: Deus-Ex <7816029+koosoli@users.noreply.github.com> Date: Fri, 29 Aug 2025 12:43:17 +0200 Subject: [PATCH] input image and meta data --- __pycache__/gui_interface.cpython-311.pyc | Bin 0 -> 83111 bytes gui_interface.py | 281 ++++++++++++++++++++++ 2 files changed, 281 insertions(+) create mode 100644 __pycache__/gui_interface.cpython-311.pyc diff --git a/__pycache__/gui_interface.cpython-311.pyc b/__pycache__/gui_interface.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a4284004db4e216481100f8d14b570a905e4180c GIT binary patch literal 83111 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", self.open_file) + def create_camera_footage_panel(self): + """Create the camera footage input panel.""" + # Configure grid for camera frame + self.camera_frame.columnconfigure(0, weight=1) + self.camera_frame.columnconfigure(1, weight=1) + self.camera_frame.columnconfigure(2, weight=2) + + # Title and description + ttk.Label(self.camera_frame, text="Load Camera Footage for Motion Tracking", font=("Segoe UI", 10, "bold")) \ + .grid(row=0, column=0, columnspan=3, sticky=tk.W, pady=(0, 10)) + + # Folder selection buttons + self.images_btn = ttk.Button( + self.camera_frame, text="📁 Select Images Folder", + command=self.select_images_folder + ) + self.images_btn.grid(row=1, column=0, sticky=tk.W, padx=(0, 5), pady=2) + + self.metadata_btn = ttk.Button( + self.camera_frame, text="📋 Select Metadata File", + command=self.select_metadata_file + ) + self.metadata_btn.grid(row=1, column=1, sticky=tk.W, padx=(0, 5), pady=2) + + # Status indicators + self.images_path_label = ttk.Label(self.camera_frame, text="Images: None", foreground="red") + self.images_path_label.grid(row=2, column=0, columnspan=2, sticky=tk.W, pady=2) + + self.metadata_path_label = ttk.Label(self.camera_frame, text="Metadata: None", foreground="red") + self.metadata_path_label.grid(row=3, column=0, columnspan=2, sticky=tk.W, pady=2) + + # Validation and preview buttons + self.validate_btn = ttk.Button( + self.camera_frame, text="✅ Validate Metadata", + command=self.validate_metadata, state="disabled" + ) + self.validate_btn.grid(row=4, column=0, sticky=tk.W, pady=(10, 5)) + + self.preview_btn = ttk.Button( + self.camera_frame, text="👁️ Preview Sequence", + command=self.preview_sequence, state="disabled" + ) + self.preview_btn.grid(row=4, column=1, sticky=tk.W, pady=(10, 5)) + + # Clear button + ttk.Button(self.camera_frame, text="🗑️ Clear Data", + command=self.clear_camera_data).grid(row=4, column=2, sticky=tk.W, pady=(10, 5)) + + # Metadata display frame + self.metadata_display_frame = ttk.LabelFrame(self.camera_frame, text="📊 Camera Metadata", padding="5") + self.metadata_display_frame.grid(row=5, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(10, 0)) + + # Configure metadata display + self.metadata_display_frame.columnconfigure(0, weight=1) + self.metadata_display_frame.columnconfigure(1, weight=1) + + # Metadata display widgets + self.metadata_text = scrolledtext.ScrolledText( + self.metadata_display_frame, + wrap=tk.WORD, + height=8, + state='disabled', + font=("Consolas", 9) + ) + self.metadata_text.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5) + + # Statistics labels + ttk.Label(self.metadata_display_frame, text="Frames:").grid(row=1, column=0, sticky=tk.W, padx=5) + self.frame_count_label = ttk.Label(self.metadata_display_frame, text="0") + self.frame_count_label.grid(row=1, column=1, sticky=tk.W) + + ttk.Label(self.metadata_display_frame, text="Cameras:").grid(row=2, column=0, sticky=tk.W, padx=5) + self.camera_count_label = ttk.Label(self.metadata_display_frame, text="0") + self.camera_count_label.grid(row=2, column=1, sticky=tk.W) + + ttk.Label(self.metadata_display_frame, text="Status:").grid(row=3, column=0, sticky=tk.W, padx=5) + self.metadata_status_label = ttk.Label(self.metadata_display_frame, text="Not loaded", foreground="red") + self.metadata_status_label.grid(row=3, column=1, sticky=tk.W) + + def select_images_folder(self): + """Select folder containing camera images.""" + folder_path = filedialog.askdirectory(title="Select Images Folder") + if folder_path: + self.camera_images_path = Path(folder_path) + image_extensions = {'.png', '.jpg', '.jpeg', '.bmp', '.tiff'} + self.image_files = [ + f for f in self.camera_images_path.iterdir() + if f.is_file() and f.suffix.lower() in image_extensions + ] + self.image_files.sort() + self.images_path_label.config( + text=f"Images: {self.camera_images_path.name} ({len(self.image_files)} files)", + foreground="green" + ) + self.log_message(f"✓ Loaded {len(self.image_files)} images from {self.camera_images_path.name}") + + # Enable validate button if both paths are set + if self.metadata_path: + self.validate_btn.config(state="normal") + self.preview_btn.config(state="normal") + self.update_status() + + def select_metadata_file(self): + """Select metadata.json file.""" + file_path = filedialog.askopenfilename( + title="Select Metadata File", + filetypes=[("JSON files", "*.json"), ("All files", "*.*")] + ) + if file_path: + self.metadata_path = Path(file_path) + self.metadata_path_label.config( + text=f"Metadata: {self.metadata_path.name}", + foreground="green" + ) + self.log_message(f"✓ Selected metadata file: {self.metadata_path.name}") + + # Enable validate button if both paths are set + if self.camera_images_path: + self.validate_btn.config(state="normal") + self.preview_btn.config(state="normal") + + def validate_metadata(self): + """Validate and parse metadata.json file.""" + try: + with open(self.metadata_path, 'r') as f: + data = json.load(f) + + # Validate data structure + if not isinstance(data, list): + raise ValueError("Metadata must be a list of camera entries") + + if len(data) == 0: + raise ValueError("No camera entries found in metadata") + + # Validate each entry + required_fields = ["camera_index", "frame_index", "camera_position", + "yaw", "pitch", "roll", "image_file"] + + validated_data = [] + cameras = set() + frames = set() + + for i, entry in enumerate(data): + for field in required_fields: + if field not in entry: + raise ValueError(f"Missing field '{field}' in entry {i+1}") + + if not isinstance(entry["camera_position"], list) or len(entry["camera_position"]) != 3: + raise ValueError(f"camera_position must be a 3-element list in entry {i+1}") + + cameras.add(entry["camera_index"]) + frames.add(entry["frame_index"]) + validated_data.append(entry) + + self.camera_data = validated_data + + # Update UI + self.frame_count_label.config(text=str(len(frames))) + self.camera_count_label.config(text=str(len(cameras))) + self.metadata_status_label.config(text="Valid ✓", foreground="green") + + # Display metadata in text area + self.metadata_text.config(state='normal') + self.metadata_text.delete(1.0, tk.END) + self.metadata_text.insert(tk.END, json.dumps(validated_data[:5], indent=2)) # Show first 5 entries + if len(validated_data) > 5: + self.metadata_text.insert(tk.END, f"\n[... and {len(validated_data)-5} more entries]") + self.metadata_text.config(state='disabled') + + self.log_message(f"✓ Validated metadata: {len(cameras)} cameras, {len(frames)} frames") + + except Exception as e: + error_msg = f"Metadata validation error: {str(e)}" + messagebox.showerror("Metadata Error", error_msg) + self.metadata_status_label.config(text="Invalid ✗", foreground="red") + self.log_message(f"❌ {error_msg}") + + def preview_sequence(self): + """Preview the image sequence.""" + if not self.image_files: + messagebox.showwarning("Preview Error", "No image files loaded") + return + + try: + from PIL import Image + + # Create a simple preview window + preview_window = tk.Toplevel(self.root) + preview_window.title("Image Sequence Preview") + preview_window.geometry("800x600") + + # Create canvas and scrollbar + canvas = tk.Canvas(preview_window) + scrollbar = ttk.Scrollbar(preview_window, orient="vertical", command=canvas.yview) + scrollable_frame = ttk.Frame(canvas) + + scrollable_frame.bind( + "", + lambda e: canvas.configure(scrollregion=canvas.bbox("all")) + ) + + canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") + canvas.configure(yscrollcommand=scrollbar.set) + + # Add thumbnail images + max_images = min(20, len(self.image_files)) # Limit to 20 thumbnails + for i, img_file in enumerate(self.image_files[:max_images]): + try: + img = Image.open(img_file) + img.thumbnail((150, 150)) + photo = tk.PhotoImage(file=str(img_file)) + + frame = ttk.Frame(scrollable_frame, borderwidth=1, relief="sunken") + frame.grid(row=i//4, column=i%4, padx=5, pady=5) + + img_label = ttk.Label(frame, image=photo) + img_label.image = photo # Keep reference + img_label.pack() + + name_label = ttk.Label(frame, text=img_file.name, font=("Segoe UI", 8)) + name_label.pack(pady=(2, 0)) + + except Exception as img_error: + error_frame = ttk.Frame(scrollable_frame, borderwidth=1, relief="sunken") + error_frame.grid(row=i//4, column=i%4, padx=5, pady=5) + ttk.Label(error_frame, text=f"Error loading: {img_file.name}", + foreground="red", font=("Segoe UI", 8)).pack() + + # Layout + canvas.pack(side="left", fill="both", expand=True) + scrollbar.pack(side="right", fill="y") + + self.log_message(f"✓ Opened image sequence preview ({max_images} thumbnails)") + + except ImportError: + messagebox.showinfo("Preview Info", "PIL (Pillow) not installed. Install with: pip install Pillow") + except Exception as e: + error_msg = f"Preview error: {str(e)}" + messagebox.showerror("Preview Error", error_msg) + self.log_message(f"❌ {error_msg}") + + def clear_camera_data(self): + """Clear all camera data and reset UI.""" + self.camera_images_path = None + self.metadata_path = None + self.camera_data = None + self.image_files = [] + + # Reset UI + self.images_path_label.config(text="Images: None", foreground="red") + self.metadata_path_label.config(text="Metadata: None", foreground="red") + + self.validate_btn.config(state="disabled") + self.preview_btn.config(state="disabled") + + self.frame_count_label.config(text="0") + self.camera_count_label.config(text="0") + self.metadata_status_label.config(text="Not loaded", foreground="red") + + self.metadata_text.config(state='normal') + self.metadata_text.delete(1.0, tk.END) + self.metadata_text.config(state='disabled') + + self.log_message("✓ Cleared all camera data") + def create_npy_viewer_panel(self): """Create the NPY file viewer panel.""" # Control frame for viewer controls