add windowing
Browse files
app.py
CHANGED
|
@@ -155,6 +155,40 @@ def to_numpy_image(image: Any) -> np.ndarray:
|
|
| 155 |
return arr.astype(np.float32)
|
| 156 |
|
| 157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
def to_display_image(image: np.ndarray) -> np.ndarray:
|
| 159 |
"""Normalise image for display purposes (uint8, 3-channel)."""
|
| 160 |
|
|
@@ -416,7 +450,11 @@ def load_dataset_sample(
|
|
| 416 |
try:
|
| 417 |
target_id = parse_target_selection(target_selection)
|
| 418 |
image, caption, meta = sample_dataset_example(subset, target_id)
|
| 419 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 420 |
target = meta.get("target")
|
| 421 |
meta_text = caption
|
| 422 |
if target is not None:
|
|
@@ -439,7 +477,7 @@ def load_dataset_sample(
|
|
| 439 |
meta_text,
|
| 440 |
pd.DataFrame(),
|
| 441 |
ground_truth_update,
|
| 442 |
-
{"image": image, "mask": meta.get("mask")},
|
| 443 |
)
|
| 444 |
except Exception as exc: # pragma: no cover - surfaced in UI
|
| 445 |
return None, f"Failed to load sample: {exc}", pd.DataFrame(), gr.update(visible=False), None
|
|
@@ -463,19 +501,24 @@ def run_inference(
|
|
| 463 |
|
| 464 |
def handle_upload_preview(
|
| 465 |
image: np.ndarray | Image.Image | None,
|
|
|
|
| 466 |
) -> Tuple[Optional[np.ndarray], str, pd.DataFrame, Dict[str, Any], Optional[Dict[str, Any]]]:
|
| 467 |
if image is None:
|
| 468 |
return None, "Please upload an image.", pd.DataFrame(), gr.update(visible=False), None
|
| 469 |
|
| 470 |
try:
|
| 471 |
np_image = to_numpy_image(image)
|
| 472 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 473 |
return (
|
| 474 |
display,
|
| 475 |
"Image uploaded. Click 'Run inference' to compute predictions.",
|
| 476 |
pd.DataFrame(),
|
| 477 |
gr.update(visible=False),
|
| 478 |
-
{"image": np_image, "mask": None},
|
| 479 |
)
|
| 480 |
except Exception as exc: # pragma: no cover - surfaced in UI
|
| 481 |
return None, f"Failed to load image: {exc}", pd.DataFrame(), gr.update(visible=False), None
|
|
|
|
| 155 |
return arr.astype(np.float32)
|
| 156 |
|
| 157 |
|
| 158 |
+
def apply_windowing(image: np.ndarray, subset: str) -> np.ndarray:
|
| 159 |
+
"""Apply CT windowing based on the dataset.
|
| 160 |
+
|
| 161 |
+
For CT images, applies window level and width transformation.
|
| 162 |
+
For MRI images (windowing=None), returns the image unchanged.
|
| 163 |
+
|
| 164 |
+
Args:
|
| 165 |
+
image: Raw image array (e.g., in Hounsfield Units for CT)
|
| 166 |
+
subset: Dataset subset name to determine windowing parameters
|
| 167 |
+
|
| 168 |
+
Returns:
|
| 169 |
+
Windowed image array
|
| 170 |
+
"""
|
| 171 |
+
windowing = DEFAULT_WINDOWINGS.get(subset)
|
| 172 |
+
|
| 173 |
+
# No windowing for MRI or unknown datasets
|
| 174 |
+
if windowing is None:
|
| 175 |
+
return image
|
| 176 |
+
|
| 177 |
+
window_level = windowing["window_level"]
|
| 178 |
+
window_width = windowing["window_width"]
|
| 179 |
+
|
| 180 |
+
# Apply CT windowing transformation
|
| 181 |
+
# Convert window level/width to min/max values
|
| 182 |
+
window_min = window_level - window_width / 2
|
| 183 |
+
window_max = window_level + window_width / 2
|
| 184 |
+
|
| 185 |
+
# Clip and normalize to [0, 1] range
|
| 186 |
+
windowed = np.clip(image, window_min, window_max)
|
| 187 |
+
windowed = (windowed - window_min) / (window_max - window_min)
|
| 188 |
+
|
| 189 |
+
return windowed.astype(np.float32)
|
| 190 |
+
|
| 191 |
+
|
| 192 |
def to_display_image(image: np.ndarray) -> np.ndarray:
|
| 193 |
"""Normalise image for display purposes (uint8, 3-channel)."""
|
| 194 |
|
|
|
|
| 450 |
try:
|
| 451 |
target_id = parse_target_selection(target_selection)
|
| 452 |
image, caption, meta = sample_dataset_example(subset, target_id)
|
| 453 |
+
|
| 454 |
+
# Apply windowing only for display, keep raw image for model inference
|
| 455 |
+
windowed_image = apply_windowing(image, subset)
|
| 456 |
+
display, mask_msg = render_image_with_mask_info(windowed_image, meta.get("mask"))
|
| 457 |
+
|
| 458 |
target = meta.get("target")
|
| 459 |
meta_text = caption
|
| 460 |
if target is not None:
|
|
|
|
| 477 |
meta_text,
|
| 478 |
pd.DataFrame(),
|
| 479 |
ground_truth_update,
|
| 480 |
+
{"image": image, "mask": meta.get("mask")}, # Store raw image for inference
|
| 481 |
)
|
| 482 |
except Exception as exc: # pragma: no cover - surfaced in UI
|
| 483 |
return None, f"Failed to load sample: {exc}", pd.DataFrame(), gr.update(visible=False), None
|
|
|
|
| 501 |
|
| 502 |
def handle_upload_preview(
|
| 503 |
image: np.ndarray | Image.Image | None,
|
| 504 |
+
subset: str,
|
| 505 |
) -> Tuple[Optional[np.ndarray], str, pd.DataFrame, Dict[str, Any], Optional[Dict[str, Any]]]:
|
| 506 |
if image is None:
|
| 507 |
return None, "Please upload an image.", pd.DataFrame(), gr.update(visible=False), None
|
| 508 |
|
| 509 |
try:
|
| 510 |
np_image = to_numpy_image(image)
|
| 511 |
+
|
| 512 |
+
# Apply windowing only for display, keep raw image for model inference
|
| 513 |
+
windowed_image = apply_windowing(np_image, subset)
|
| 514 |
+
display = to_display_image(windowed_image)
|
| 515 |
+
|
| 516 |
return (
|
| 517 |
display,
|
| 518 |
"Image uploaded. Click 'Run inference' to compute predictions.",
|
| 519 |
pd.DataFrame(),
|
| 520 |
gr.update(visible=False),
|
| 521 |
+
{"image": np_image, "mask": None}, # Store raw image for inference
|
| 522 |
)
|
| 523 |
except Exception as exc: # pragma: no cover - surfaced in UI
|
| 524 |
return None, f"Failed to load image: {exc}", pd.DataFrame(), gr.update(visible=False), None
|