File size: 15,240 Bytes
6779b1a
 
 
4d6feed
ef4e074
4d6feed
ef4e074
1c5a214
ef4e074
e653994
 
 
 
 
ef4e074
 
 
 
 
1c5a214
e653994
 
 
 
 
 
 
 
 
 
 
4d6feed
ef4e074
e653994
ef4e074
2cff2f8
ef4e074
 
 
 
 
 
 
2cff2f8
 
e653994
 
 
 
 
 
 
 
2cff2f8
 
ef4e074
 
2cff2f8
e653994
 
 
2cff2f8
 
ef4e074
 
e653994
 
2cff2f8
 
e653994
 
2cff2f8
e653994
2cff2f8
 
 
e653994
 
ef4e074
e653994
 
2cff2f8
e653994
 
 
2cff2f8
e653994
 
 
2cff2f8
e653994
ef4e074
e653994
ef4e074
 
e653994
 
 
 
 
 
 
 
 
2cff2f8
 
 
e653994
1c5a214
 
 
e653994
1c5a214
e653994
1c5a214
 
 
e653994
ef4e074
619aadd
bc4a6f3
 
 
 
619aadd
bc4a6f3
 
619aadd
bc4a6f3
 
619aadd
4d6feed
 
e653994
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
619aadd
 
e653994
ef4e074
e653994
ef4e074
 
 
612fcaf
619aadd
bc4a6f3
 
 
619aadd
 
 
e653994
619aadd
 
 
e653994
a128b36
 
619aadd
e653994
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
717ded7
 
 
 
 
 
2cff2f8
e653994
 
717ded7
 
 
 
 
 
 
e653994
717ded7
e653994
 
717ded7
 
 
 
 
e653994
 
717ded7
 
e653994
717ded7
 
 
e653994
717ded7
e653994
717ded7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1c5a214
e653994
 
 
 
 
 
 
717ded7
 
 
 
7ebecdb
717ded7
 
 
 
 
bc4a6f3
e653994
 
717ded7
 
 
 
 
 
 
 
 
 
e653994
717ded7
 
 
e653994
717ded7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a128b36
7ebecdb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4d6feed
6779b1a
 
ef4e074
1c5a214
e653994
1c5a214
6779b1a
612fcaf
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
import gradio as gr
import numpy as np
import cv2
import os
import tempfile

############################################
# 1. SINGLE-IMAGE GOIS INFERENCE (Imported)
############################################
from gois_core import run_inference
"""
Assumed signature of run_inference:
run_inference(
    image: np.ndarray (RGB),
    model_name: str,
    coarse_size: int,
    fine_size: int,
    c_overlap: float,
    f_overlap: float,
    nms_thresh: float
) -> Tuple[
    np.ndarray,  # FI image
    np.ndarray,  # GOIS image
    str,         # FI HTML table
    str,         # GOIS HTML table
    np.ndarray,  # bar_chart image
    np.ndarray,  # fi_pie image
    np.ndarray,  # gois_pie image
    str          # metrics table HTML
]
"""

############################################
# 2. BATCH INFERENCE FOR MULTIPLE IMAGES
############################################
def run_inference_batch(
    file_paths: list,
    model_name: str,
    coarse_size: int,
    fine_size: int,
    c_overlap: float,
    f_overlap: float,
    nms_thresh: float
):
    """
    1. `file_paths` is a list of string file paths.
    2. For each path:
       - Load the image
       - Run single-image GOIS inference
       - Collect FI & GOIS outputs plus minimal metrics
    3. Return:
       - A gallery of pairs [(FI_1, "Name FI"), (GOIS_1, "Name GOIS"), ..., (FI_N, "Name FI"), (GOIS_N, "Name GOIS")]
       - An HTML table summarizing metrics for each image
    """

    if not file_paths:
        return None, "<p style='color:red;'>No images uploaded.</p>"

    # This will store pairs of images for a gallery
    combined_images = []
    # This will collect rows (HTML) for a metrics summary table
    metrics_rows = []

    for path in file_paths:
        img_name = os.path.basename(path)
        img = cv2.imread(path)  # BGR
        if img is None:
            continue

        # Convert to RGB if your model expects that
        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

        # 1) Run the single-image inference
        (
            fi_img,
            gois_img,
            fi_table_html,
            gois_table_html,
            bar_chart,
            fi_pie,
            gois_pie,
            metrics_html
        ) = run_inference(
            img_rgb, model_name, coarse_size, fine_size, c_overlap, f_overlap, nms_thresh
        )

        # 2) Add to gallery: (image, label)
        combined_images.append((fi_img, f"{img_name} - FI"))
        combined_images.append((gois_img, f"{img_name} - GOIS"))

        # 3) Collect metrics in a single row
        metrics_rows.append(
            f"<tr><td>{img_name}</td><td>{metrics_html or '(No metrics)'} </td></tr>"
        )

    # 4) Build a single HTML table for the entire batch
    table_html = f"""
    <table border="1" style="border-collapse: collapse; width:100%;">
        <thead>
            <tr><th>Image Name</th><th>Metrics</th></tr>
        </thead>
        <tbody>
            {''.join(metrics_rows)}
        </tbody>
    </table>
    """

    return combined_images, table_html


############################################
# 3. EXTENDED MODEL REGISTRY (Placeholder)
############################################
from models_loader import EXTENDED_MODELS


############################################
# 4. OPTIONAL CUSTOM CSS
############################################
CUSTOM_CSS = """
body {
    background-color: #fafafa;
    font-family: 'Inter', sans-serif;
}
#custom-header {
    background: linear-gradient(135deg, #8EC5FC, #E0C3FC);
    border-radius: 10px;
    padding: 1rem;
    text-align: center;
    color: #fff;
}
#custom-header h1 { font-size: 2.0rem; margin-bottom: 0.3rem; }
#custom-header h3 { font-size: 1.2rem; margin-top: 0.2rem; }

.tabitem {
    background-color: #ffffff;
    border: 1px solid #ddd;
    border-radius: 8px;
    margin: 0.5rem 0;
    padding: 1rem;
}

.gradio-tab .gradio-tabitem label {
    background: linear-gradient(135deg, #ffc3a0, #ffafbd);
    color: #3c3c3c !important;
    border-radius: 5px 5px 0 0;
    margin-right: 3px;
    font-weight: 600 !important;
    font-size: 0.95rem;
}
.gradio-tab .gradio-tabitem input:checked + label {
    background: linear-gradient(135deg, #D3CCE3, #E9E4F0);
    color: #000 !important;
}
"""


############################################
# 5. BUILD GRADIO INTERFACE
############################################
import gradio as gr

def build_app():
    theme = gr.themes.Soft(
        primary_hue="indigo",
        secondary_hue="pink",
        neutral_hue="blue"
    )

    with gr.Blocks(css=CUSTOM_CSS, theme=theme) as demo:
        # 5.1 HEADER
        gr.HTML("""
        <div id="custom-header">
            <h1>GOIS vs. Full-Image Detection</h1>
            <h3>Single & Multiple Images Comparison</h3>
        </div>
        """)

        ####################################
        # 5.2. OVERVIEW TAB
        ####################################
        with gr.Tab("Overview", elem_classes="tabitem"):
            gr.Markdown("""
            ### GOIS: Granular Object Inspection Strategy
            **Granular Object Inspection Strategy (GOIS)** slices images (coarse → fine) to detect objects that might be missed in a single pass:
            - Better handling of **occlusion** or partially visible objects.
            - Fewer **boundary artifacts** when patch edges overlap.
            - Can reduce **false positives** by skipping large uniform regions.

            **Why This App?**  
            - **Single Image**: See side-by-side Full-Image (FI) detection vs. GOIS detection for one image.  
            - **Multiple Images**: Upload a batch of images and get a combined gallery + metrics table.

            **Extended Models**:  
            - YOLOv8  
            - RT-DETR-l  
            - YOLOv8s-Worldv2  
            ...and more from our custom registry.

            **Speed vs. Accuracy**:  
            - GOIS is more thorough but can be slower due to multi-slicing.  
            - Adjust slice sizes and overlaps to balance performance.

            ---
            """)

        ####################################
        # 5.3. SINGLE IMAGE TAB
        ####################################
        with gr.Tab("Single Image", elem_classes="tabitem"):
            gr.Markdown("#### Upload one image and view FI vs. GOIS outputs in the same tab")

            # Input row
            with gr.Row():
                inp_image = gr.Image(label="Upload Image", type="numpy")
                model_dd_img = gr.Dropdown(
                    label="Select Model",
                    choices=list(EXTENDED_MODELS.keys()),
                    value="YOLOv8"
                )

            # Parameter sliders
            with gr.Row():
                coarse_size_img = gr.Slider(512, 768, value=640, step=1, label="Coarse Slice Size")
                fine_size_img   = gr.Slider(128, 384, value=256, step=1, label="Fine Slice Size")
                c_overlap_img   = gr.Slider(0.1, 0.4, value=0.2, step=0.1, label="Coarse Overlap")
                f_overlap_img   = gr.Slider(0.1, 0.4, value=0.2, step=0.1, label="Fine Overlap")
                nms_thresh_img  = gr.Slider(0.3, 0.5, value=0.4, step=0.1, label="NMS Threshold")

            run_btn_img = gr.Button("Run Inference (Single Image)", variant="primary")

            # Output section in the same tab
            gr.Markdown("#### Results (FI vs. GOIS) + Metrics")
            with gr.Row():
                fi_out   = gr.Image(label="FI-Det (Baseline)")
                gois_out = gr.Image(label="GOIS-Det")

            with gr.Row():
                fi_table_html   = gr.HTML(label="FI Classes/Distribution")
                gois_table_html = gr.HTML(label="GOIS Classes/Distribution")

            with gr.Row():
                bar_chart   = gr.Image(label="Bar Chart (FI vs. GOIS)")
                fi_pie_img  = gr.Image(label="FI Pie Chart")
                gois_pie_img= gr.Image(label="GOIS Pie Chart")

            metrics_html = gr.HTML(label="Metrics Table (Single Image)")

            # Button binding
            run_btn_img.click(
                fn=run_inference,
                inputs=[
                    inp_image,
                    model_dd_img,
                    coarse_size_img,
                    fine_size_img,
                    c_overlap_img,
                    f_overlap_img,
                    nms_thresh_img
                ],
                outputs=[
                    fi_out,
                    gois_out,
                    fi_table_html,
                    gois_table_html,
                    bar_chart,
                    fi_pie_img,
                    gois_pie_img,
                    metrics_html
                ]
            )

        ####################################
        # 5.4. MULTIPLE IMAGES TAB
        ####################################
        with gr.Tab("Multiple Images", elem_classes="tabitem"):
            gr.Markdown("#### Upload multiple images and compare FI vs. GOIS on each")

            # Input row
            with gr.Row():
                batch_imgs = gr.Files(
                    label="Upload Multiple Images (JPG/PNG)",
                    file_count="multiple",
                    type="filepath"
                )
                model_dd_multi = gr.Dropdown(
                    label="Select Model",
                    choices=list(EXTENDED_MODELS.keys()),
                    value="YOLOv8"
                )

            # Parameter sliders
            with gr.Row():
                coarse_size_multi = gr.Slider(512, 768, value=640, step=1, label="Coarse Slice Size")
                fine_size_multi   = gr.Slider(128, 384, value=256, step=1, label="Fine Slice Size")
                c_overlap_multi   = gr.Slider(0.1, 0.4, value=0.2, step=0.1, label="Coarse Overlap")
                f_overlap_multi   = gr.Slider(0.1, 0.4, value=0.2, step=0.1, label="Fine Overlap")
                nms_thresh_multi  = gr.Slider(0.3, 0.5, value=0.4, step=0.1, label="NMS Threshold")

            run_btn_multi = gr.Button("Run Inference (Batch)", variant="primary")

            # Outputs in the same tab
            gr.Markdown("#### Batch Results & Metrics")
            batch_gallery = gr.Gallery(label="FI-Det vs GOIS-Det (All Images)")
            batch_metrics_html = gr.HTML(label="Batch Metrics Table")

            # Button binding
            run_btn_multi.click(
                fn=run_inference_batch,
                inputs=[
                    batch_imgs,
                    model_dd_multi,
                    coarse_size_multi,
                    fine_size_multi,
                    c_overlap_multi,
                    f_overlap_multi,
                    nms_thresh_multi
                ],
                outputs=[
                    batch_gallery,
                    batch_metrics_html
                ]
            )

        ####################################
        # 5.5. RECOMMENDED CONFIGURATIONS TAB
        ####################################
        with gr.Tab("Recommended Configs"):
            gr.Markdown("""
            ### Suggested Parameter Settings (for GOIS)

            | Config  | Coarse Size | Fine Size | Coarse Overlap | Fine Overlap | NMS Thresh |
            |---------|------------:|----------:|---------------:|-------------:|-----------:|
            | **C1**  | 512 px      | 128 px    | 0.1            | 0.1          | 0.3        |
            | **C2**  | 640 px      | 256 px    | 0.2            | 0.2          | 0.4        |
            | **C3**  | 768 px      | 384 px    | 0.3            | 0.3          | 0.5        |

            **Tips**:
            - **Coarse Size**: Larger slices → fewer patches → faster, but might miss tiny objects.
            - **Fine Size**: Smaller slices → more detailed detection → slower.
            - **Overlap**: Higher overlap → fewer boundary misses → more computations.
            - **NMS Threshold**: Lower → more aggressive overlap removal → might drop some true positives.
            """)

            gr.Markdown("""
            ### More Suggestions and Advanced Parameter Settings (for GOIS)

            | Parameter               | Recommended Values  | Expanded Ranges | Effect on Performance                                             | Handling Errors                                               |
            |-------------------------|---------------------|-----------------|-------------------------------------------------------------------|---------------------------------------------------------------|
            | **Coarse Slice Size**   | 512 - 768          | 256 - 1024      | Larger slices improve speed but may miss small objects            | If small objects are missed, reduce slice size                |
            | **Fine Slice Size**     | 128 - 384          | 64 - 512        | Smaller slices detect tiny objects better but increase computation| If too many duplicate detections occur, increase NMS          |
            | **Coarse Overlap**      | 0.2 - 0.4          | 0.1 - 0.5       | Higher overlap reduces boundary artifacts but increases processing time  | Increase overlap if objects near boundaries are missing      |
            | **Fine Overlap**        | 0.4 - 0.6          | 0.3 - 0.7       | Higher overlap helps detect occluded objects but increases computation   | Increase overlap to detect occluded objects but monitor speed |
            | **NMS Threshold**       | 0.3 - 0.5          | 0.2 - 0.6       | Lower values remove overlapping boxes but may discard true positives    | If detections overlap, lower NMS; if detections disappear, increase NMS |
            | **IoU Threshold**       | 0.4 - 0.6          | 0.3 - 0.7       | Defines the box-overlap level for merging detections                     | Lower values reduce false positives; higher values may merge distinct objects |
            | **Confidence Threshold**| 0.2 - 0.5          | 0.1 - 0.6       | Minimum confidence score required to keep detections                    | If too many false positives, increase the confidence threshold |
            | **✅ Adaptive Slicing**    | **Enabled**       | Auto-Tuned      | **Dynamically adjusts slice sizes based on object density**             | **Helps avoid redundant computations and adapts to object distribution** |
            | **✅ Multi-Scale Fusion**  | **Enabled**       | Auto-Tuned      | **Merges detections from different scales for improved accuracy**       | **Reduces false negatives for small or occluded objects**     |
            | **✅ Context Integration** | **Enabled**       | Auto-Tuned      | **Incorporates adjacent slice info to handle occlusions**               | **Reduces missing objects near slice boundaries**             |
            | **✅ Class-Aware NMS**     | **Enabled**       | Auto-Tuned      | Applies NMS per object class to preserve small-object diversity         | Helps maintain multiple overlapping objects of different classes |
            """)

    return demo


############################################
# 6. MAIN
############################################
if __name__ == "__main__":
    demo_app = build_app()
    demo_app.launch()