spacr.plaque¶
Well detection and physical scale for the plaque assay.
A plaque assay is counted from images that arrive in two shapes, and they need different handling:
one plaque field per image – segment it and count, which is what
spacr.submodules.analyze_plaques()has always done;several wells in one image – a whole plate, or a strip. Segmenting that directly counts every plaque in every well into one number and loses which well each came from, which is the entire experiment.
This module supplies the front half for the second case: find the wells, then hand each one to the segmenter separately.
WHY THE WELL IS MEASURED AND NOT JUST CROPPED. Plaque area in pixels is a property of the microscope, not of the biology. The same plaque imaged at two magnifications gives two areas, and a study that pools them is comparing optics. A well, by contrast, is a manufactured object of known physical size – a 6-well plate well is 34.8 mm whatever images it. So the well’s diameter in pixels is a ruler that is present in the image itself, and dividing by it turns every area into a physical one that can be pooled across microscopes, objectives and days.
That is why detect_wells() returns a diameter rather than only a box, and
why scale_from_well() is the piece the analysis actually consumes.
Classes¶
Pixels-to-millimetres for one well, and what it was derived from. |
|
One detected well. |
Functions¶
|
The image inside one well. |
|
Find the wells in one image. |
|
The flow picture and cell probability out of a Cellpose result. |
|
Pixels-per-millimetre from a detected well, or |
|
Segment one plaque image the way both Plaque mode's preview and run do. |
Module Contents¶
- class spacr.plaque.PlaqueScale[source]¶
Pixels-to-millimetres for one well, and what it was derived from.
- Parameters:
px_per_mm – pixels per millimetre.
well_diameter_px – the measured diameter the scale came from.
well_diameter_mm – the physical diameter it was compared against.
source – how
well_diameter_mmwas decided – a plate format name, or"explicit".
- class spacr.plaque.Well[source]¶
One detected well.
- Parameters:
x0 – left edge in pixels.
y0 – top edge in pixels.
x1 – right edge in pixels.
y1 – bottom edge in pixels.
confidence – the detector’s score for this box.
- as_dict() Dict[str, Any][source]¶
The box plus its derived measures, for a results table.
- Returns:
The stored coordinates and confidence plus derived
diameter_pxandaxis_ratiovalues.
- property axis_ratio: float[source]¶
Shorter box side over longer, so 1.0 is square.
THE HONESTY CHECK ON THE RULER. A well is circular; a box much wider than it is tall means the detector clipped it at an image edge, or merged two wells, or found something that is not a well. Any of those makes
diameter_pxwrong, and since that diameter rescales every area in the well, a wrong one is worse than a missing one.
- property diameter_px: float[source]¶
The well’s diameter in pixels, as the mean of the box sides.
A well is round, so a correct box is square and the two sides agree. The MEAN rather than either side alone is what makes a slightly loose box degrade gently instead of biasing one way – and the disagreement itself is reported by
axis_ratio, so a box that is not square is visible rather than silently averaged into a plausible number.
- spacr.plaque.crop_well(image: numpy.ndarray, well: Well, *, pad: int = 0) numpy.ndarray[source]¶
The image inside one well.
- Parameters:
image – the full field.
well – the box to cut out.
pad – extra pixels around the box, clipped to the image.
- Returns:
the clipped image region selected by the padded box.
- spacr.plaque.detect_wells(image: numpy.ndarray, weights: str, *, confidence: float = DEFAULT_CONFIDENCE, imgsz: int = 640, min_axis_ratio: float = 0.7) List[Well][source]¶
Find the wells in one image.
- Parameters:
image – the field, as an
H x W x 3array in RGB channel order – whatcellpose.io.imread()returns for a colour image. It is converted to BGR here, because that is the order ultralytics reads an array in and therefore the order these detectors were trained in. A greyscale or otherwise non-three-channel array is passed through untouched, and so is a file path, which ultralytics decodes itself.weights – path to the YOLO checkpoint.
confidence – drop detections scoring below this.
imgsz – inference size; 640 is what the shipped detector trained at.
min_axis_ratio – reject boxes less square than this. See
Well.axis_ratio– a non-square box makes the diameter, and therefore every area in that well, wrong.
- Returns:
the wells, ordered top-to-bottom then left-to-right, which is reading order and therefore the order a plate map is written in.
- Raises:
ImportError – when
ultralyticsis not installed.
- spacr.plaque.plaque_flow_outputs(output: Any) Dict[str, numpy.ndarray | None][source]¶
The flow picture and cell probability out of a Cellpose result.
Cellpose’s
evalreturns(masks, flows, styles), andflowsis a list whose first entry is the flow field already drawn as an RGB image (direction as hue, strength as brightness, the picture the Cellpose GUI shows) and whose third is the cell-probability map, in logits. Either can be a torch tensor on the device the model ran on, so both go through_host_array(). A result that has no flows – a stub, or a model that returned only masks – givesNonefor both.- Parameters:
output – what
model.evalreturned.- Returns:
{'flow_rgb': H x W x 3 uint8 or None, 'cellprob': H x W float32 or None}.
- spacr.plaque.scale_from_well(well: Well, *, plate_format: str | None = None, well_diameter_mm: float | None = None) PlaqueScale | None[source]¶
Pixels-per-millimetre from a detected well, or
None.- Parameters:
well – the detected well to measure.
plate_format – a key of
WELL_DIAMETERS_MM.well_diameter_mm – the physical diameter, overriding
plate_format.
- Returns:
the scale, or
Nonewhen neither argument says how big the well physically is.- Raises:
KeyError – if
plate_formatis not a known format.
RETURNS
NoneRATHER THAN ASSUMING. Without a physical diameter there is no scale, and inventing one – a default plate format, say – would convert every area into confident millimetres that are wrong by whatever the real plate was. The caller keeps pixels and says so.
- spacr.plaque.segment_plaque_image(model: Any, image: numpy.ndarray, settings: Dict[str, Any], *, return_flows: bool = False) Any[source]¶
Segment one plaque image the way both Plaque mode’s preview and run do.
The image goes to Cellpose as it is, RGB or grey, and Cellpose normalises it. It is NOT sent through the run’s historical loader (
_load_normalized_images_and_labelswithbackground=200): on an 8-bit crop that loader saturated every pixel to 1.0 – measured onmalnio__2.tif, min = max = 1.0 in every channel – so the run found no plaques while the preview, reading the image as it is, found 67. One function for both is what keeps them from disagreeing again.- Parameters:
model – a Cellpose model.
image –
H x WorH x W x 3.settings –
diameter,flow_thresholdandCP_prob.return_flows – also hand back what the live preview’s Flows and Cell probability tabs show, from the same call.
- Returns:
the label image; with
return_flows,(labels, flows)whereflowsisplaque_flow_outputs().