ALYYAN commited on
Commit
f17446b
·
unverified ·
1 Parent(s): bd7703e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +76 -34
app.py CHANGED
@@ -1,40 +1,71 @@
1
- # app.py (Final Version - Clear History Feature Removed)
2
 
3
  import gradio as gr
4
  from pathlib import Path
5
  import asyncio
 
6
 
7
- # Import backend components
8
  from app.prediction import PredictionPipeline
9
  from app.database import add_patient_record, get_all_records
10
- # NOTE: 'clear_all_records' is no longer imported
11
 
12
  # --- Initialization ---
13
  prediction_pipeline = PredictionPipeline()
 
14
  SAMPLE_IMAGE_DIR = Path("sample_images")
15
  try:
16
  if SAMPLE_IMAGE_DIR.is_dir():
 
17
  NORMAL_SAMPLES = [str(p) for p in sorted(list((SAMPLE_IMAGE_DIR / 'NORMAL').glob('*.jpeg')))]
18
  PNEUMONIA_SAMPLES = [str(p) for p in sorted(list((SAMPLE_IMAGE_DIR / 'PNEUMONIA').glob('*.jpeg')))]
19
- else: raise FileNotFoundError
 
20
  except FileNotFoundError:
21
- print("Warning: 'sample_images' directory not found."); NORMAL_SAMPLES, PNEUMONIA_SAMPLES = [], []
 
22
 
23
- # --- Core Logic Functions ---
24
 
 
25
  async def process_analysis(patient_name, patient_age, image_list):
26
- if not patient_name or patient_age is None: raise gr.Error("Patient Name and Age are required.")
27
- if not image_list: raise gr.Error("At least one image is required.")
 
 
 
 
 
 
 
28
  result = prediction_pipeline.predict(image_list)
29
- if "error" in result: raise gr.Error(result.get("details", result["error"]))
30
- final_pred, final_conf = result["final_prediction"], result["final_confidence"]
 
 
 
 
 
31
  await add_patient_record(str(patient_name), int(patient_age), final_pred, final_conf)
32
- confidences = {"NORMAL": 0.0, "PNEUMONIA": 0.0}; confidences[final_pred] = final_conf; confidences["NORMAL" if final_pred == "PNEUMONIA" else "PNEUMONIA"] = 1 - final_conf
33
- return [gr.update(visible=False), gr.update(visible=True), gr.update(value=result["watermarked_images"]), gr.update(value=confidences)]
 
 
 
 
 
 
 
 
 
 
 
 
34
  async def refresh_history_table():
 
35
  records = await get_all_records()
36
- data = [[r.get('name'), r.get('age'), r.get('prediction_result'), f"{r.get('confidence_score', 0):.2%}", r.get('timestamp').strftime('%Y-%m-%d %H:%M')] for r in records] if records else []
37
- return gr.update(value=data)
 
 
38
 
39
  # --- Gradio UI Definition ---
40
  css = """
@@ -55,15 +86,18 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="blue")
55
  with gr.Column(elem_id="app_header"):
56
  gr.Markdown("# 🩺 Pneumonia Detection AI", elem_id="app_title")
57
  gr.Markdown("An AI-powered tool to assist in the diagnosis of pneumonia.", elem_id="app_subtitle")
 
58
  with gr.Row(elem_id="main_container"):
59
  with gr.Column(scale=1) as uploader_column:
60
  gr.Markdown("### Upload Patient X-Rays")
61
  image_input = gr.File(label="Upload up to 3 Images", file_count="multiple", file_types=["image"], type="filepath")
 
62
  with gr.Column(scale=2, visible=False) as results_column:
63
  gr.Markdown("### Analysis Results")
64
  result_images = gr.Gallery(label="Analyzed Images", columns=3, object_fit="contain", height=350, elem_id="results_gallery")
65
  result_label = gr.Label(label="Overall Prediction", num_top_classes=2)
66
  start_over_btn = gr.Button("Start New Analysis", variant="secondary")
 
67
  with gr.Group(visible=False) as patient_info_modal:
68
  gr.Markdown("## Enter Patient Details", elem_classes="text-center")
69
  patient_name_modal = gr.Textbox(label="Patient Name", placeholder="e.g., John Doe")
@@ -71,22 +105,21 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="blue")
71
  with gr.Row():
72
  submit_analysis_btn = gr.Button("Analyze Images", variant="primary")
73
  cancel_btn = gr.Button("Cancel", variant="stop")
 
74
  with gr.Column(elem_id="bottom_controls"):
75
  with gr.Accordion("About this Tool", open=False):
76
  gr.Markdown(
77
  """
78
- ### 🩺 MLOps-Powered Pneumonia Detection
79
-
80
- This project showcases a complete **end-to-end MLOps pipeline** for medical image classification.
81
- It leverages a cutting-edge **Vision Transformer (ViT)** model, fine-tuned on publicly available chest X-ray datasets, to classify images into **Normal** or **Pneumonia** cases.
82
-
83
- ⚠️ **Disclaimer:** This application is intended **solely for educational and demonstration purposes**. It is **not a medical diagnostic tool** and must not be used as a substitute for professional medical advice.
84
 
 
 
85
  ---
86
-
87
- ### 👥 Project Team
88
- - **Alyyan Ahmed** ML Engineer & Developer
89
- - **Munim Akbar** ML Engineer & Developer
90
  """
91
  )
92
  with gr.Row():
@@ -98,47 +131,56 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="blue")
98
  with gr.Row():
99
  back_to_main_btn_hist = gr.Button("⬅️ Back to Main App")
100
  refresh_history_btn = gr.Button("Refresh History")
101
- # --- "Clear History" Button REMOVED ---
102
  history_df = gr.DataFrame(headers=["Name", "Age", "Prediction", "Confidence", "Date"], row_count=10, interactive=False)
103
 
104
  with gr.Column(visible=False) as samples_page:
105
  gr.Markdown("# 🖼️ Sample Image Library", elem_classes="app_title")
106
  gr.Markdown("You can download these sample images to test the tool on the main page.")
107
  back_to_main_btn_samp = gr.Button("⬅️ Back to Main App")
 
108
  with gr.Row():
109
  with gr.Column():
110
  gr.Markdown("### Normal Cases")
111
  for img_path in NORMAL_SAMPLES:
112
  gr.File(value=img_path, label=Path(img_path).name, interactive=False)
 
113
  with gr.Column():
114
  gr.Markdown("### Pneumonia Cases")
115
  for img_path in PNEUMONIA_SAMPLES:
116
  gr.File(value=img_path, label=Path(img_path).name, interactive=False)
117
 
118
- # --- Event Handling Logic (Unchanged and Correct) ---
119
- def show_patient_info(files): return gr.update(visible=True) if files else gr.update(visible=False)
 
 
120
  image_input.upload(fn=show_patient_info, inputs=image_input, outputs=patient_info_modal)
 
121
  async def submit_and_hide_modal(name, age, files):
122
- analysis_results = await process_analysis(name, age, files); return [*analysis_results, gr.update(visible=False)]
 
123
  submit_analysis_btn.click(fn=submit_and_hide_modal, inputs=[patient_name_modal, patient_age_modal, image_input], outputs=[uploader_column, results_column, result_images, result_label, patient_info_modal])
 
124
  cancel_btn.click(lambda: (gr.update(visible=False), None), None, [patient_info_modal, image_input])
125
  start_over_btn.click(fn=None, js="() => { window.location.reload(); }")
126
 
127
  all_pages = [main_app, history_page, samples_page]
 
128
  async def show_history_page_and_refresh():
129
- records_update = await refresh_history_table(); return [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), records_update]
130
- def show_samples_page(): return [gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)]
131
- def show_main_page(): return [gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)]
 
 
132
 
 
 
 
133
  history_btn.click(fn=show_history_page_and_refresh, outputs=all_pages + [history_df])
134
  samples_btn.click(fn=show_samples_page, outputs=all_pages)
135
  back_to_main_btn_hist.click(fn=show_main_page, outputs=all_pages)
136
  back_to_main_btn_samp.click(fn=show_main_page, outputs=all_pages)
137
 
138
  refresh_history_btn.click(fn=refresh_history_table, outputs=history_df)
139
-
140
- # --- "Clear History" Logic REMOVED ---
141
-
142
  demo.load(fn=refresh_history_table, outputs=history_df)
143
 
144
  # --- Launch the App ---
 
1
+ # app.py (The Definitive Final Version)
2
 
3
  import gradio as gr
4
  from pathlib import Path
5
  import asyncio
6
+ from PIL import Image
7
 
8
+ # Import backend components from the 'app' folder
9
  from app.prediction import PredictionPipeline
10
  from app.database import add_patient_record, get_all_records
 
11
 
12
  # --- Initialization ---
13
  prediction_pipeline = PredictionPipeline()
14
+ # Point to the locally cloned sample images directory from setup.sh
15
  SAMPLE_IMAGE_DIR = Path("sample_images")
16
  try:
17
  if SAMPLE_IMAGE_DIR.is_dir():
18
+ # Separate the lists of images for the two-column layout
19
  NORMAL_SAMPLES = [str(p) for p in sorted(list((SAMPLE_IMAGE_DIR / 'NORMAL').glob('*.jpeg')))]
20
  PNEUMONIA_SAMPLES = [str(p) for p in sorted(list((SAMPLE_IMAGE_DIR / 'PNEUMONIA').glob('*.jpeg')))]
21
+ else:
22
+ raise FileNotFoundError
23
  except FileNotFoundError:
24
+ print("Warning: 'sample_images' directory not found or is empty. Please check setup.sh. Samples will be unavailable.")
25
+ NORMAL_SAMPLES, PNEUMONIA_SAMPLES = [], []
26
 
 
27
 
28
+ # --- Core Logic (Async Functions) ---
29
  async def process_analysis(patient_name, patient_age, image_list):
30
+ """
31
+ Handles the core logic: validates input, gets prediction, saves to DB, and returns UI updates.
32
+ """
33
+ # This function is now only for real analysis, not samples
34
+ if not patient_name or patient_age is None:
35
+ raise gr.Error("Patient Name and Age are required.")
36
+ if not image_list:
37
+ raise gr.Error("At least one image is required.")
38
+
39
  result = prediction_pipeline.predict(image_list)
40
+ if "error" in result:
41
+ raise gr.Error(result.get("details", result["error"]))
42
+
43
+ final_pred = result["final_prediction"]
44
+ final_conf = result["final_confidence"]
45
+
46
+ # Save the record to the database
47
  await add_patient_record(str(patient_name), int(patient_age), final_pred, final_conf)
48
+
49
+ confidences = {"NORMAL": 0.0, "PNEUMONIA": 0.0}
50
+ confidences[final_pred] = final_conf
51
+ # Calculate the other confidence score for the progress bar
52
+ confidences["NORMAL" if final_pred == "PNEUMONIA" else "PNEUMONIA"] = 1 - final_conf
53
+
54
+ # Return a list of updates for the output components
55
+ return [
56
+ gr.update(visible=False), # uploader_column
57
+ gr.update(visible=True), # results_column
58
+ gr.update(value=result["watermarked_images"]), # result_images
59
+ gr.update(value=confidences) # result_label
60
+ ]
61
+
62
  async def refresh_history_table():
63
+ """Fetches records from the DB and formats them for the DataFrame."""
64
  records = await get_all_records()
65
+ data_for_df = []
66
+ if records:
67
+ data_for_df = [[r.get('name'), r.get('age'), r.get('prediction_result'), f"{r.get('confidence_score', 0):.2%}", r.get('timestamp').strftime('%Y-%m-%d %H:%M')] for r in records]
68
+ return gr.update(value=data_for_df)
69
 
70
  # --- Gradio UI Definition ---
71
  css = """
 
86
  with gr.Column(elem_id="app_header"):
87
  gr.Markdown("# 🩺 Pneumonia Detection AI", elem_id="app_title")
88
  gr.Markdown("An AI-powered tool to assist in the diagnosis of pneumonia.", elem_id="app_subtitle")
89
+
90
  with gr.Row(elem_id="main_container"):
91
  with gr.Column(scale=1) as uploader_column:
92
  gr.Markdown("### Upload Patient X-Rays")
93
  image_input = gr.File(label="Upload up to 3 Images", file_count="multiple", file_types=["image"], type="filepath")
94
+
95
  with gr.Column(scale=2, visible=False) as results_column:
96
  gr.Markdown("### Analysis Results")
97
  result_images = gr.Gallery(label="Analyzed Images", columns=3, object_fit="contain", height=350, elem_id="results_gallery")
98
  result_label = gr.Label(label="Overall Prediction", num_top_classes=2)
99
  start_over_btn = gr.Button("Start New Analysis", variant="secondary")
100
+
101
  with gr.Group(visible=False) as patient_info_modal:
102
  gr.Markdown("## Enter Patient Details", elem_classes="text-center")
103
  patient_name_modal = gr.Textbox(label="Patient Name", placeholder="e.g., John Doe")
 
105
  with gr.Row():
106
  submit_analysis_btn = gr.Button("Analyze Images", variant="primary")
107
  cancel_btn = gr.Button("Cancel", variant="stop")
108
+
109
  with gr.Column(elem_id="bottom_controls"):
110
  with gr.Accordion("About this Tool", open=False):
111
  gr.Markdown(
112
  """
113
+ ### MLOps-Powered Pneumonia Detection
114
+ This application demonstrates a complete, end-to-end MLOps pipeline for medical image classification. It leverages a state-of-the-art **Vision Transformer (ViT)** model, fine-tuned on a public dataset of chest X-ray images to distinguish between Normal and Pneumonia cases.
 
 
 
 
115
 
116
+ **Disclaimer:** This tool is for demonstration and educational purposes only and is **not a substitute for professional medical advice.**
117
+
118
  ---
119
+
120
+ **Project Team:**
121
+ * **Alyyan Ahmed** - ML Engineer & Developer
122
+ * **Munim Akbar** - ML Engineer & Developer
123
  """
124
  )
125
  with gr.Row():
 
131
  with gr.Row():
132
  back_to_main_btn_hist = gr.Button("⬅️ Back to Main App")
133
  refresh_history_btn = gr.Button("Refresh History")
 
134
  history_df = gr.DataFrame(headers=["Name", "Age", "Prediction", "Confidence", "Date"], row_count=10, interactive=False)
135
 
136
  with gr.Column(visible=False) as samples_page:
137
  gr.Markdown("# 🖼️ Sample Image Library", elem_classes="app_title")
138
  gr.Markdown("You can download these sample images to test the tool on the main page.")
139
  back_to_main_btn_samp = gr.Button("⬅️ Back to Main App")
140
+
141
  with gr.Row():
142
  with gr.Column():
143
  gr.Markdown("### Normal Cases")
144
  for img_path in NORMAL_SAMPLES:
145
  gr.File(value=img_path, label=Path(img_path).name, interactive=False)
146
+
147
  with gr.Column():
148
  gr.Markdown("### Pneumonia Cases")
149
  for img_path in PNEUMONIA_SAMPLES:
150
  gr.File(value=img_path, label=Path(img_path).name, interactive=False)
151
 
152
+ # --- Event Handling Logic ---
153
+
154
+ def show_patient_info(files):
155
+ return gr.update(visible=True) if files else gr.update(visible=False)
156
  image_input.upload(fn=show_patient_info, inputs=image_input, outputs=patient_info_modal)
157
+
158
  async def submit_and_hide_modal(name, age, files):
159
+ analysis_results = await process_analysis(name, age, files)
160
+ return [*analysis_results, gr.update(visible=False)]
161
  submit_analysis_btn.click(fn=submit_and_hide_modal, inputs=[patient_name_modal, patient_age_modal, image_input], outputs=[uploader_column, results_column, result_images, result_label, patient_info_modal])
162
+
163
  cancel_btn.click(lambda: (gr.update(visible=False), None), None, [patient_info_modal, image_input])
164
  start_over_btn.click(fn=None, js="() => { window.location.reload(); }")
165
 
166
  all_pages = [main_app, history_page, samples_page]
167
+
168
  async def show_history_page_and_refresh():
169
+ records_update = await refresh_history_table()
170
+ return [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), records_update]
171
+
172
+ def show_samples_page():
173
+ return [gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)]
174
 
175
+ def show_main_page():
176
+ return [gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)]
177
+
178
  history_btn.click(fn=show_history_page_and_refresh, outputs=all_pages + [history_df])
179
  samples_btn.click(fn=show_samples_page, outputs=all_pages)
180
  back_to_main_btn_hist.click(fn=show_main_page, outputs=all_pages)
181
  back_to_main_btn_samp.click(fn=show_main_page, outputs=all_pages)
182
 
183
  refresh_history_btn.click(fn=refresh_history_table, outputs=history_df)
 
 
 
184
  demo.load(fn=refresh_history_table, outputs=history_df)
185
 
186
  # --- Launch the App ---