Borcherding commited on
Commit
70e7709
·
verified ·
1 Parent(s): a94c4e0

Upload gradio-hed-app.py

Browse files
Files changed (1) hide show
  1. src/training/gradio-hed-app.py +420 -0
src/training/gradio-hed-app.py ADDED
@@ -0,0 +1,420 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+
3
+ import os
4
+ import sys
5
+ import shutil
6
+ import gradio as gr
7
+ import torch
8
+ import numpy as np
9
+ import PIL.Image
10
+ from PIL import Image
11
+ from pathlib import Path
12
+ import tkinter as tk
13
+ from tkinter import filedialog
14
+ import base64
15
+ import io
16
+
17
+ # Import the model from the provided run.py
18
+ sys.path.append(".")
19
+ from run import Network, estimate
20
+
21
+ # Global variables
22
+ processed_images = []
23
+ output_dir = None
24
+
25
+ # Custom CSS with simplified styling and taller galleries
26
+ custom_css = """
27
+ :root {
28
+ --slate-color: #3F4756;
29
+ --mustard-color: #E5B22B;
30
+ --dark-slate: #2C3241;
31
+ --light-slate: #616C7C;
32
+ --light-yellow: #F8E9B7;
33
+ --warning-red: #D64045;
34
+ }
35
+
36
+ body {
37
+ background-color: var(--slate-color) !important;
38
+ color: white !important;
39
+ }
40
+
41
+ .gradio-container {
42
+ max-width: 95% !important;
43
+ }
44
+
45
+ /* Gallery styling for taller display */
46
+ .gallery-container .svelte-1p8za3 {
47
+ height: 600px !important;
48
+ }
49
+
50
+ .construction-header {
51
+ background-color: var(--mustard-color);
52
+ color: black;
53
+ padding: 20px;
54
+ border-radius: 10px;
55
+ margin-bottom: 20px;
56
+ border: 5px solid #333;
57
+ }
58
+
59
+ button.primary {
60
+ background-color: var(--mustard-color) !important;
61
+ color: black !important;
62
+ font-weight: bold !important;
63
+ border: 2px solid black !important;
64
+ }
65
+
66
+ button {
67
+ background-color: var(--dark-slate) !important;
68
+ border: 2px solid var(--mustard-color) !important;
69
+ color: white !important;
70
+ }
71
+
72
+ .container-box {
73
+ background-color: var(--dark-slate);
74
+ border-radius: 10px;
75
+ padding: 20px;
76
+ margin-bottom: 20px;
77
+ border: 2px solid var(--mustard-color);
78
+ }
79
+
80
+ .caution-divider {
81
+ height: 15px;
82
+ background: repeating-linear-gradient(
83
+ 45deg,
84
+ black,
85
+ black 10px,
86
+ var(--mustard-color) 10px,
87
+ var(--mustard-color) 20px
88
+ );
89
+ margin: 20px 0;
90
+ border-radius: 2px;
91
+ }
92
+
93
+ .info-box {
94
+ background-color: var(--light-slate);
95
+ color: white;
96
+ padding: 15px;
97
+ border-radius: 5px;
98
+ margin: 15px 0;
99
+ border-left: 5px solid var(--mustard-color);
100
+ }
101
+
102
+ .warning-box {
103
+ background-color: var(--warning-red);
104
+ color: white;
105
+ padding: 10px;
106
+ border-radius: 5px;
107
+ margin: 10px 0;
108
+ border: 2px solid black;
109
+ }
110
+
111
+ .footer {
112
+ background-color: var(--mustard-color);
113
+ color: black;
114
+ padding: 10px;
115
+ border-radius: 10px;
116
+ text-align: center;
117
+ margin-top: 20px;
118
+ border: 3px solid #333;
119
+ }
120
+
121
+ /* Construction icon styling */
122
+ .construction-icon {
123
+ display: inline-block;
124
+ font-size: 24px;
125
+ margin-right: 10px;
126
+ vertical-align: middle;
127
+ }
128
+ """
129
+
130
+ def create_output_dir(input_dir):
131
+ """Create an output directory based on the input directory name"""
132
+ input_path = Path(input_dir)
133
+ output_path = input_path.parent / f"{input_path.name}_hed_output"
134
+ os.makedirs(output_path, exist_ok=True)
135
+ return str(output_path)
136
+
137
+ def process_image(input_path):
138
+ """Process a single image with HED"""
139
+ try:
140
+ # Read the image
141
+ img = PIL.Image.open(input_path)
142
+ img = img.convert("RGB")
143
+
144
+ # Resize image to 480x320 if needed (as required by the model)
145
+ orig_size = img.size
146
+ img_resized = img.resize((480, 320), PIL.Image.LANCZOS)
147
+
148
+ # Convert to tensor
149
+ img_np = np.array(img_resized)[:, :, ::-1].transpose(2, 0, 1).astype(np.float32) * (1.0 / 255.0)
150
+ ten_input = torch.FloatTensor(np.ascontiguousarray(img_np))
151
+
152
+ # Process with the model
153
+ ten_output = estimate(ten_input)
154
+
155
+ # Convert back to PIL image
156
+ output_np = (ten_output.clip(0.0, 1.0).numpy(force=True).transpose(1, 2, 0)[:, :, 0] * 255.0).astype(np.uint8)
157
+ output_img = PIL.Image.fromarray(output_np)
158
+
159
+ # Resize back to original dimensions if needed
160
+ if orig_size != (480, 320):
161
+ output_img = output_img.resize(orig_size, PIL.Image.LANCZOS)
162
+
163
+ return output_img
164
+
165
+ except Exception as e:
166
+ print(f"Error processing {input_path}: {str(e)}")
167
+ return None
168
+
169
+ def process_folder(input_dir, output_folder=None, progress=gr.Progress()):
170
+ """Process all images in a folder"""
171
+ global processed_images, output_dir
172
+
173
+ # Remember output directory for other functions
174
+ if not output_folder or output_folder.strip() == "":
175
+ output_dir = create_output_dir(input_dir)
176
+ else:
177
+ output_dir = output_folder
178
+
179
+ # Create dataset directory
180
+ dataset_dir = os.path.join(output_dir, "dataset")
181
+ os.makedirs(dataset_dir, exist_ok=True)
182
+
183
+ # Get all image files
184
+ valid_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif']
185
+ image_files = [
186
+ f for f in os.listdir(input_dir)
187
+ if os.path.isfile(os.path.join(input_dir, f)) and
188
+ any(f.lower().endswith(ext) for ext in valid_extensions)
189
+ ]
190
+
191
+ if not image_files:
192
+ return [], [], f"⚠️ No image files found in {input_dir}"
193
+
194
+ # Initialize progress tracking
195
+ progress(0, desc="🚧 STARTING EDGE DETECTION PROCESSING 🚧")
196
+ processed_images = []
197
+
198
+ # Process each image
199
+ for i, img_file in enumerate(image_files):
200
+ input_path = os.path.join(input_dir, img_file)
201
+
202
+ # Update progress
203
+ progress((i / len(image_files)), desc=f"🔄 Processing image {i+1}/{len(image_files)}: {img_file}")
204
+
205
+ # Process the image
206
+ output_img = process_image(input_path)
207
+ if output_img is None:
208
+ continue
209
+
210
+ # Save input and output images to the dataset folder
211
+ input_copy_path = os.path.join(dataset_dir, f"input_{i:04d}{os.path.splitext(img_file)[1]}")
212
+ output_path = os.path.join(dataset_dir, f"output_{i:04d}{os.path.splitext(img_file)[1]}")
213
+
214
+ # Copy original to dataset
215
+ shutil.copy(input_path, input_copy_path)
216
+
217
+ # Save edge map to dataset
218
+ output_img.save(output_path)
219
+
220
+ # For preview, use original images (better quality)
221
+ input_thumb = Image.open(input_path)
222
+
223
+ # Add to processed images
224
+ processed_images.append((input_thumb, output_img, os.path.basename(input_path)))
225
+
226
+ # Final progress update
227
+ progress(1.0, desc="✅ PROCESSING COMPLETE")
228
+
229
+ # Return thumbnails for gallery display
230
+ input_images = [pair[0] for pair in processed_images[:8]] # First 8 input images
231
+ output_images = [pair[1] for pair in processed_images[:8]] # First 8 output images
232
+
233
+ completion_message = f"✅ CONSTRUCTION COMPLETE! \n🏗️ Processed {len(processed_images)} images \n📊 Dataset created in: {dataset_dir}\n\n(Showing first 8 images in gallery, all images saved to dataset folder)"
234
+
235
+ return input_images, output_images, completion_message
236
+
237
+ def open_folder(folder_path):
238
+ """Open a folder in file explorer"""
239
+ if folder_path and os.path.exists(folder_path):
240
+ if sys.platform == 'win32':
241
+ os.startfile(folder_path)
242
+ elif sys.platform == 'darwin': # macOS
243
+ os.system(f'open "{folder_path}"')
244
+ else: # Linux
245
+ os.system(f'xdg-open "{folder_path}"')
246
+ return f"🔍 Opened folder: {folder_path}"
247
+ return "⚠️ Folder doesn't exist"
248
+
249
+ def browse_folder():
250
+ """Open a folder browser dialog and return the selected path"""
251
+ root = tk.Tk()
252
+ root.withdraw() # Hide the main window
253
+ root.attributes('-topmost', True) # Make sure it appears on top
254
+ folder_path = filedialog.askdirectory()
255
+ return folder_path if folder_path else None
256
+
257
+ def view_more_images():
258
+ """Open the output folder to view all processed images"""
259
+ global output_dir
260
+ if output_dir and os.path.exists(output_dir):
261
+ return open_folder(output_dir)
262
+ return "⚠️ No output folder available. Process images first."
263
+
264
+ # Initialize the model
265
+ print("Initializing HED model...")
266
+ model = Network().cuda().train(False)
267
+ print("Model loaded!")
268
+
269
+ # Create Gradio interface
270
+ with gr.Blocks(title="HED Edge Detection - Construction Zone", css=custom_css) as app:
271
+ # Custom Header
272
+ gr.HTML("""
273
+ <div class="construction-header">
274
+ <h1>🏗️ EDGE DETECTION CONSTRUCTION ZONE 🏗️</h1>
275
+ <p>Build perfect edge maps from your images with HED technology</p>
276
+ </div>
277
+ """)
278
+
279
+ # Main content
280
+ with gr.Row():
281
+ with gr.Column(scale=1):
282
+ # Container for controls
283
+ gr.HTML('<div class="container-box">')
284
+ gr.HTML("<h3>🔧 CONTROL PANEL 🔧</h3>")
285
+
286
+ # Caution divider
287
+ gr.HTML('<div class="caution-divider"></div>')
288
+
289
+ # Input components
290
+ gr.HTML('<span class="construction-icon">📁</span><b>PROJECT MATERIALS</b>')
291
+ input_folder = gr.Textbox(label="Input Folder", placeholder="Select your input folder")
292
+ browse_input_button = gr.Button("📂 Browse Input Folder")
293
+
294
+ gr.HTML('<div class="caution-divider"></div>')
295
+
296
+ gr.HTML('<span class="construction-icon">🔨</span><b>CONSTRUCTION OUTPUT</b>')
297
+ output_folder = gr.Textbox(label="Output Folder (optional)", placeholder="Select output destination (or leave empty)")
298
+ browse_output_button = gr.Button("📂 Browse Output Folder")
299
+
300
+ gr.HTML('<div class="caution-divider"></div>')
301
+
302
+ # Action buttons
303
+ gr.HTML('<span class="construction-icon">⚡</span><b>OPERATIONS</b>')
304
+ process_button = gr.Button("🚧 START CONSTRUCTION 🚧", elem_classes=["primary"])
305
+
306
+ with gr.Row():
307
+ open_input_button = gr.Button("🔍 View Input Site")
308
+ open_output_button = gr.Button("🔍 View Output Site")
309
+
310
+ # Add view more button
311
+ view_more_button = gr.Button("🔍 View All Processed Images")
312
+
313
+ # Status textbox
314
+ status_text = gr.Textbox(
315
+ label="CONSTRUCTION STATUS",
316
+ interactive=False,
317
+ value="🚧 Ready to build! Select folders and start construction.",
318
+ lines=5
319
+ )
320
+
321
+ gr.HTML('</div>') # Close container-box
322
+
323
+ # Info box
324
+ gr.HTML("""
325
+ <div class="info-box">
326
+ <h4>🔔 SITE INFORMATION</h4>
327
+ <ul>
328
+ <li>All edge maps will be saved directly to the dataset folder</li>
329
+ <li>Images are temporarily resized to 480x320 during processing</li>
330
+ <li>Input/output pairs are saved with matching indices for training</li>
331
+ <li>Gallery shows first 8 images - use "View All Processed Images" to see all results</li>
332
+ </ul>
333
+ </div>
334
+ """)
335
+
336
+ # Warning box
337
+ gr.HTML("""
338
+ <div class="warning-box">
339
+ <h4>⚠️ SAFETY FIRST!</h4>
340
+ <p>This operation requires CUDA to run efficiently. CPU processing will be extremely slow.</p>
341
+ </div>
342
+ """)
343
+
344
+ with gr.Column(scale=2):
345
+ # Construction site viewer with separate galleries
346
+ gr.HTML("<h3>🏗️ CONSTRUCTION SITE VIEWER 🏗️</h3>")
347
+
348
+ with gr.Row():
349
+ with gr.Column():
350
+ # Input images with taller height
351
+ input_gallery = gr.Gallery(
352
+ label="INPUT IMAGES",
353
+ show_label=True,
354
+ height=600, # Increased from 400 to 600
355
+ object_fit="contain",
356
+ elem_classes=["gallery-container"]
357
+ )
358
+
359
+ with gr.Column():
360
+ # Output images (HED) with taller height
361
+ output_gallery = gr.Gallery(
362
+ label="HED EDGE MAPS",
363
+ show_label=True,
364
+ height=600, # Increased from 400 to 600
365
+ object_fit="contain",
366
+ elem_classes=["gallery-container"]
367
+ )
368
+
369
+ # Footer
370
+ gr.HTML("""
371
+ <div class="footer">
372
+ <p>🏗️ HOLISTICALLY-NESTED EDGE DETECTION (HED) CONSTRUCTION EQUIPMENT 🏗️</p>
373
+ <p>Building better edges since 2015 | Hard Hat Area | Authorized Personnel Only</p>
374
+ </div>
375
+ """)
376
+
377
+ # Event handlers for folder selection
378
+ browse_input_button.click(
379
+ fn=browse_folder,
380
+ outputs=input_folder
381
+ )
382
+
383
+ browse_output_button.click(
384
+ fn=browse_folder,
385
+ outputs=output_folder
386
+ )
387
+
388
+ # Process button handler
389
+ process_button.click(
390
+ fn=process_folder,
391
+ inputs=[input_folder, output_folder],
392
+ outputs=[input_gallery, output_gallery, status_text]
393
+ )
394
+
395
+ # Open folder buttons
396
+ open_input_button.click(
397
+ fn=open_folder,
398
+ inputs=input_folder,
399
+ outputs=status_text
400
+ )
401
+
402
+ open_output_button.click(
403
+ fn=open_folder,
404
+ inputs=output_folder,
405
+ outputs=status_text
406
+ )
407
+
408
+ # View more images button
409
+ view_more_button.click(
410
+ fn=view_more_images,
411
+ outputs=status_text
412
+ )
413
+
414
+ if __name__ == "__main__":
415
+ # Make sure cuda is available
416
+ if not torch.cuda.is_available():
417
+ print("⚠️ WARNING: CUDA is not available. The model will run on CPU and be EXTREMELY slow.")
418
+
419
+ # Launch the app
420
+ app.launch(share=False)