File size: 4,623 Bytes
c0abae0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
{
  "schema_version": "1.0.0",
  "note": "The single source of truth for pixel handling. scripts/build_frame_dataset.py, scripts/train.py, src/organscan/preprocess.py and the exported ONNX graph all read this file, and scripts/export_onnx.py copies it into models/ so the shipped artifact carries its own contract. If training and serving ever disagree about normalisation, the model silently loses several points and nothing errors - hence one file, not four constants.",

  "input_name": "pixel_values",
  "dtype": "float32",
  "dtype_note": "float32 in, even for the INT8 model. Static quantization inserts QuantizeLinear at the graph head; handing it uint8 is a silent accuracy loss.",
  "layout": "NCHW",
  "channels": 3,
  "channels_note": "Always 3. Grayscale B-mode replicates across channels; colour Doppler uses them for real.",
  "image_size": 224,
  "dynamic_axes": { "batch": 0 },
  "spatial_dims_fixed": true,
  "spatial_dims_note": "H and W are fixed at 224. Dynamic spatial dims block ORT/NNAPI kernel selection and cost real latency on edge hardware.",

  "pipeline": [
    { "step": 1, "op": "decode", "detail": "Honour TransferSyntaxUID. Multi-frame ultrasound is sampled by temporal stride, never frame-by-frame." },
    { "step": 2, "op": "photometric", "detail": "Invert when PhotometricInterpretation == MONOCHROME1." },
    { "step": 3, "op": "crop_scan_region", "detail": "Ultrasound: crop to SequenceOfUltrasoundRegions (0018,6011) RegionLocationMinX0/MinY0/MaxX1/MaxY1. Non-negotiable - vendor UI text often reads the organ name outright." },
    { "step": 4, "op": "intensity", "detail": "US is already 8-bit. CT/MR apply RescaleSlope/RescaleIntercept, then the fixed window below." },
    { "step": 5, "op": "resize", "detail": "Shortest side to 224, bilinear, antialias on." },
    { "step": 6, "op": "center_crop", "detail": "224 x 224." },
    { "step": 7, "op": "to_float", "detail": "Divide by 255 into [0, 1]." },
    { "step": 8, "op": "normalize", "detail": "Subtract mean, divide by std." },
    { "step": 9, "op": "layout", "detail": "HWC -> CHW, stack to [B, 3, 224, 224]." }
  ],

  "resize": {
    "shortest_side": 224,
    "interpolation": "bilinear",
    "antialias": true
  },
  "center_crop": 224,
  "rescale_factor": 0.00392156862745098,
  "image_mean": [0.485, 0.456, 0.406],
  "image_std": [0.229, 0.224, 0.225],
  "normalization_note": "ImageNet statistics, because the student is ImageNet-initialised. scripts/export_onnx.py re-reads the chosen timm config at export time and fails if it disagrees with these values rather than letting them drift.",

  "ct_windows": {
    "note": "Fixed window per region, applied to Hounsfield units after rescale. `pseudo_rgb` stacks three windows into the three channels instead of replicating one - free accuracy on CT, and the reason channels is 3 rather than 1.",
    "default": { "center": 40, "width": 400 },
    "abdomen": { "center": 40, "width": 400 },
    "chest": { "center": -600, "width": 1500 },
    "head": { "center": 40, "width": 80 },
    "extremity": { "center": 300, "width": 1500 },
    "pseudo_rgb": {
      "enabled": true,
      "channels": [
        { "center": 40, "width": 400, "label": "soft_tissue" },
        { "center": -600, "width": 1500, "label": "lung" },
        { "center": 300, "width": 1500, "label": "bone" }
      ]
    }
  },

  "mr_intensity": {
    "note": "MR has no absolute scale, so a fixed window is meaningless. Percentile clip then min-max into [0,1].",
    "clip_percentiles": [0.5, 99.5]
  },

  "ultrasound": {
    "require_region_crop": true,
    "require_region_crop_note": "A US frame without a successful region crop is rejected by scripts/build_frame_dataset.py rather than used. Burned-in annotation is a label leak that produces a 99% model which fails on every other scanner (plan section 8.1).",
    "frames_per_loop": { "min": 5, "max": 20, "selection": "temporal_stride" }
  },

  "train_augmentation": {
    "note": "Deliberately mild. Anatomy classification is left/right sensitive - a horizontal flip turns KIDNEY_LEFT into KIDNEY_RIGHT, so it is off everywhere.",
    "horizontal_flip": 0.0,
    "horizontal_flip_note": "Zero, permanently. Enabling it silently destroys every laterality label in the corpus.",
    "vertical_flip": 0.0,
    "random_resized_crop": { "enabled": true, "scale": [0.7, 1.0], "ratio": [0.85, 1.18] },
    "rotation_degrees": 10,
    "brightness": 0.2,
    "contrast": 0.2,
    "gamma": [0.8, 1.25],
    "gaussian_noise_std": 0.02,
    "speckle_noise": { "enabled": true, "modalities": ["US"], "std": 0.05 },
    "random_erasing": 0.1
  }
}