Morelli001 commited on
Commit
bfeb81e
·
verified ·
1 Parent(s): 749251c

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +7 -2
  2. image_processing_cond_unet.py +10 -20
  3. pipeline.py +4 -2
README.md CHANGED
@@ -54,13 +54,18 @@ result = segmenter("ultrasound.png", organ_id=3)
54
  If `organ_id` is not provided, the model automatically uses `-1`, matching the
55
  unknown-organ conditioning used in training.
56
 
 
 
 
 
57
  ## Output
58
 
59
  The pipeline applies a sigmoid to the foreground logit and returns a binary
60
- PIL mask thresholded at 0.5. Change the threshold if needed:
 
61
 
62
  ```python
63
- result = segmenter("ultrasound.png", threshold=0.4)
64
  ```
65
 
66
  ## Preprocessing
 
54
  If `organ_id` is not provided, the model automatically uses `-1`, matching the
55
  unknown-organ conditioning used in training.
56
 
57
+ Known organ IDs are: appendix `0`, breast `1`, cardiac `2`, thyroid `3`, fetal
58
+ `4`, kidney `5`, liver `6`, and testicle `7`. To reproduce the original
59
+ evaluation pipeline for a Fetal HC image, use `organ_id=4`.
60
+
61
  ## Output
62
 
63
  The pipeline applies a sigmoid to the foreground logit and returns a binary
64
+ PIL mask thresholded at 0.7, matching the original evaluation pipeline. Change
65
+ the threshold if needed:
66
 
67
  ```python
68
+ result = segmenter("ultrasound.png", threshold=0.7)
69
  ```
70
 
71
  ## Preprocessing
image_processing_cond_unet.py CHANGED
@@ -2,8 +2,8 @@ from typing import Optional, Union
2
 
3
  import numpy as np
4
  import torch
5
- import torch.nn.functional as F
6
  from PIL import Image
 
7
  from transformers.image_processing_utils import BaseImageProcessor, BatchFeature
8
 
9
 
@@ -51,32 +51,22 @@ class CondUNetImageProcessor(BaseImageProcessor):
51
  image = image.expand(3, -1, -1)
52
  if image.shape[0] != 3:
53
  raise ValueError("Cond-UNet requires one or three input channels.")
54
- image = image.to(dtype=torch.float32)
55
- if image.max() <= 1:
56
- image = image * 255.0
57
  height, width = image.shape[-2:]
58
  if self.keep_aspect_ratio:
59
- scale = self.image_size / max(height, width)
60
- new_height = int(height * scale)
61
- new_width = int(width * scale)
62
  new_height += new_height % 2
63
  new_width += new_width % 2
64
- image = F.interpolate(
65
- image.unsqueeze(0),
66
- size=(new_height, new_width),
67
- mode="bilinear",
68
- align_corners=False,
69
- ).squeeze(0)
70
  pad_left = (self.image_size - new_width) // 2
71
  pad_top = (self.image_size - new_height) // 2
72
- image = F.pad(image, (pad_left, self.image_size - new_width - pad_left, pad_top, self.image_size - new_height - pad_top))
73
  else:
74
- image = F.interpolate(
75
- image.unsqueeze(0),
76
- size=(self.image_size, self.image_size),
77
- mode="bilinear",
78
- align_corners=False,
79
- ).squeeze(0)
80
  mean = torch.tensor(self.mean, dtype=image.dtype).view(-1, 1, 1)
81
  std = torch.tensor(self.std, dtype=image.dtype).view(-1, 1, 1)
82
  return (image - mean) / std
 
2
 
3
  import numpy as np
4
  import torch
 
5
  from PIL import Image
6
+ from torchvision.transforms import v2
7
  from transformers.image_processing_utils import BaseImageProcessor, BatchFeature
8
 
9
 
 
51
  image = image.expand(3, -1, -1)
52
  if image.shape[0] != 3:
53
  raise ValueError("Cond-UNet requires one or three input channels.")
 
 
 
54
  height, width = image.shape[-2:]
55
  if self.keep_aspect_ratio:
56
+ resize_factor = max(height, width) / self.image_size
57
+ new_height = int(height / resize_factor)
58
+ new_width = int(width / resize_factor)
59
  new_height += new_height % 2
60
  new_width += new_width % 2
61
+ image = v2.functional.resize(image, [new_height, new_width])
 
 
 
 
 
62
  pad_left = (self.image_size - new_width) // 2
63
  pad_top = (self.image_size - new_height) // 2
64
+ image = v2.functional.pad(image, fill=0, padding=[pad_left, pad_top])
65
  else:
66
+ image = v2.functional.resize(image, [self.image_size, self.image_size])
67
+ image = image.to(dtype=torch.float32)
68
+ if image.max() <= 1:
69
+ image = image * 255.0
 
 
70
  mean = torch.tensor(self.mean, dtype=image.dtype).view(-1, 1, 1)
71
  std = torch.tensor(self.std, dtype=image.dtype).view(-1, 1, 1)
72
  return (image - mean) / std
pipeline.py CHANGED
@@ -42,9 +42,11 @@ class CondUNetImageSegmentationPipeline(Pipeline):
42
  outputs = self.model(**model_inputs)
43
  return {"logits": outputs.logits, "original_size": original_size}
44
 
45
- def postprocess(self, model_outputs, threshold=0.5, **kwargs):
46
  logits = model_outputs["logits"]
47
  height, width = model_outputs["original_size"]
48
- probabilities = torch.sigmoid(F.interpolate(logits, size=(height, width), mode="bilinear", align_corners=False))[0, 0]
 
 
49
  mask = (probabilities >= threshold).to(torch.uint8).cpu().numpy() * 255
50
  return {"label": "foreground", "mask": Image.fromarray(mask), "score": float(probabilities.mean())}
 
42
  outputs = self.model(**model_inputs)
43
  return {"logits": outputs.logits, "original_size": original_size}
44
 
45
+ def postprocess(self, model_outputs, threshold=0.7, **kwargs):
46
  logits = model_outputs["logits"]
47
  height, width = model_outputs["original_size"]
48
+ probabilities = torch.sigmoid(
49
+ F.interpolate(logits, size=(height, width), mode="nearest")
50
+ )[0, 0]
51
  mask = (probabilities >= threshold).to(torch.uint8).cpu().numpy() * 255
52
  return {"label": "foreground", "mask": Image.fromarray(mask), "score": float(probabilities.mean())}