Use original online structural extractors
Browse files- submission_task1.py +69 -15
submission_task1.py
CHANGED
|
@@ -216,28 +216,82 @@ def load_model():
|
|
| 216 |
|
| 217 |
|
| 218 |
def _pil_to_tensor(image: Image.Image) -> torch.Tensor:
|
| 219 |
-
|
| 220 |
-
|
|
|
|
|
|
|
|
|
|
| 221 |
arr = np.transpose(arr, (2, 0, 1))
|
| 222 |
return torch.from_numpy(arr).float().unsqueeze(0)
|
| 223 |
|
| 224 |
|
| 225 |
-
|
| 226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
"""
|
| 233 |
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
return {
|
| 238 |
-
"prior_mask":
|
| 239 |
-
"topo_features": torch.
|
| 240 |
-
"global_features": torch.
|
| 241 |
}
|
| 242 |
|
| 243 |
|
|
@@ -245,7 +299,7 @@ def predict_one(image: Image.Image, question: str, max_new_tokens: int = 48) ->
|
|
| 245 |
model = load_model()
|
| 246 |
device = _DEVICE or _select_device()
|
| 247 |
image_tensor = _pil_to_tensor(image).to(device)
|
| 248 |
-
structural = {key: value.to(device) for key, value in
|
| 249 |
|
| 250 |
with torch.inference_mode():
|
| 251 |
prediction = model.generate(
|
|
|
|
| 216 |
|
| 217 |
|
| 218 |
def _pil_to_tensor(image: Image.Image) -> torch.Tensor:
|
| 219 |
+
import cv2
|
| 220 |
+
|
| 221 |
+
image_rgb = np.asarray(image.convert("RGB")).astype(np.uint8)
|
| 222 |
+
image_rgb = cv2.resize(image_rgb, DEFAULT_IMAGE_SIZE[::-1], interpolation=cv2.INTER_AREA)
|
| 223 |
+
arr = image_rgb.astype(np.float32) / 255.0
|
| 224 |
arr = np.transpose(arr, (2, 0, 1))
|
| 225 |
return torch.from_numpy(arr).float().unsqueeze(0)
|
| 226 |
|
| 227 |
|
| 228 |
+
_STRUCTURAL_EXTRACTORS = None
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
def _get_structural_extractors():
|
| 232 |
+
"""Create the same structural extractors used by offline precompute."""
|
| 233 |
+
|
| 234 |
+
global _STRUCTURAL_EXTRACTORS
|
| 235 |
+
if _STRUCTURAL_EXTRACTORS is not None:
|
| 236 |
+
return _STRUCTURAL_EXTRACTORS
|
| 237 |
+
|
| 238 |
+
from src.topology.lesion_prior import LesionPriorExtractor
|
| 239 |
+
from src.topology.tda_morphology import TopologicalExtractor as MorphologyTopologicalExtractor
|
| 240 |
+
|
| 241 |
+
prior_extractor = LesionPriorExtractor(
|
| 242 |
+
image_size=DEFAULT_IMAGE_SIZE,
|
| 243 |
+
grid_size=DEFAULT_GRID_SIZE,
|
| 244 |
+
use_morphology=False,
|
| 245 |
+
)
|
| 246 |
+
morpho_extractor = MorphologyTopologicalExtractor(
|
| 247 |
+
grid_size=DEFAULT_GRID_SIZE,
|
| 248 |
+
image_size=DEFAULT_IMAGE_SIZE,
|
| 249 |
+
)
|
| 250 |
+
_STRUCTURAL_EXTRACTORS = (prior_extractor, morpho_extractor)
|
| 251 |
+
return _STRUCTURAL_EXTRACTORS
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
def _extract_online_structural_tensors(image: Image.Image) -> Dict[str, torch.Tensor]:
|
| 255 |
+
"""Compute structural features online with the original precompute logic.
|
| 256 |
+
|
| 257 |
+
This mirrors `scripts/precompute_structural_features.py`:
|
| 258 |
+
prior_output = LesionPriorExtractor.extract_prior(image_bgr)
|
| 259 |
+
morpho_output = MorphologyTopologicalExtractor.extract_features(image_bgr)
|
| 260 |
|
| 261 |
+
Shapes:
|
| 262 |
+
- prior_mask: [1, 14, 14]
|
| 263 |
+
- topo_features: [1, 14, 14, 12]
|
| 264 |
+
- global_features: [1, 8]
|
| 265 |
"""
|
| 266 |
|
| 267 |
+
import cv2
|
| 268 |
+
|
| 269 |
+
prior_extractor, morpho_extractor = _get_structural_extractors()
|
| 270 |
+
|
| 271 |
+
image_rgb = np.asarray(image.convert("RGB")).astype(np.uint8)
|
| 272 |
+
image_bgr = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2BGR)
|
| 273 |
+
|
| 274 |
+
prior_output = prior_extractor.extract_prior(image_bgr)
|
| 275 |
+
morpho_output = morpho_extractor.extract_features(image_bgr)
|
| 276 |
+
|
| 277 |
+
prior_mask = prior_output["prior_mask"].astype(np.float32)
|
| 278 |
+
topo_features = morpho_output["topo_features"].astype(np.float32)
|
| 279 |
+
global_features = morpho_output["global_features"].astype(np.float32)
|
| 280 |
+
|
| 281 |
+
if prior_mask.shape != DEFAULT_GRID_SIZE:
|
| 282 |
+
raise ValueError(f"prior_mask shape {prior_mask.shape} != {DEFAULT_GRID_SIZE}")
|
| 283 |
+
expected_topo_shape = (*DEFAULT_GRID_SIZE, DEFAULT_TOPO_FEATURE_DIM)
|
| 284 |
+
if topo_features.shape != expected_topo_shape:
|
| 285 |
+
raise ValueError(f"topo_features shape {topo_features.shape} != {expected_topo_shape}")
|
| 286 |
+
if global_features.shape != (DEFAULT_GLOBAL_FEATURE_DIM,):
|
| 287 |
+
raise ValueError(
|
| 288 |
+
f"global_features shape {global_features.shape} != {(DEFAULT_GLOBAL_FEATURE_DIM,)}"
|
| 289 |
+
)
|
| 290 |
+
|
| 291 |
return {
|
| 292 |
+
"prior_mask": torch.from_numpy(prior_mask).float().unsqueeze(0),
|
| 293 |
+
"topo_features": torch.from_numpy(topo_features).float().unsqueeze(0),
|
| 294 |
+
"global_features": torch.from_numpy(global_features).float().unsqueeze(0),
|
| 295 |
}
|
| 296 |
|
| 297 |
|
|
|
|
| 299 |
model = load_model()
|
| 300 |
device = _DEVICE or _select_device()
|
| 301 |
image_tensor = _pil_to_tensor(image).to(device)
|
| 302 |
+
structural = {key: value.to(device) for key, value in _extract_online_structural_tensors(image).items()}
|
| 303 |
|
| 304 |
with torch.inference_mode():
|
| 305 |
prediction = model.generate(
|