Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -13,16 +13,16 @@ from PIL import Image
|
|
| 13 |
# ==============================================================================
|
| 14 |
IMG_SIZE = 128
|
| 15 |
LABELS = [
|
| 16 |
-
"Adenocarcinoma",
|
| 17 |
-
"Large Cell Carcinoma",
|
| 18 |
-
"Normal
|
| 19 |
-
"Squamous Cell Carcinoma"
|
| 20 |
]
|
| 21 |
|
| 22 |
class MedicalCLAHEEqualization(object):
|
| 23 |
"""
|
| 24 |
-
Applies
|
| 25 |
-
|
| 26 |
"""
|
| 27 |
def __call__(self, img):
|
| 28 |
img_np = np.array(img)
|
|
@@ -31,7 +31,7 @@ class MedicalCLAHEEqualization(object):
|
|
| 31 |
equalized = clahe.apply(gray)
|
| 32 |
return Image.fromarray(cv2.cvtColor(equalized, cv2.COLOR_GRAY2RGB))
|
| 33 |
|
| 34 |
-
#
|
| 35 |
eval_transforms = transforms.Compose([
|
| 36 |
MedicalCLAHEEqualization(),
|
| 37 |
transforms.Resize((IMG_SIZE, IMG_SIZE)),
|
|
@@ -39,12 +39,11 @@ eval_transforms = transforms.Compose([
|
|
| 39 |
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
|
| 40 |
])
|
| 41 |
|
| 42 |
-
device = torch.device("cpu")
|
| 43 |
|
| 44 |
def load_bioset_cccm():
|
| 45 |
"""
|
| 46 |
-
|
| 47 |
-
and binds the trained Bioset CCCM parameter weights.
|
| 48 |
"""
|
| 49 |
model = models.efficientnet_b0(weights=None)
|
| 50 |
in_features = model.classifier[1].in_features
|
|
@@ -53,7 +52,7 @@ def load_bioset_cccm():
|
|
| 53 |
nn.Linear(in_features, len(LABELS))
|
| 54 |
)
|
| 55 |
|
| 56 |
-
#
|
| 57 |
if os.path.exists('chestnet_efficientnet_weights.pth'):
|
| 58 |
model.load_state_dict(torch.load('chestnet_efficientnet_weights.pth', map_location=device))
|
| 59 |
elif os.path.exists('chestnet_efficientnet_full.pth'):
|
|
@@ -66,17 +65,17 @@ def load_bioset_cccm():
|
|
| 66 |
model.eval()
|
| 67 |
return model
|
| 68 |
|
| 69 |
-
# Initialize the
|
| 70 |
model = load_bioset_cccm()
|
| 71 |
|
| 72 |
# ==============================================================================
|
| 73 |
-
# 2.
|
| 74 |
# ==============================================================================
|
| 75 |
def predict_chest_slice(input_image):
|
| 76 |
if input_image is None:
|
| 77 |
-
return "###
|
| 78 |
|
| 79 |
-
# Convert
|
| 80 |
pil_img = Image.fromarray(input_image.astype('uint8'), 'RGB')
|
| 81 |
tensor_input = eval_transforms(pil_img).unsqueeze(0).to(device)
|
| 82 |
|
|
@@ -84,94 +83,93 @@ def predict_chest_slice(input_image):
|
|
| 84 |
logits = model(tensor_input)
|
| 85 |
probabilities = F.softmax(logits, dim=1).squeeze(0).numpy()
|
| 86 |
|
| 87 |
-
#
|
| 88 |
indexed_results = [
|
| 89 |
{"label": LABELS[i], "prob": float(probabilities[i])}
|
| 90 |
for i in range(len(LABELS))
|
| 91 |
]
|
| 92 |
indexed_results.sort(key=lambda x: x["prob"], reverse=True)
|
| 93 |
|
| 94 |
-
#
|
| 95 |
top_winner = indexed_results[0]
|
| 96 |
-
output_summary = f"###
|
| 97 |
|
| 98 |
-
# Return
|
| 99 |
-
gradio_label_output = {item["label"]: item["prob"] for item in indexed_results
|
| 100 |
|
| 101 |
return output_summary, gradio_label_output
|
| 102 |
|
| 103 |
# ==============================================================================
|
| 104 |
-
# 3.
|
| 105 |
# ==============================================================================
|
| 106 |
-
with gr.Blocks(title="Bioset CCCM
|
| 107 |
|
| 108 |
-
#
|
| 109 |
gr.Markdown(
|
| 110 |
"""
|
| 111 |
# Bioset CCCM
|
| 112 |
-
### Part of the **Bioset Model Collection** by **Infinitode** โข
|
| 113 |
---
|
| 114 |
"""
|
| 115 |
)
|
| 116 |
|
| 117 |
-
#
|
| 118 |
gr.Markdown(
|
| 119 |
"""
|
| 120 |
-
> โ ๏ธ **
|
| 121 |
-
> The Chest Cancer Classification Model (CCCM) is
|
| 122 |
"""
|
| 123 |
)
|
| 124 |
|
| 125 |
-
# Interactive
|
| 126 |
with gr.Row():
|
| 127 |
-
#
|
| 128 |
with gr.Column(scale=5):
|
| 129 |
-
gr.Markdown("#### ๐ฅ
|
| 130 |
|
| 131 |
input_image = gr.Image(
|
| 132 |
-
label="
|
| 133 |
sources=["upload", "clipboard", "webcam"],
|
| 134 |
type="numpy"
|
| 135 |
)
|
| 136 |
|
| 137 |
with gr.Row():
|
| 138 |
-
clear_btn = gr.Button("Clear
|
| 139 |
-
submit_btn = gr.Button("
|
| 140 |
|
| 141 |
-
#
|
| 142 |
with gr.Column(scale=7):
|
| 143 |
-
gr.Markdown("#### ๐
|
| 144 |
|
| 145 |
-
output_text = gr.Markdown("### Awaiting
|
| 146 |
|
| 147 |
output_labels = gr.Label(
|
| 148 |
-
num_top_classes=
|
| 149 |
-
label="
|
| 150 |
)
|
| 151 |
|
| 152 |
gr.Markdown(
|
| 153 |
"""
|
| 154 |
-
* **
|
| 155 |
"""
|
| 156 |
)
|
| 157 |
|
| 158 |
-
#
|
| 159 |
-
with gr.Accordion("๐ Model Profile & Performance
|
| 160 |
gr.Markdown(
|
| 161 |
"""
|
| 162 |
-
###
|
| 163 |
-
|
| 164 |
|
| 165 |
-
|
|
| 166 |
| :--- | :--- | :--- |
|
| 167 |
-
| **
|
| 168 |
-
| **
|
| 169 |
-
| **
|
| 170 |
-
| **Model Footprint Volumetrics** | **16.6 MB** | Unified monolithic serialization array |
|
| 171 |
"""
|
| 172 |
)
|
| 173 |
|
| 174 |
-
#
|
| 175 |
submit_btn.click(
|
| 176 |
fn=predict_chest_slice,
|
| 177 |
inputs=[input_image],
|
|
@@ -179,7 +177,7 @@ with gr.Blocks(title="Bioset CCCM Engine Deck") as demo:
|
|
| 179 |
)
|
| 180 |
|
| 181 |
clear_btn.click(
|
| 182 |
-
fn=lambda: ("### Awaiting
|
| 183 |
inputs=None,
|
| 184 |
outputs=[output_text, output_labels]
|
| 185 |
)
|
|
|
|
| 13 |
# ==============================================================================
|
| 14 |
IMG_SIZE = 128
|
| 15 |
LABELS = [
|
| 16 |
+
"Adenocarcinoma (Lung Cancer Type)",
|
| 17 |
+
"Large Cell Carcinoma (Lung Cancer Type)",
|
| 18 |
+
"Normal Lung (No Cancer Detected)",
|
| 19 |
+
"Squamous Cell Carcinoma (Lung Cancer Type)"
|
| 20 |
]
|
| 21 |
|
| 22 |
class MedicalCLAHEEqualization(object):
|
| 23 |
"""
|
| 24 |
+
Applies standard contrast adjustments to neutralize image lighting,
|
| 25 |
+
ensuring the model looks only at scan shapes.
|
| 26 |
"""
|
| 27 |
def __call__(self, img):
|
| 28 |
img_np = np.array(img)
|
|
|
|
| 31 |
equalized = clahe.apply(gray)
|
| 32 |
return Image.fromarray(cv2.cvtColor(equalized, cv2.COLOR_GRAY2RGB))
|
| 33 |
|
| 34 |
+
# Standard image sizing and color adjustments matching the model's training
|
| 35 |
eval_transforms = transforms.Compose([
|
| 36 |
MedicalCLAHEEqualization(),
|
| 37 |
transforms.Resize((IMG_SIZE, IMG_SIZE)),
|
|
|
|
| 39 |
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
|
| 40 |
])
|
| 41 |
|
| 42 |
+
device = torch.device("cpu")
|
| 43 |
|
| 44 |
def load_bioset_cccm():
|
| 45 |
"""
|
| 46 |
+
Loads the underlying architecture and fills it with our trained weights.
|
|
|
|
| 47 |
"""
|
| 48 |
model = models.efficientnet_b0(weights=None)
|
| 49 |
in_features = model.classifier[1].in_features
|
|
|
|
| 52 |
nn.Linear(in_features, len(LABELS))
|
| 53 |
)
|
| 54 |
|
| 55 |
+
# Safely load the saved training weights file
|
| 56 |
if os.path.exists('chestnet_efficientnet_weights.pth'):
|
| 57 |
model.load_state_dict(torch.load('chestnet_efficientnet_weights.pth', map_location=device))
|
| 58 |
elif os.path.exists('chestnet_efficientnet_full.pth'):
|
|
|
|
| 65 |
model.eval()
|
| 66 |
return model
|
| 67 |
|
| 68 |
+
# Initialize the model
|
| 69 |
model = load_bioset_cccm()
|
| 70 |
|
| 71 |
# ==============================================================================
|
| 72 |
+
# 2. IMAGE PREDICTION PIPELINE
|
| 73 |
# ==============================================================================
|
| 74 |
def predict_chest_slice(input_image):
|
| 75 |
if input_image is None:
|
| 76 |
+
return "### Please upload a scan to begin...", {}
|
| 77 |
|
| 78 |
+
# Convert image array into a standard image format
|
| 79 |
pil_img = Image.fromarray(input_image.astype('uint8'), 'RGB')
|
| 80 |
tensor_input = eval_transforms(pil_img).unsqueeze(0).to(device)
|
| 81 |
|
|
|
|
| 83 |
logits = model(tensor_input)
|
| 84 |
probabilities = F.softmax(logits, dim=1).squeeze(0).numpy()
|
| 85 |
|
| 86 |
+
# Sort results from highest probability to lowest
|
| 87 |
indexed_results = [
|
| 88 |
{"label": LABELS[i], "prob": float(probabilities[i])}
|
| 89 |
for i in range(len(LABELS))
|
| 90 |
]
|
| 91 |
indexed_results.sort(key=lambda x: x["prob"], reverse=True)
|
| 92 |
|
| 93 |
+
# Create the top result header text
|
| 94 |
top_winner = indexed_results[0]
|
| 95 |
+
output_summary = f"### Main Finding: **{top_winner['label']}** ({top_winner['prob']*100:.1f}% Match)"
|
| 96 |
|
| 97 |
+
# FIXED: Return all 4 conditions to show the full picture to the user
|
| 98 |
+
gradio_label_output = {item["label"]: item["prob"] for item in indexed_results}
|
| 99 |
|
| 100 |
return output_summary, gradio_label_output
|
| 101 |
|
| 102 |
# ==============================================================================
|
| 103 |
+
# 3. USER INTERFACE LAYOUT
|
| 104 |
# ==============================================================================
|
| 105 |
+
with gr.Blocks(title="Bioset CCCM Lung Analysis") as demo:
|
| 106 |
|
| 107 |
+
# Title Block
|
| 108 |
gr.Markdown(
|
| 109 |
"""
|
| 110 |
# Bioset CCCM
|
| 111 |
+
### Part of the **Bioset Model Collection** by **Infinitode** โข Artificial Intelligence + Biology
|
| 112 |
---
|
| 113 |
"""
|
| 114 |
)
|
| 115 |
|
| 116 |
+
# Simplified Medical Disclaimer
|
| 117 |
gr.Markdown(
|
| 118 |
"""
|
| 119 |
+
> โ ๏ธ **Important Safety Notice**
|
| 120 |
+
> The Chest Cancer Classification Model (CCCM) is an experimental prototype built purely for educational and scientific research. It is **not a certified medical tool**, does not have FDA clearance, and must never be used to diagnose medical conditions or replace the expert advice of a real doctor or radiologist.
|
| 121 |
"""
|
| 122 |
)
|
| 123 |
|
| 124 |
+
# Main Interactive Split Screen
|
| 125 |
with gr.Row():
|
| 126 |
+
# Left Side: Image Upload Controls
|
| 127 |
with gr.Column(scale=5):
|
| 128 |
+
gr.Markdown("#### ๐ฅ Step 1: Upload Lung Scan")
|
| 129 |
|
| 130 |
input_image = gr.Image(
|
| 131 |
+
label="Chest CT Scan Slice",
|
| 132 |
sources=["upload", "clipboard", "webcam"],
|
| 133 |
type="numpy"
|
| 134 |
)
|
| 135 |
|
| 136 |
with gr.Row():
|
| 137 |
+
clear_btn = gr.Button("Clear Image", variant="secondary")
|
| 138 |
+
submit_btn = gr.Button("Analyze Scan", variant="primary")
|
| 139 |
|
| 140 |
+
# Right Side: Results Presentation
|
| 141 |
with gr.Column(scale=7):
|
| 142 |
+
gr.Markdown("#### ๐ Step 2: Analysis Results")
|
| 143 |
|
| 144 |
+
output_text = gr.Markdown("### Awaiting image upload...")
|
| 145 |
|
| 146 |
output_labels = gr.Label(
|
| 147 |
+
num_top_classes=4, # FIXED: Set to 4 to ensure all conditions are shown every time
|
| 148 |
+
label="Match Likelihood for Each Condition"
|
| 149 |
)
|
| 150 |
|
| 151 |
gr.Markdown(
|
| 152 |
"""
|
| 153 |
+
* **How it works:** This app automatically smooths out lighting and shadows on your uploaded scan. This makes it easier for the AI to focus entirely on the physical shapes and textures in the lung tissue rather than the brightness settings of the scanner machine.
|
| 154 |
"""
|
| 155 |
)
|
| 156 |
|
| 157 |
+
# Performance Stats Accordion
|
| 158 |
+
with gr.Accordion("๐ Model Profile & Performance Summary", open=True):
|
| 159 |
gr.Markdown(
|
| 160 |
"""
|
| 161 |
+
### Accuracy & Performance Testing Data (CCCM v1.4)
|
| 162 |
+
These scores show how well this model performed during final testing on our verified lung scan dataset:
|
| 163 |
|
| 164 |
+
| Test Category | Score | What it means |
|
| 165 |
| :--- | :--- | :--- |
|
| 166 |
+
| **Overall Reliability** | **93.06%** | The average accuracy rate during final verification tests. |
|
| 167 |
+
| **Highest Match Certainty** | **98.7%** | The maximum confidence score achieved on clear positive scans. |
|
| 168 |
+
| **App File Size** | **16.6 MB** | A highly compressed, efficient size designed to load and run quickly. |
|
|
|
|
| 169 |
"""
|
| 170 |
)
|
| 171 |
|
| 172 |
+
# Connect the buttons to our prediction function
|
| 173 |
submit_btn.click(
|
| 174 |
fn=predict_chest_slice,
|
| 175 |
inputs=[input_image],
|
|
|
|
| 177 |
)
|
| 178 |
|
| 179 |
clear_btn.click(
|
| 180 |
+
fn=lambda: ("### Awaiting image upload...", {}),
|
| 181 |
inputs=None,
|
| 182 |
outputs=[output_text, output_labels]
|
| 183 |
)
|