File size: 5,883 Bytes
bc1fb7d
b46360a
 
bc1fb7d
b46360a
bc1fb7d
b46360a
 
 
 
 
 
bc1fb7d
b46360a
 
 
 
 
 
bc1fb7d
b46360a
 
bc1fb7d
 
 
b46360a
bc1fb7d
 
 
b46360a
bc1fb7d
 
 
 
b46360a
 
bc1fb7d
b46360a
 
 
 
 
bc1fb7d
b46360a
bc1fb7d
b46360a
 
bc1fb7d
b46360a
 
 
bc1fb7d
b46360a
 
 
 
 
bc1fb7d
 
b46360a
 
bc1fb7d
b46360a
 
bc1fb7d
 
bf08742
bc1fb7d
 
 
 
bf08742
 
 
 
 
 
 
b46360a
 
bc1fb7d
b46360a
 
 
 
 
bc1fb7d
b46360a
 
bc1fb7d
 
b46360a
 
 
bc1fb7d
 
 
 
 
b46360a
 
 
bc1fb7d
b46360a
bc1fb7d
b46360a
bc1fb7d
b46360a
 
 
bc1fb7d
 
 
 
 
 
 
 
 
b46360a
 
bc1fb7d
b46360a
 
 
bc1fb7d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b46360a
 
bc1fb7d
 
 
bf08742
 
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
"""UI components for MelanoScope AI."""
import os
import logging
from typing import List
import gradio as gr
from ..config.settings import UIConfig
from ..core.utils import create_empty_dataframe
from .styles import get_custom_css, create_theme, get_header_html, get_footer_html, get_model_info_html

logger = logging.getLogger(__name__)

class MelanoScopeUI:
    """Handles UI components and layout."""
    
    def __init__(self, model_instance, classes: List[str]):
        self.model = model_instance
        self.classes = classes
        self.theme = create_theme()
        self.css = get_custom_css()
        logger.info("UI initialized")
    
    def create_interface(self) -> gr.Blocks:
        """Create complete Gradio interface."""
        with gr.Blocks(theme=self.theme, css=self.css) as interface:
            self._create_header()
            
            with gr.Row(equal_height=True):
                self._create_input_column()
                self._create_results_column()
            
            self._create_footer()
            self._setup_event_handlers()
        
        return interface
    
    def _create_header(self) -> None:
        """Create header section."""
        with gr.Row():
            with gr.Column(scale=6):
                gr.Markdown(get_header_html())
            with gr.Column(scale=1, min_width=UIConfig.THEME_TOGGLE_MIN_WIDTH):
                try:
                    self.dark_toggle = gr.ThemeMode(label="Theme", value="system")
                except Exception:
                    gr.Markdown("")
    
    def _create_input_column(self) -> None:
        """Create input column with image upload and controls."""
        with gr.Column(scale=UIConfig.LEFT_COLUMN_SCALE):
            self.image_input = gr.Image(
                type="numpy",
                label="Lesion Image",
                height=UIConfig.IMAGE_HEIGHT,
                sources=["upload", "clipboard"]
            )
            
            with gr.Row():
                self.analyze_btn = gr.Button("Analyze", variant="primary")
                self.clear_btn = gr.Button("Clear")
            
            self._create_examples_section()
            self.latency_output = gr.Label(label="Inference Time")
    
    def _create_examples_section(self) -> None:
        """Create examples section if files exist."""
        example_files = [
            "melanoma.jpg", "vascular_lesion.jpg"
        ]
        
        existing_examples = [f for f in example_files if os.path.exists(f)]
        if existing_examples:
            gr.Examples(
                examples=existing_examples,
                inputs=self.image_input,
                label="Quick Examples",
                cache_examples=False
            )

    
    def _create_results_column(self) -> None:
        """Create results column with predictions and info."""
        with gr.Column(scale=UIConfig.RIGHT_COLUMN_SCALE):
            self._create_prediction_results()
            self._create_information_tabs()
    
    def _create_prediction_results(self) -> None:
        """Create prediction results section."""
        with gr.Group():
            with gr.Row():
                self.prediction_output = gr.Label(label="Primary Prediction", elem_classes=["pred-card"])
                self.confidence_output = gr.Label(label="Confidence")
            
            self.probability_plot = gr.BarPlot(
                value=create_empty_dataframe(self.classes),
                x="item", y="probability",
                title="Probability Distribution (Top-k)",
                x_title="Class", y_title="Probability",
                vertical=False, tooltip=["item", "probability"],
                width=UIConfig.PLOT_WIDTH, height=UIConfig.PLOT_HEIGHT,
            )
    
    def _create_information_tabs(self) -> None:
        """Create information tabs."""
        with gr.Tabs():
            with gr.TabItem("Medical Details"):
                self._create_medical_details()
            with gr.TabItem("About Model"):
                gr.Markdown(get_model_info_html())
    
    def _create_medical_details(self) -> None:
        """Create medical details accordions."""
        with gr.Accordion("Description", open=True):
            self.description_output = gr.Textbox(lines=UIConfig.TEXTBOX_LINES, interactive=False)
        with gr.Accordion("Symptoms", open=False):
            self.symptoms_output = gr.Textbox(lines=UIConfig.TEXTBOX_LINES, interactive=False)
        with gr.Accordion("Causes", open=False):
            self.causes_output = gr.Textbox(lines=UIConfig.TEXTBOX_LINES, interactive=False)
        with gr.Accordion("Treatment", open=False):
            self.treatment_output = gr.Textbox(lines=UIConfig.TEXTBOX_LINES, interactive=False)
    
    def _create_footer(self) -> None:
        """Create footer section."""
        gr.Markdown(get_footer_html())
    
    def _setup_event_handlers(self) -> None:
        """Set up event handlers."""
        outputs = [
            self.prediction_output, self.confidence_output, self.description_output,
            self.symptoms_output, self.causes_output, self.treatment_output,
            self.probability_plot, self.latency_output
        ]
        
        self.analyze_btn.click(
            fn=self.model.predict, inputs=[self.image_input], 
            outputs=outputs, show_progress="full"
        )
        
        self.clear_btn.click(
            fn=self._clear_all, inputs=[], 
            outputs=[self.image_input] + outputs
        )
    
    def _clear_all(self) -> tuple:
        """Clear all inputs and outputs."""
        empty_df = create_empty_dataframe(self.classes)
        return (None, "", "", "", "", "", "", empty_df, "")