File size: 6,385 Bytes
4697ca4
 
 
 
 
 
3834351
4768cde
dd1d7f5
a32df56
4c1c4a7
3834351
668a993
 
 
 
 
 
 
 
 
 
484130e
 
 
668a993
 
 
f8ac29e
 
668a993
 
 
 
f8ac29e
 
ca48751
916b83d
668a993
 
916b83d
4768cde
916b83d
2716edf
49abd9f
916b83d
2716edf
916b83d
2716edf
 
 
 
 
7c31b44
0153931
 
668a993
60e6efb
3c8af25
 
 
 
 
 
 
 
 
 
 
60e6efb
f8ac29e
60e6efb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3834351
916b83d
02ce968
e88ed7d
2592b1e
 
 
 
 
 
 
 
 
 
2716edf
668a993
e768711
d807150
 
916b83d
 
 
 
 
 
668a993
 
 
 
 
d807150
e768711
668a993
2a055cf
 
668a993
2a055cf
e768711
5f6c42c
3c8af25
c170961
 
2a055cf
c170961
 
93d0941
 
2a055cf
 
e768711
2a055cf
668a993
2a055cf
1bf989c
2a055cf
 
e768711
 
916b83d
 
668a993
2a055cf
 
 
 
 
 
 
 
 
 
 
 
916b83d
3834351
 
69b6a19
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
import os
# Force cache to /tmp before any imports
os.environ['HF_HOME'] = '/tmp/huggingface'
os.environ['TRANSFORMERS_CACHE'] = '/tmp/huggingface/transformers'
os.environ['TORCH_HOME'] = '/tmp/torch'

import gradio as gr
import tempfile
from pathlib import Path
from wrapper import run_pipeline_on_image
from PIL import Image

# Base directory for preset images
BASE_DIR = Path(__file__).resolve().parent

# Preset images available for selection
PRESET_IMAGES = {
    "Sorghum": str(BASE_DIR / "Sorghum.tif"),
    "Corn": str(BASE_DIR / "Corn.tif"),
    "Cotton": str(BASE_DIR / "Cotton.tif"),
}

# Path to header logo
LOGO_PATH = str(BASE_DIR / "logo.png")


def process(file_path, preset_choice):
    """Process image and yield results progressively for immediate display."""
    # Determine dataset type (single-plant mode for Corn, multi-plant for others)
    single_plant_mode = False
    if preset_choice:
        chosen = PRESET_IMAGES.get(preset_choice)
        if chosen:
            file_path = chosen
            # Corn uses single-plant mode
            single_plant_mode = (preset_choice == "Corn")

    if not file_path:
        # Return 10 outputs (removed YOLO tips)
        return None, None, None, None, None, None, None, [], None, ""

    with tempfile.TemporaryDirectory() as tmpdir:
        src = Path(file_path)
        ext = src.suffix.lstrip('.') or 'tif'
        img_path = Path(tmpdir) / f"input.{ext}"

        try:
            # Copy raw uploaded bytes
            img_bytes = src.read_bytes()
            img_path.write_bytes(img_bytes)
        except Exception:
            # Fallback: save via PIL if direct copy fails
            Image.open(src).save(img_path)

        # Don't show immediate preview - wait for pipeline's InputImage for correctness
        input_preview = None

        # Helper to load PIL images
        def load_pil(path_str):
            try:
                if not path_str:
                    return None
                im = Image.open(path_str)
                copied = im.copy()
                im.close()
                return copied
            except Exception:
                return None

        # Run the pipeline progressively (generator)
        for outputs in run_pipeline_on_image(str(img_path), tmpdir, save_artifacts=True, single_plant_mode=single_plant_mode):
            # Load all available outputs progressively
            composite = load_pil(outputs.get('Composite'))
            overlay = load_pil(outputs.get('Overlay'))
            mask = load_pil(outputs.get('Mask'))
            input_img = load_pil(outputs.get('InputImage')) or input_preview
            size_img = load_pil(str(Path(tmpdir) / 'results/size.size_analysis.png'))

            # Texture images (green band)
            lbp_path = Path(tmpdir) / 'texture_output/lbp_green.png'
            hog_path = Path(tmpdir) / 'texture_output/hog_green.png'
            lac1_path = Path(tmpdir) / 'texture_output/lac1_green.png'
            texture_img = load_pil(str(lbp_path)) if lbp_path.exists() else None
            hog_img = load_pil(str(hog_path)) if hog_path.exists() else None
            lac1_img = load_pil(str(lac1_path)) if lac1_path.exists() else None

            # Vegetation indices
            order = ['NDVI', 'GNDVI', 'SAVI']
            gallery_items = [load_pil(outputs[k]) for k in order if k in outputs]

            stats_text = outputs.get('StatsText', '')

            # Yield intermediate/final results as they become available
            yield (
                input_img,
                composite,
                mask,
                overlay,
                texture_img,
                hog_img,
                lac1_img,
                gallery_items,
                size_img,
                stats_text,
            )


with gr.Blocks(fill_width=True) as demo:
    # Header logo (no share/download/fullscreen buttons), full width
    gr.Image(
        value=LOGO_PATH,
        show_label=False,
        interactive=False,
        height=80,
        container=False,
        show_download_button=False,
        show_share_button=False,
        show_fullscreen_button=False,
    )
    gr.Markdown("# 🌿 Automated Plant Analysis Demo")
    gr.Markdown("Upload a plant image (TIFF preferred) to compute and visualize composite, mask, overlay, texture, vegetation indices, and statistics.")

    with gr.Row():
        with gr.Column():
            # Use File input to preserve raw TIFFs
            inp = gr.File(
                type="filepath",
                file_types=[".tif", ".tiff", ".png", ".jpg"],
                label="Upload Image"
            )
            preset = gr.Radio(
                choices=list(PRESET_IMAGES.keys()),
                label="Or choose a preset image",
                value=None
            )
            run = gr.Button("Run Pipeline", variant="primary")

    # Row 1: input image
    with gr.Row():
        input_img = gr.Image(type="pil", label="Input Image", interactive=False, height=380)
    
    # Row 2: composite, mask, overlay
    with gr.Row():
        composite_img = gr.Image(type="pil", label="Composite (Segmentation Input)", interactive=False)
        mask_img = gr.Image(type="pil", label="Mask", interactive=False)
        overlay_img = gr.Image(type="pil", label="Segmentation Overlay", interactive=False)

    # Row 3: textures
    with gr.Row():
        texture_img = gr.Image(type="pil", label="Texture LBP (Green Band)", interactive=False)
        hog_img = gr.Image(type="pil", label="Texture HOG (Green Band)", interactive=False)
        lac1_img = gr.Image(type="pil", label="Texture Lac1 (Green Band)", interactive=False)
    
    # Row 4: vegetation indices
    gallery = gr.Gallery(label="Vegetation Indices", columns=3, height="auto")

    # Row 5: morphology size (YOLO removed)
    with gr.Row():
        size_img = gr.Image(type="pil", label="Plant Morphology", interactive=False)

    # Final: statistics table
    stats = gr.Textbox(label="Statistics", lines=4)

    run.click(
        process,
        inputs=[inp, preset],
        outputs=[
            input_img,
            composite_img,
            mask_img,
            overlay_img,
            texture_img,
            hog_img,
            lac1_img,
            gallery,
            size_img,
            stats,
        ]
    )

if __name__ == "__main__":
    demo.launch()