Updated UI to resize according to model preferred dimensions (640x640
Browse files- gradio_test.py +155 -123
gradio_test.py
CHANGED
@@ -52,35 +52,54 @@ ONEFORMER_CONFIG = {
|
|
52 |
"swin_cfg": "configs/ade20k/oneformer_swin_large_IN21k_384_bs16_160k.yaml",
|
53 |
"swin_model": "shi-labs/oneformer_ade20k_swin_large",
|
54 |
"swin_file": "250_16_swin_l_oneformer_ade20k_160k.pth",
|
55 |
-
"
|
|
|
56 |
}
|
57 |
}
|
58 |
|
59 |
BLACKSPOT_MODEL_REPO = "sww35/neuronest-blackspot"
|
60 |
BLACKSPOT_MODEL_FILE = "model_0004999.pth"
|
61 |
|
62 |
-
|
63 |
-
|
64 |
|
65 |
from universal_contrast_analyzer import UniversalContrastAnalyzer
|
66 |
|
67 |
-
def
|
68 |
h, w = image.shape[:2]
|
69 |
-
|
70 |
-
|
71 |
-
elif max(h, w) > max_size:
|
72 |
scale = max_size / max(h, w)
|
73 |
-
else:
|
74 |
-
return image
|
75 |
new_w = int(w * scale)
|
76 |
new_h = int(h * scale)
|
77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
|
79 |
class OneFormerManager:
|
80 |
def __init__(self):
|
81 |
self.predictor = None
|
82 |
self.metadata = None
|
83 |
self.initialized = False
|
|
|
|
|
84 |
|
85 |
def initialize(self, backbone: str = "swin"):
|
86 |
if not ONEFORMER_AVAILABLE:
|
@@ -113,31 +132,25 @@ class OneFormerManager:
|
|
113 |
logger.error(f"Failed to initialize OneFormer: {e}")
|
114 |
return False
|
115 |
|
116 |
-
def semantic_segmentation(self, image: np.ndarray) -> Tuple[np.ndarray, np.ndarray
|
117 |
if not self.initialized:
|
118 |
raise RuntimeError("OneFormer not initialized")
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
seg_mask = predictions["sem_seg"].argmax(dim=0).cpu().numpy()
|
126 |
-
seg_mask_original = cv2.resize(
|
127 |
-
seg_mask.astype(np.uint8),
|
128 |
-
(original_w, original_h),
|
129 |
-
interpolation=cv2.INTER_NEAREST
|
130 |
-
)
|
131 |
visualizer = Visualizer(
|
132 |
image[:, :, ::-1],
|
133 |
metadata=self.metadata,
|
134 |
instance_mode=ColorMode.IMAGE,
|
135 |
-
scale=1.
|
136 |
)
|
137 |
-
vis_output = visualizer.draw_sem_seg(seg_mask_original, alpha=0.
|
138 |
vis_image = vis_output.get_image()[:, :, ::-1]
|
139 |
-
vis_image_display =
|
140 |
-
return seg_mask_original, vis_image_display
|
141 |
|
142 |
def extract_floor_areas(self, segmentation: np.ndarray) -> np.ndarray:
|
143 |
floor_mask = np.zeros_like(segmentation, dtype=bool)
|
@@ -275,7 +288,7 @@ class ImprovedBlackspotDetector:
|
|
275 |
for mask in filtered_blackspot_masks:
|
276 |
combined_blackspot |= mask
|
277 |
visualization = self.create_visualization(image, floor_mask, combined_blackspot)
|
278 |
-
visualization_display =
|
279 |
floor_area = int(np.sum(floor_mask))
|
280 |
blackspot_area = int(np.sum(combined_blackspot))
|
281 |
coverage_percentage = (blackspot_area / floor_area * 100) if floor_area > 0 else 0
|
@@ -304,12 +317,12 @@ class ImprovedBlackspotDetector:
|
|
304 |
blackspot_contours, _ = cv2.findContours(
|
305 |
blackspot_mask.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
|
306 |
)
|
307 |
-
cv2.drawContours(vis, blackspot_contours, -1, (255, 255, 0),
|
308 |
return vis
|
309 |
|
310 |
def _empty_results(self, image: np.ndarray) -> Dict:
|
311 |
empty_mask = np.zeros(image.shape[:2], dtype=bool)
|
312 |
-
visualization_display =
|
313 |
return {
|
314 |
'visualization': visualization_display,
|
315 |
'floor_mask': empty_mask,
|
@@ -351,7 +364,7 @@ class NeuroNestApp:
|
|
351 |
if not self.initialized:
|
352 |
return {"error": "Application not properly initialized"}
|
353 |
try:
|
354 |
-
image = cv2.imread(image_path)
|
355 |
if image is None:
|
356 |
return {"error": "Could not load image"}
|
357 |
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
@@ -364,7 +377,7 @@ class NeuroNestApp:
|
|
364 |
'statistics': {}
|
365 |
}
|
366 |
logger.info("Running semantic segmentation...")
|
367 |
-
seg_mask, seg_visualization
|
368 |
results['segmentation'] = {
|
369 |
'visualization': seg_visualization,
|
370 |
'mask': seg_mask
|
@@ -387,7 +400,7 @@ class NeuroNestApp:
|
|
387 |
contrast_results = self.contrast_analyzer.analyze_contrast(
|
388 |
image_rgb, seg_mask
|
389 |
)
|
390 |
-
contrast_viz_display =
|
391 |
contrast_results['visualization'] = contrast_viz_display
|
392 |
results['contrast'] = contrast_results
|
393 |
logger.info("Contrast analysis completed")
|
@@ -535,105 +548,124 @@ def create_gradio_interface():
|
|
535 |
- **Floor-Only Blackspot Detection**: Locates dangerous dark areas on walking surfaces
|
536 |
- **Universal Contrast Analysis**: Evaluates visibility between ALL adjacent objects
|
537 |
|
538 |
-
*Following WCAG 2.1 guidelines for visual accessibility
|
539 |
"""
|
540 |
with gr.Blocks(css="""
|
541 |
-
.
|
542 |
-
|
543 |
-
|
544 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
545 |
}
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
)
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
with gr.
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
593 |
seg_display = gr.Image(
|
594 |
-
label=
|
595 |
interactive=False,
|
596 |
-
show_label=
|
597 |
-
|
598 |
-
)
|
599 |
-
blackspot_display = gr.Image(
|
600 |
-
label="⚫ Blackspot Detection",
|
601 |
-
interactive=False,
|
602 |
-
visible=blackspot_ok,
|
603 |
-
show_label=True,
|
604 |
-
height=600
|
605 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
606 |
contrast_display = gr.Image(
|
607 |
-
label=
|
608 |
interactive=False,
|
609 |
-
show_label=
|
610 |
-
|
611 |
)
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
|
|
637 |
return interface
|
638 |
|
639 |
if __name__ == "__main__":
|
|
|
52 |
"swin_cfg": "configs/ade20k/oneformer_swin_large_IN21k_384_bs16_160k.yaml",
|
53 |
"swin_model": "shi-labs/oneformer_ade20k_swin_large",
|
54 |
"swin_file": "250_16_swin_l_oneformer_ade20k_160k.pth",
|
55 |
+
"process_size": 640,
|
56 |
+
"max_size": 2560
|
57 |
}
|
58 |
}
|
59 |
|
60 |
BLACKSPOT_MODEL_REPO = "sww35/neuronest-blackspot"
|
61 |
BLACKSPOT_MODEL_FILE = "model_0004999.pth"
|
62 |
|
63 |
+
DISPLAY_MAX_WIDTH = 1920
|
64 |
+
DISPLAY_MAX_HEIGHT = 1080
|
65 |
|
66 |
from universal_contrast_analyzer import UniversalContrastAnalyzer
|
67 |
|
68 |
+
def resize_image_for_processing(image: np.ndarray, target_size: int = 640, max_size: int = 2560) -> Tuple[np.ndarray, float]:
|
69 |
h, w = image.shape[:2]
|
70 |
+
scale = target_size / min(h, w)
|
71 |
+
if scale * max(h, w) > max_size:
|
|
|
72 |
scale = max_size / max(h, w)
|
|
|
|
|
73 |
new_w = int(w * scale)
|
74 |
new_h = int(h * scale)
|
75 |
+
new_w = (new_w // 32) * 32
|
76 |
+
new_h = (new_h // 32) * 32
|
77 |
+
resized = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4)
|
78 |
+
return resized, scale
|
79 |
+
|
80 |
+
def resize_mask_to_original(mask: np.ndarray, original_size: Tuple[int, int]) -> np.ndarray:
|
81 |
+
return cv2.resize(mask.astype(np.uint8), (original_size[1], original_size[0]), interpolation=cv2.INTER_NEAREST)
|
82 |
+
|
83 |
+
def prepare_display_image(image: np.ndarray, max_width: int = DISPLAY_MAX_WIDTH, max_height: int = DISPLAY_MAX_HEIGHT) -> np.ndarray:
|
84 |
+
h, w = image.shape[:2]
|
85 |
+
scale = 1.0
|
86 |
+
if w > max_width:
|
87 |
+
scale = max_width / w
|
88 |
+
if h * scale > max_height:
|
89 |
+
scale = max_height / h
|
90 |
+
if scale < 1.0:
|
91 |
+
new_w = int(w * scale)
|
92 |
+
new_h = int(h * scale)
|
93 |
+
return cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4)
|
94 |
+
return image
|
95 |
|
96 |
class OneFormerManager:
|
97 |
def __init__(self):
|
98 |
self.predictor = None
|
99 |
self.metadata = None
|
100 |
self.initialized = False
|
101 |
+
self.process_size = ONEFORMER_CONFIG["ADE20K"]["process_size"]
|
102 |
+
self.max_size = ONEFORMER_CONFIG["ADE20K"]["max_size"]
|
103 |
|
104 |
def initialize(self, backbone: str = "swin"):
|
105 |
if not ONEFORMER_AVAILABLE:
|
|
|
132 |
logger.error(f"Failed to initialize OneFormer: {e}")
|
133 |
return False
|
134 |
|
135 |
+
def semantic_segmentation(self, image: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
136 |
if not self.initialized:
|
137 |
raise RuntimeError("OneFormer not initialized")
|
138 |
+
original_size = (image.shape[0], image.shape[1])
|
139 |
+
image_processed, scale = resize_image_for_processing(image, self.process_size, self.max_size)
|
140 |
+
logger.info(f"Processing image at {image_processed.shape}, scale: {scale}")
|
141 |
+
predictions = self.predictor(image_processed, "semantic")
|
142 |
+
seg_mask_processed = predictions["sem_seg"].argmax(dim=0).cpu().numpy()
|
143 |
+
seg_mask_original = resize_mask_to_original(seg_mask_processed, original_size)
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
visualizer = Visualizer(
|
145 |
image[:, :, ::-1],
|
146 |
metadata=self.metadata,
|
147 |
instance_mode=ColorMode.IMAGE,
|
148 |
+
scale=1.0
|
149 |
)
|
150 |
+
vis_output = visualizer.draw_sem_seg(seg_mask_original, alpha=0.6)
|
151 |
vis_image = vis_output.get_image()[:, :, ::-1]
|
152 |
+
vis_image_display = prepare_display_image(vis_image)
|
153 |
+
return seg_mask_original, vis_image_display
|
154 |
|
155 |
def extract_floor_areas(self, segmentation: np.ndarray) -> np.ndarray:
|
156 |
floor_mask = np.zeros_like(segmentation, dtype=bool)
|
|
|
288 |
for mask in filtered_blackspot_masks:
|
289 |
combined_blackspot |= mask
|
290 |
visualization = self.create_visualization(image, floor_mask, combined_blackspot)
|
291 |
+
visualization_display = prepare_display_image(visualization)
|
292 |
floor_area = int(np.sum(floor_mask))
|
293 |
blackspot_area = int(np.sum(combined_blackspot))
|
294 |
coverage_percentage = (blackspot_area / floor_area * 100) if floor_area > 0 else 0
|
|
|
317 |
blackspot_contours, _ = cv2.findContours(
|
318 |
blackspot_mask.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
|
319 |
)
|
320 |
+
cv2.drawContours(vis, blackspot_contours, -1, (255, 255, 0), 4)
|
321 |
return vis
|
322 |
|
323 |
def _empty_results(self, image: np.ndarray) -> Dict:
|
324 |
empty_mask = np.zeros(image.shape[:2], dtype=bool)
|
325 |
+
visualization_display = prepare_display_image(image)
|
326 |
return {
|
327 |
'visualization': visualization_display,
|
328 |
'floor_mask': empty_mask,
|
|
|
364 |
if not self.initialized:
|
365 |
return {"error": "Application not properly initialized"}
|
366 |
try:
|
367 |
+
image = cv2.imread(image_path, cv2.IMREAD_COLOR)
|
368 |
if image is None:
|
369 |
return {"error": "Could not load image"}
|
370 |
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
|
|
377 |
'statistics': {}
|
378 |
}
|
379 |
logger.info("Running semantic segmentation...")
|
380 |
+
seg_mask, seg_visualization = self.oneformer.semantic_segmentation(image_rgb)
|
381 |
results['segmentation'] = {
|
382 |
'visualization': seg_visualization,
|
383 |
'mask': seg_mask
|
|
|
400 |
contrast_results = self.contrast_analyzer.analyze_contrast(
|
401 |
image_rgb, seg_mask
|
402 |
)
|
403 |
+
contrast_viz_display = prepare_display_image(contrast_results['visualization'])
|
404 |
contrast_results['visualization'] = contrast_viz_display
|
405 |
results['contrast'] = contrast_results
|
406 |
logger.info("Contrast analysis completed")
|
|
|
548 |
- **Floor-Only Blackspot Detection**: Locates dangerous dark areas on walking surfaces
|
549 |
- **Universal Contrast Analysis**: Evaluates visibility between ALL adjacent objects
|
550 |
|
551 |
+
*Following WCAG 2.1 guidelines for visual accessibility | Upload a Picture. Click 'Analyze Environment'.Then scroll down.*
|
552 |
"""
|
553 |
with gr.Blocks(css="""
|
554 |
+
.container { max-width: 100%; margin: auto; padding: 20px; }
|
555 |
+
.image-output { margin: 20px 0; }
|
556 |
+
.image-output img {
|
557 |
+
width: 100%;
|
558 |
+
height: auto;
|
559 |
+
max-width: 1920px;
|
560 |
+
margin: 0 auto;
|
561 |
+
display: block;
|
562 |
+
border: 1px solid #ddd;
|
563 |
+
border-radius: 8px;
|
564 |
}
|
565 |
+
.controls-row { margin-bottom: 30px; background: #f5f5f5; padding: 20px; border-radius: 8px; }
|
566 |
+
.main-button { height: 80px !important; font-size: 1.3em !important; font-weight: bold !important; }
|
567 |
+
.report-box { max-width: 1200px; margin: 30px auto; padding: 30px; background: #f9f9f9; border-radius: 8px; }
|
568 |
+
h2 { margin-top: 40px; margin-bottom: 20px; color: #333; }
|
569 |
+
""", theme=gr.themes.Base()) as interface:
|
570 |
+
with gr.Column(elem_classes="container"):
|
571 |
+
gr.Markdown(f"# {title}")
|
572 |
+
gr.Markdown(description)
|
573 |
+
if not blackspot_ok:
|
574 |
+
gr.Markdown("""
|
575 |
+
⚠️ **Note:** Blackspot detection model not available.
|
576 |
+
To enable blackspot detection, upload the model to HuggingFace or ensure it's in the local directory.
|
577 |
+
""")
|
578 |
+
with gr.Row(elem_classes="controls-row"):
|
579 |
+
with gr.Column(scale=1):
|
580 |
+
enable_blackspot = gr.Checkbox(
|
581 |
+
value=blackspot_ok,
|
582 |
+
label="Enable Floor Blackspot Detection",
|
583 |
+
interactive=blackspot_ok
|
584 |
+
)
|
585 |
+
blackspot_threshold = gr.Slider(
|
586 |
+
minimum=0.1,
|
587 |
+
maximum=0.9,
|
588 |
+
value=0.5,
|
589 |
+
step=0.05,
|
590 |
+
label="Blackspot Sensitivity",
|
591 |
+
visible=blackspot_ok
|
592 |
+
)
|
593 |
+
with gr.Column(scale=1):
|
594 |
+
enable_contrast = gr.Checkbox(
|
595 |
+
value=True,
|
596 |
+
label="Enable Universal Contrast Analysis"
|
597 |
+
)
|
598 |
+
contrast_threshold = gr.Slider(
|
599 |
+
minimum=3.0,
|
600 |
+
maximum=7.0,
|
601 |
+
value=4.5,
|
602 |
+
step=0.1,
|
603 |
+
label="WCAG Contrast Threshold"
|
604 |
+
)
|
605 |
+
with gr.Row():
|
606 |
+
with gr.Column(scale=2):
|
607 |
+
image_input = gr.Image(
|
608 |
+
label="📸 Upload Room Image",
|
609 |
+
type="filepath",
|
610 |
+
height=500
|
611 |
+
)
|
612 |
+
with gr.Column(scale=1):
|
613 |
+
analyze_button = gr.Button(
|
614 |
+
"🔍 Analyze Environment",
|
615 |
+
variant="primary",
|
616 |
+
elem_classes="main-button"
|
617 |
+
)
|
618 |
+
gr.Markdown("---")
|
619 |
+
gr.Markdown("## 🎯 Segmented Objects")
|
620 |
seg_display = gr.Image(
|
621 |
+
label=None,
|
622 |
interactive=False,
|
623 |
+
show_label=False,
|
624 |
+
elem_classes="image-output"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
625 |
)
|
626 |
+
if blackspot_ok:
|
627 |
+
gr.Markdown("## ⚫ Blackspot Detection")
|
628 |
+
blackspot_display = gr.Image(
|
629 |
+
label=None,
|
630 |
+
interactive=False,
|
631 |
+
show_label=False,
|
632 |
+
elem_classes="image-output"
|
633 |
+
)
|
634 |
+
else:
|
635 |
+
blackspot_display = gr.Image(visible=False)
|
636 |
+
gr.Markdown("## 🎨 Contrast Analysis")
|
637 |
contrast_display = gr.Image(
|
638 |
+
label=None,
|
639 |
interactive=False,
|
640 |
+
show_label=False,
|
641 |
+
elem_classes="image-output"
|
642 |
)
|
643 |
+
gr.Markdown("---")
|
644 |
+
analysis_report = gr.Markdown(
|
645 |
+
value="Upload an image and click 'Analyze Environment' to begin.",
|
646 |
+
elem_classes="report-box"
|
647 |
+
)
|
648 |
+
analyze_button.click(
|
649 |
+
fn=analyze_wrapper,
|
650 |
+
inputs=[
|
651 |
+
image_input,
|
652 |
+
blackspot_threshold,
|
653 |
+
contrast_threshold,
|
654 |
+
enable_blackspot,
|
655 |
+
enable_contrast
|
656 |
+
],
|
657 |
+
outputs=[
|
658 |
+
seg_display,
|
659 |
+
blackspot_display,
|
660 |
+
contrast_display,
|
661 |
+
analysis_report
|
662 |
+
]
|
663 |
+
)
|
664 |
+
gr.Markdown("""
|
665 |
+
---
|
666 |
+
**NeuroNest** v2.0 - Enhanced with floor-only blackspot detection and universal contrast analysis
|
667 |
+
*Creating safer environments for cognitive health through AI*
|
668 |
+
""")
|
669 |
return interface
|
670 |
|
671 |
if __name__ == "__main__":
|