spacr.qt.screens.make_masks¶
Workflow inputs and outputs¶
Make Masks¶
Curate image/mask pairs for segmentation training, or use Organize for Measure to merge images and their masks into arrays Measure reads. Saving a mask does not train a classifier.
Open: Home → Make Masks.
Inputs and outputs below include conditional alternatives. The guidance and handoff notes say which route applies.
Inputs
Microscope images — Source image folder; original files, supported vendor files or imported TIFFs.
Label masks — masks/ when retained, or explicitly saved image/mask pairs. Intermediate masks may be removed by cleanup.
Outputs
Curated training fields — Separate image and integer-mask files with matching field identities; preserve original images and labels.
Label masks — masks/ when retained, or explicitly saved image/mask pairs. Intermediate masks may be removed by cleanup.
After this module
Cellpose Workbench: Use independently checked image/mask pairs.
Measure: Use Organize for Measure to merge images and their masks into the arrays Measure reads; standalone masks are not merged arrays.
Plaque Assay: Use plaque masks with matching source images; cell masks are not automatically plaque labels.
Interactive editing and curation of segmentation masks.
Make Masks is filed under Tools on the home screen and is the manual
half of segmentation: it corrects masks a model got wrong, one field at a
time, and its masthead opens the Cellpose workflows that produced them.
MakeMasksScreen loads images and labelled masks from
<folder>/masks and saves edited labels as uint16 TIFF files.
THE TEN TOOLS, in TOOL_MODES order, because this vocabulary is what
a reader needs before opening the screen:
Brush and Erase paint and unpaint the active label a pixel at a time.
Erase object removes a whole label in one click. Wand grows a region
from the pixel clicked and adds it to the mask by default. Hold Ctrl while
clicking to remove that region; the tolerance is relative to the image’s intensity range
by default, see spacr.qt.mask_engine.relative_tolerance(). Draw
traces a free-form outline that closes and fills as ONE object – the tool a
brush is not, because a brush stamps disks along the path, so tracing a rim
with it labels the rim and leaves the middle background. Divide / Merge
uses a left-button drag to divide and a right-button drag to merge crossed
objects under the first touched identifier without changing their pixels.
The merged identity is retained when saving and reopening. Dividing drags a
line across a merged object and makes it two, keeping the original identifier
on the larger component and giving the smaller a new one, with every other
object untouched; spacr.qt.mask_engine.DIVIDE_CUT_WIDTH is the cut
width. Zoom rectangle-drags the view and changes no labels at all.
Recrop is the ninth and is the only one that changes WHICH field is on
screen rather than what is painted on it. A staged crop holding several cells
is not one training example, and curating it as one teaches a network that
two objects are one picture – so a box round an object writes that region of
both the image and the mask as a field of its own
(spacr.qt.mask_engine.write_recrop()), queued straight after the current
field, and the multi-object original is retired into
recropped_originals/ rather than curated
(spacr.qt.mask_engine.retire_recropped_original()). A box shorter than
spacr.qt.mask_engine.RECROP_MIN_SIDE on a side, or repeating a cut
already made past spacr.qt.mask_engine.RECROP_MAX_OVERLAP, is refused;
objects the box cuts through are dropped, because an object whose boundary is
where the mouse was released is not that object; and the labels that survive
are renumbered from one.
Ruler is the tenth. Drag between image pixels to read a length without editing labels. Zoom and pan preserve it; right-click with Ruler selected clears it. The same ruler is available on the paired live preview canvases.
Each field has a spacr.curation.CurationLog, initialized from any
existing sidecar. spacr.qt.mask_engine.save_mask() writes the labels and
ledger together, allowing spacr.curation.is_curated() to distinguish
manually corrected masks from pipeline output. A single gesture produces one
ledger entry and one undo step.
MakeMasksScreen.run_cellpose() applies the pipeline’s resolved
Cellpose-SAM model to the open field and displays the mask, cell-probability
map, and flow field. The intermediate outputs support evaluation of
CELLPROB_THRESHOLD when detections are missing or incomplete.
Editor modes are assembled from tool_row_entries() and
TOOL_MODES. MakeMasksScreen.add_toolbar_action() inserts
non-mode actions into the same toolbar. The settings panel carries the
operations that are not gestures – object filling and relabeling, swapping
object and background, size filtering and Otsu detection, with undo and redo
over all of them – alongside the brush, wand and display controls, and can
be hidden to return its width to the canvas.
Image inversion and swapping object and background are separate operations. The first changes the image seen by the detector; the second changes labels.
Invert image, in the Display category, is THE inversion. It draws the
field as its own negative and hands the detectors that same negative: both
detect buttons and the live magnifier segment
MakeMasksScreen._detector_image(), so a threshold written for bright
objects takes dark ones, and a banner above the image says so for as long as
it is on. The corner readout’s pixel intensity follows the inverted image.
Object mean intensities and the object filter still read the loaded pixels.
Saving writes label masks without changing the source image.
IT USED TO BE TWO SWITCHES AND THEY COULD DISAGREE. A display-only “Invert
image” drew a negative and left detection untouched; “Invert for detection”
inverted what the detectors read and drew nothing. A curator could hold
either without the other, and the first one’s status line had to contradict
the second one’s banner. They are one switch because the reason for the
switch is one intention: inverting a field of dark objects so that Otsu and
the magnifier can detect them. _cp_invert is now the same widget object as
_invert_display under its old name, so the two cannot come apart.
Swap object and background, in Object operations, is the old “Invert mask” under the name that describes it – it flips the LABEL image, which on an ordinary field leaves one object covering the frame. It is not a picture invert and is deliberately not called one.
THE ARITHMETIC. invert_normalized() normalises
the field to 0..1 on its own range and takes 1 - v, and the
normalisation is not incidental: the Otsu correction is a
MULTIPLIER on an absolute level, so normalising first is what makes one
correction value mean the same thing on the next image.
invert_intensity() (dtype complement) and
invert_for_detection() (reflection about the
field’s range) are what this screen used before and are kept for other
callers. invert_mask() flips labels.
Additional segmentation tools are opened from the masthead in
FOLD_ORDER through
FoldStrip. Mask the whole folder
applies Cellpose to every image in the selected folder, while Save mask on
the Curate page calls spacr.curation.MaskCuration.save_mask(). Folded
modules retain their existing widgets inside FoldedModulePanel.
Time-series tracking is integrated with Mask Generation as the Timelapse
settings category; see spacr.qt.screens.mask. Motility Assay instead
belongs to Measure because it consumes existing masks and writes measurement
tables rather than generating masks.
Classes¶
One folded module, as the whole screen it was, plus what the host adds. |
|
Qt widget for the Make Masks app — the successor to Tk ModifyMaskApp. |
|
Exchange an image and label mask with an interactive napari viewer. |
|
Make Masks' object filters: one row per regionprop the user added. |
Functions¶
|
The name a magnifier mode goes under today. |
|
Segment one field with |
|
Pull |
|
Cellpose's probability map as an RGB heatmap, |
|
Cellpose's flow field as |
|
|
|
Return True when no interactive display is attached to this process. |
|
Load a Cellpose model through spaCR's own resolver. |
|
Percentile-stretch any float array to 0-255 uint8. |
|
Every canvas tool the toolbar row should hold. |
Module Contents¶
- class spacr.qt.screens.make_masks.FoldedModulePanel(key: str, screen: PySide6.QtWidgets.QWidget, title: str, parent: PySide6.QtWidgets.QWidget | None = None, actions=())[source]¶
Bases:
PySide6.QtWidgets.QWidgetOne folded module, as the whole screen it was, plus what the host adds.
A fold that reimplemented the module it replaced would keep whatever the person doing the folding happened to think of and quietly drop the rest. So the button opens the module’s OWN widget: every control, every worker, every drop target it had as a tile is what arrives, and the only thing that changed is where it is opened from.
IT IS A PAGE ON THIS SCREEN, not a window over it. A window is the last resort for a fold, and it is what this becomes only when the host has no body to make pages out of — see
spacr.qt.screens.map_barcodes.show_as_page(). As a page it is closed by the tab’s own close button, so the standard Close button below belongs to the window shape alone and is added with it.- Parameters:
key – the folded module’s registry key.
screen – the module’s own widget, already built.
title – the page’s caption and the window title — the module’s name.
actions – extra buttons for the button row, each
(label, tooltip, callback). This is where a capability the folded module lacks and its host has arrives.parent – parent widget; ownership only.
Wrap one folded module’s screen with a title and its actions.
- Parameters:
key – the module’s registry key.
screen – the screen to wrap.
title – the caption over it.
parent – parent widget.
actions – extra buttons for the panel’s own row.
- add_close_button() None[source]¶
Give this panel the Close button a window needs.
A page is closed by its tab. A window has no tab, so the row that carries the host’s extra actions carries a Close beside them — added when the panel becomes a window rather than always, so a page never shows a button that would hide it inside its own tab.
- class spacr.qt.screens.make_masks.MakeMasksScreen(parent: PySide6.QtWidgets.QWidget | None = None)[source]¶
Bases:
PySide6.QtWidgets.QWidgetQt widget for the Make Masks app — the successor to Tk ModifyMaskApp.
Owns the canvas, the tools panel, and the file-navigation state; see the module docstring for the full feature list.
- Parameters:
parent – parent widget.
Build the editor, its canvas and its tool panel.
- Parameters:
parent – parent widget.
- add_toolbar_action(button: PySide6.QtWidgets.QPushButton) PySide6.QtWidgets.QPushButton[source]¶
Insert a non-mode action into the editor toolbar.
The button is placed after the other actions in the part of the row that scrolls. The Magnifier and the settings toggle are pinned outside it, so nothing added here can come between them or push them out of sight.
- Parameters:
button – Action button to insert.
- Returns:
The same button.
- apply_object_filter(*, on_load: bool = False) int[source]¶
Apply the filter list to the field on screen; return how many it hides.
Runs itself when a field loads – a draft segmentation usually arrives with the same class of junk in every field – whenever the list is edited, and when the user presses Filter.
One engine:
mask_engine.apply_filters(), the same one Mask generation’sobject_filterssetting runs, with oneregionprops_tablepass for every property the list names. The result is one undo step and one ledger entry that records the whole list, so the mask’s provenance says which filters shaped it.Every hidden object is written into the Filter category’s ledger, one red row each, cleared at the start of every run.
- Parameters:
on_load – True when this is the automatic run on opening a field. It starts a fresh baseline and keeps a run that hid nothing quiet.
- clear_mask() None[source]¶
Zero the current mask without asking, recording it in history.
The confirmation lives in
_on_clear_mask(); this is the scriptable entry point (and what the undo stack sees).
- closeEvent(event)[source]¶
Drain the background image loader before Qt destroys this screen.
_MaskLoadWorkeris parented to this widget, so without this the screen’s destructor deletes a QThread that is still decoding a large TIFF, and Qt answers that withqFatal("QThread: Destroyed while thread is still running")— a core dump, not an exception. The window is exactly as wide as one image decode, which is why it shows up in a loaded test shard and almost never by hand.Any folded module still open goes with it: each one is a window of its own, and several of them own worker threads and viewers that must be told to stop rather than be collected out from under Qt. A model download is cancelled; a backend install is left to finish, because
pipstopped half way can leave the environment broken.- Parameters:
event – the close event; it is passed on to the base class once the workers and folded modules have been stopped.
- close_folded() None[source]¶
Close every folded module, and everything it started.
Closing the panel is not enough. A module polls the machine’s RAM and GPU on a worker thread, and Qt answers a running QThread being destroyed by aborting the process — so the module’s own close handler, which drains that worker, has to run. A page nested inside another module’s tabs never gets one from Qt, which is why they are closed by hand here.
EVERY MODULE THAT WAS BUILT, not every module that was opened. A module is built the moment something asks this screen to point it at the open folder —
seed_folded()does, and so does any test or caller reaching forfolded_screen()— and pointing Model Compare at a folder starts a load thread before its panel has ever been on screen. Walking the panels alone left those threads running with nothing holding them, and the process died of it several actions later, in whatever happened to be running when the memory behind them was touched.
- contribute_images_and_masks(*, show: bool = True, upload=None, threaded: bool = True)[source]¶
Open the dialog that sends images and masks to a community dataset.
One click from curation: the dialog opens on the image on screen and its mask, with every curated image of the folder one choice away, and an images folder plus a masks folder as the third way in.
- Parameters:
show – show the dialog; False only builds it (tests).
upload –
fn(folder, target) -> urlin place of the real upload (tests).threaded – send on a worker thread.
- Returns:
the dialog.
- download_zoo_model(entry) bool[source]¶
Ask, then download a zoo Cellpose model and select it when it lands.
The download goes through
spacr.model_zoo.install()on a worker thread (spacr.qt.model_install.CheckpointDownload), into the folder the Model zoo picker uses, so either route finds the file the other fetched. The bar under the Model row shows the bytes as they arrive, and the screen stays usable. A model that publishes no checksum is downloaded only after the user has been told spaCR cannot then check it.- Parameters:
entry – the zoo’s record of the model.
- Returns:
True when a download was started.
- export_all_rois(target: str, fmt: str) List[str][source]¶
Write every field’s mask as ROIs.
GeoJSON and ImageJ are one file per field in the folder
target(<stem>.geojson,<stem>_RoiSet.zip), the way QuPath and Fiji open them beside the image; COCO is one dataset file,target, holding every field. The field on screen is exported as it is on screen; the others as saved. Fields without objects are left out.- Parameters:
target – the folder (GeoJSON, ImageJ) or file (COCO).
fmt –
"geojson","imagej"or"coco".
- Returns:
the files written.
- export_field_rois(path: str, fmt: str | None = None) str[source]¶
Write the mask on screen as QuPath GeoJSON, a RoiSet or COCO JSON.
- Parameters:
path – the file to write.
fmt –
"geojson","imagej"or"coco"; default from the suffix ofpath.
- Returns:
the path written, or
""when no field is open.
- finish_recrop() bool[source]¶
Archive a recropped source field and advance to its first child.
The source image, mask, and ledger move to
recropped_originals/and remain recoverable through the recrop manifest.- Returns:
Trueif a field was archived.
- folded_screen(key: str) PySide6.QtWidgets.QWidget | None[source]¶
The folded module’s own screen, built on first use and kept.
- Parameters:
key – one of
FOLD_ORDER. Keys that share a screen — seeFOLD_HOSTS— resolve to the one widget that hosts them both.- Returns:
the screen, or
Nonefor a key this screen does not fold.
- import_all_rois(source: str, fmt: str, object_type: str | None = None) List[str][source]¶
Import ROIs for every field that has them and save them as masks.
GeoJSON and ImageJ files are found in the folder
sourceby the field’s stem (<stem>.geojson,<stem>_RoiSet.zip…); a COCOsourceis one file matched onfile_name. Each matched field’s mask is written as_on_save()writes it; the field on screen is replaced on the canvas instead, as one undoable edit, and saved with the rest when the user saves it.- Parameters:
source – the folder (GeoJSON, ImageJ) or file (COCO).
fmt –
"geojson","imagej"or"coco".object_type – the object type to take from files of several; default the folder’s own type, else the first in the file.
- Returns:
the names of the fields that were given masks.
- import_field_rois(path: str, fmt: str | None = None, object_type: str | None = None) int[source]¶
Replace the mask on screen with the objects of a ROI file.
One edit on the undo stack and in the curation ledger; nothing is written until the mask is saved. A file of several object types puts
object_typeon the canvas, else the folder’s own type, else the user’s choice.- Parameters:
path – the GeoJSON, RoiSet or COCO file.
fmt – its format; default from the file.
object_type – the object type to take.
- Returns:
the number of objects on screen afterwards, or
-1when nothing was imported.
- mask_settings() dict[source]¶
The configured chain and PSF as the Mask module’s settings.
spacr.qt.detect_chain.chain_settings()writes the image steps asenhance_*keys and the PSF controls write thepsf_*keys, which is exactly whatspacr.psf_pipeline.prepare_chain()reads back, so the chain a plate run applies is the chain configured here – whether or not Apply is on, since Apply is this screen’s switch.
- mask_whole_folder() bool[source]¶
Segment every image in the open folder with the current model.
The one folded button that does something rather than opening something: it points the applying half of the Cellpose workbench at the folder already open here and starts it. “The current model” is whatever that tab holds — the checkpoint the Train tab produced if there is one, and the stock model otherwise.
The Apply half writes one mask per image into
<src>/masks, which is the nested layout’s masks folder and nobody else’s: in a sibling session it would beimages/masks, beside the set’s real masks rather than in them, and a folder of_seg.npybundles has no images for it to read. Both are refused, in the status line.- Returns:
whether a run was started. A folder that is not open, a session whose masks are not in
<folder>/masks, and a confirmation that is declined, all answerFalse.
- open_folded(key: str) FoldedModulePanel | None[source]¶
Open a folded module on this screen, pointed at the open field.
The module arrives as a PAGE beside the editor, which is where a fold belongs; it becomes a window only if this screen has no body to make pages out of.
- Parameters:
key – one of
FOLD_ORDER.- Returns:
the module’s panel, or
Nonefor a key this screen does not fold. Pressing the same button again raises the page that is already there rather than building a second one.
- open_paths(paths) bool[source]¶
Open a drop the way its contents say, asking where there is a choice.
spacr.drop_classification.classify_drop()says what was dropped, and:image files (with or without folders) open as one queue, in drop order, each field edited where it lies with its mask in its own
<folder>/masks–_open_queue();one folder of images opens as it is;
one folder whose subfolders look like channels (DAPI/, GFP/…, or the same fields in each) opens “Organize for Measure” with a channel column per subfolder; any other folder whose images sit in subfolders is offered for consolidation;
several folders of images open “Organize for Measure” with a channel column per folder;
when that popup is cancelled (or nobody can see it), the drop opens as it always did: the folder as it is, or a queue;
images dropped with their masks open with those masks (
_open_with_masks());a folder spaCR wrote is named in the console and its images, if any, open – a
.npyis never opened as an image.
Everything the drop held that is used for nothing is listed in the console.
- Parameters:
paths – the dropped files and folders, in the order dropped.
- Returns:
whether something was opened or a job started.
- open_queue(queue) bool[source]¶
Open the fields a curation session offers, in the order it offers.
The session decides WHICH fields and in WHAT ORDER – reviewed ones already dropped,
--limitalready applied – and this screen shows them. All three layouts are edited where they lie:nestedopens the queue folder, masks in<folder>/masks;siblingopens<folder>/imagesand reads and saves the masks in<folder>/masksbeside it, neverimages/masks;segopens the queue folder with the_seg.npybundles as its fields, each saved back into itself.
What the session had to say – a
proboreasyorder that fell back for want of scores, fields the scores do not name, a resume record beside the folder rather than in it – is put on screen with it, not only in the terminal that started it. It stays on the status line after the first field loads, including a field large enough to load in the background, which lands after this returns.- Parameters:
queue – a
spacr.curation_queue.CurationQueue.- Returns:
whether the editor is now on that session.
- recrop(x0: int, y0: int, x1: int, y1: int) str | None[source]¶
Extract one selected region into a separate training field.
Accepted crops are written immediately, inserted after the source field in the queue, and marked on the canvas. Rejected selections are reported in the status label without modifying the queue.
- Parameters:
x0 – x of the first selection corner, in image pixels.
y0 – y of the first selection corner, in image pixels.
x1 – x of the opposite corner, in image pixels. The corners may come in either order and are clipped to the image.
y1 – y of the opposite corner, in image pixels.
- Returns:
Filename of the recropped field, or
Noneif the selection was rejected or could not be written.
- run_cellpose() int[source]¶
Segment the open field with the chosen model; objects found.
The two panes are filled BEFORE the mask is touched, and they are filled even when the run found nothing at all. A run that returns an empty mask is exactly the run whose probability map you need to see: it says whether the network found nothing, or found plenty and the threshold threw it away.
This programmatic method is synchronous and returns the object count. The toolbar uses a background worker instead, taking a snapshot and discarding results after field changes or mask edits. If toolbar detection is already running, this method returns zero without starting a second run.
With Invert on the model is given the INVERTED field (
_detector_image()), and the status line and the ledger entry both say so.
- save_curated_mask() str[source]¶
Write the labels Curate corrected back to the mask file.
The Curate page retains corrected labels in its curation session. This method calls
spacr.curation.MaskCuration.save_mask(), which writes the labels and, when edits exist, writes the corresponding curation ledger beside the output file.- Returns:
the path written, or
""when there is nothing to write.
- seed_folded(key: str) dict[source]¶
Point a folded module at the field this screen has open.
The whole reason these are buttons on this masthead rather than rows of their own is that the folder is already chosen here; a folded module that opened on an empty path would have folded the file dialog in with it.
- Parameters:
key – one of
FOLD_ORDER. Note that this is the button’s key, not its host’s: the two Cellpose halves share a screen and seed different halves of it.- Returns:
what was seeded, as
{name: value}. Empty when no folder is open, which is not a failure — the module opens on its own file picker exactly as its tile did.
- class spacr.qt.screens.make_masks.NapariBridgeScreen(parent: PySide6.QtWidgets.QWidget | None = None)[source]¶
Bases:
PySide6.QtWidgets.QWidgetExchange an image and label mask with an interactive napari viewer.
napari is imported only when the viewer is opened, and spaCR’s existing Qt event loop remains active. Corrected labels are validated and recorded in the same curation ledger used by the Curate screen. The corresponding headless operations are available from
spacr.napari_bridge.- Parameters:
parent – parent widget.
Build the bridge’s two path rows and its launch button.
- Parameters:
parent – parent widget.
- closeEvent(event)[source]¶
Stop background work and unlink before going away.
- Parameters:
event – the Qt close event.
- open_in_napari() Any[source]¶
Open the selected image and mask in napari.
- Returns:
The napari viewer, or
Noneif validation or startup fails. Missing optional dependencies are reported in the status pane.
- say(text: str, *, append: bool = False) str[source]¶
Display a status message and return the complete displayed text.
- Parameters:
text – the message; converted to
str.append – add the message below the current text, after a blank line, instead of replacing it.
- set_paths(mask: str = '', image: str = '') None[source]¶
Set the mask and optional source-image paths.
- take_mask_back()[source]¶
Import corrected labels and record the mask correction.
- Returns:
spacr.napari_bridge.CorrectionResult, orNoneif no active handoff exists or validation fails.
- class spacr.qt.screens.make_masks.ObjectFilterList(parent=None)[source]¶
Bases:
PySide6.QtWidgets.QWidgetMake Masks’ object filters: one row per regionprop the user added.
The Filter category used to hold four fixed boxes – minimum and maximum area, minimum and maximum mean intensity. It now starts EMPTY, and “Add a filter” offers every scalar property
skimage.measure.regionprops()computes (seemask_engine.filter_properties()); each added row carries the property, a minimum, a maximum and a Remove button. A blank bound is off. The old four are two of the rows a user can add:areaandintensity_mean.The intensity statistics are offered only while an intensity image is open (
set_intensity_available()), so a property that cannot be measured is never offered, rather than offered and refused later.filters()is the serialised list – the same one Mask generation reads fromobject_filters– andchangedfires whenever it may have changed, which is what applies the list live.Build the Add control and the empty row list.
- add_filter(name, minimum=None, maximum=None, *, notify: bool = True) dict[source]¶
Add one row; return it as
{widget, property, min, max, remove}.- Parameters:
name – the regionprop the row filters on.
minimum – the lower bound, or
Nonefor none.maximum – the upper bound, or
Nonefor none.notify – emit
changedonce the row is added.
- Raises:
ValueError – when
nameis not a scalar regionprop, or is an intensity property while no intensity image is open.
- filters() List[dict][source]¶
The rows as the serialised filter list the engine and a run read.
- Raises:
ValueError – when a row’s minimum is above its maximum.
- remove_filter(index: int) None[source]¶
Remove row
index; the objects only it hid come back.- Parameters:
index – the row, in the order rows were added.
- set_bounds(index: int, minimum=None, maximum=None) None[source]¶
Set row
index’s bounds as if typed, and apply them.- Parameters:
index – the row, in the order rows were added.
minimum – the lower bound, or
Noneto clear it.maximum – the upper bound, or
Noneto clear it.
- set_filter(name, minimum=None, maximum=None) None[source]¶
Set the bounds of the first row for
name, adding it if absent.- Parameters:
name – the regionprop, current or legacy spelling.
minimum – the lower bound, or
Noneto clear it.maximum – the upper bound, or
Noneto clear it.
- set_filters(filters) None[source]¶
Replace every row with
filters, a list or a legacy bounds dict.A dict of the old four bounds (
min_areaand the rest) is migrated bymask_engine.legacy_filters(), so a saved state from before the filter list opens as the rows it meant.- Parameters:
filters – a filter list in any form
mask_engine.normalise_filters()accepts, or the legacy dict.
- spacr.qt.screens.make_masks.canonical_magnifier_mode(mode) str[source]¶
The name a magnifier mode goes under today.
- Parameters:
mode – a mode name, possibly one this screen has renamed.
- Returns:
the current name; an unknown mode is handed back unchanged, so
_segment_region()can still say it does not know it.
- spacr.qt.screens.make_masks.cellpose_detect(image: numpy.ndarray, model, *, diameter: int = 0, normalize: bool = True, flow_threshold: float = FLOW_THRESHOLD, cellprob_threshold: float = CELLPROB_THRESHOLD, min_size: int = 0) tuple[source]¶
Segment one field with
model; return labels and both intermediates.The image goes in as a batch of one, which is what
spacr.spacr_cellpose.parse_cellpose4_output()— the repository’s own reader of this return value — is written for. Handed a bare 2-D array instead,evalreturns a flat three-member flows list and that function readslen(masks)as the number of images and finds the image height, so the parse fails on an image that segmented perfectly well.diameteris passed as None when it is 0, which is Cellpose’s “work it out from the image”; it is the one pre-SAM sizing argument Cellpose 4 still honours, sinceevalrescales by30/diameter.- Parameters:
image – one 2-D field, as the canvas holds it.
model – a loaded
CellposeModel(seeload_cellpose_model()), or anything with the sameeval.
- Returns:
(labels, cellprob, flow_rgb)— an int32 label image, and the two maps ascellpose_intermediates()reads them.
- spacr.qt.screens.make_masks.cellpose_intermediates(flows) tuple[source]¶
Pull
(cellprob, flow_rgb)out of one image’sflowsentry.Measured against cellpose 4.2.1.1:
CellposeModel.evalreturns(masks, flows, styles), and for one 2-D imageflowsis a list of three arrays that are three different things —flows[0]an(H, W, 3)uint8 RGB rendering of the field,flows[1]the(2, H, W)float32 vectors, andflows[2]the(H, W)float32 cell-probability map. Indexing it as though the members were interchangeable is how a flow pane ends up showing the probability map.- Parameters:
flows – one image’s flows list, as
spacr.spacr_cellpose.parse_cellpose4_output()hands it over per image, or the raw list from a single-imageeval.- Returns:
(cellprob, rgb), either of which may be None when this Cellpose did not produce it.
- spacr.qt.screens.make_masks.cellprob_heatmap(cellprob: numpy.ndarray) numpy.ndarray[source]¶
Cellpose’s probability map as an RGB heatmap,
(H, W, 3)uint8.A greyscale probability map beside a greyscale image is two pictures that look alike and mean different things. A colour ramp says at a glance which pixels the network was confident about, which is the whole reason to look at this map before moving a threshold.
Matplotlib’s
magmais used where it is importable, and a plain black-to-white ramp stands in where it is not, so the pane is never the thing that fails.- Parameters:
cellprob – Cellpose’s cell-probability map, a 2-D float array; it is percentile-stretched with
stretch_to_uint8()before colouring.
- spacr.qt.screens.make_masks.flow_rgb(flow: numpy.ndarray) numpy.ndarray | None[source]¶
Cellpose’s flow field as
(H, W, 3)uint8, or None if it is not one.evalhands back the flow field twice over and the two entries are not the same thing:flows[0]is already an RGB picture of the field (hue = direction), whileflows[1]is the raw(2, H, W)vector field. This takes the picture where it is given one and builds an equivalent from the vectors otherwise, so the pane fills whichever entry a caller passes.- Parameters:
flow – either Cellpose’s RGB flow picture, an array of shape (H, W, 3 or more), or the raw vector field of shape (2, H, W);
Noneor any other shape givesNone.
- spacr.qt.screens.make_masks.fold_description(key: str) tuple[source]¶
(name, description, stage)for a folded module.The app registry answers while it still holds the module’s row; once the row has been dropped — which is what folding a module ends in — the answer comes from
FOLD_FALLBACK, so the button goes on carrying the name, the sentence and the maturity colour its tile had.- Parameters:
key – the app registry key of the folded module; an unknown key gives empty strings.
- spacr.qt.screens.make_masks.is_headless() bool[source]¶
Return True when no interactive display is attached to this process.
A modal
QMessageBoxruns its own event loop and only returns once somebody clicks a button. Under theoffscreen/minimalQt platform plugins — CI, a headless server, an SSH session with no X — nobody can, so the call never returns and the whole app hangs. Any message triggered by data rather than by a user gesture therefore has to degrade to the status line instead.Sibling screens (align / batch / convert / report / plate_view / …) solve this by never opening a modal at all — see their
_set_statusdocstrings, which cite this screen as the case that actually hung. That is not sufficient here because “Clear mask” genuinely needs a yes/no answer, so this screen keeps the modal when — and only when — there is somebody able to answer it.
- spacr.qt.screens.make_masks.load_cellpose_model(model_name: str)[source]¶
Load a Cellpose model through spaCR’s own resolver.
spacr.utils._resolve_cellpose_pretrained()is what the pipeline itself calls: it maps every pre-SAM name ontocpsam, keeps a fine-tuned checkpoint path as itself, and raises rather than quietly substituting stock weights for a checkpoint that is not there. Going around it with a second, simpler call would give this screen a different answer from the run it is meant to be correcting.ON A CPU THE WEIGHTS ARE LOADED IN FLOAT32. Cellpose-SAM loads them in bfloat16 by default, and a processor without native bfloat16 arithmetic runs every tile through a slow emulation of it: measured on an AMD Ryzen 9 5950X, one 256 px tile took 162-197 s in bfloat16 and 31-46 s in float32, alternating, under the same load. A whole field is a hundred such tiles, so this is the difference between most of an hour and most of a day. On a GPU nothing changes:
spacr.accelerator.cellpose_kwargs()decides there, as it does for the pipeline.A
cellpose3:<name or path>model – a stock Cellpose 3 model or a bioimage.io Cellpose 3 checkpoint – is not a Cellpose 4 model at all: Cellpose 4 would load such a checkpoint and segment nonsense with it. It is loaded by_backend_model(), in the Cellpose 3 backend’s own environment, as Mask generation loads it. Acellpose_dino:<path>model, a Cellpose-DINO checkpoint, is loaded the same way in the Cellpose-DINO backend, and astardist:,instanseg:oromnipose:model in its own backend.- Parameters:
model_name – a Cellpose model name, the path of a fine-tuned checkpoint, resolved by
spacr.utils._resolve_cellpose_pretrained(), orcellpose3:<name or path>.
- spacr.qt.screens.make_masks.stretch_to_uint8(array: numpy.ndarray, lower_pct: float = 1.0, upper_pct: float = 99.0) numpy.ndarray[source]¶
Percentile-stretch any float array to 0-255 uint8.
The probability map runs roughly -12..+8 and the flow components ±5; the intensity image is 16-bit counts. Shown raw beside each other they are three different scales and none of them is readable. Stretching each one by its own percentiles is what puts them on the same footing as the contrast-stretched intensity image the canvas already draws, which is the only way the panes can be compared with it by eye.
- Parameters:
array – any numeric array; it is read as float32, and an empty array gives an empty uint8 array.
lower_pct – percentile mapped to 0.
upper_pct – percentile mapped to 255.
- spacr.qt.screens.make_masks.tool_row_entries() List[tuple][source]¶
Every canvas tool the toolbar row should hold.
TOOL_MODESfirst, in its own order, then any otherMODE_*constant in this module the table does not mention — labelled from its own value and drawn with whateverspacr.qt.iconset.icon()has for that name, which is a fallback glyph when it has nothing. Alphabetical among themselves, so the row is the same on every run.MODE_WAND_ERASEis the legacy canvas action, now selected with Ctrl on the single Wand tool rather than a separate toolbar button.MODE_NONEis excluded because it is not a tool: it is the canvas with no tool held, which is what the row shows when nothing is checked.
Nested helpers¶
- MakeMasksScreen._apply_magnifier_drag.common(field, default)¶
Return a shared frame value, or the explicit mixed/unknown value.
spacr/qt/screens/make_masks.py:16481
- MakeMasksScreen._apply_uncertainty_ranking.stem(name: str) str¶
- Parameters:
name – a field’s file name.
- Returns:
its stem, as the curation queue names it.
spacr/qt/screens/make_masks.py:10151
- MakeMasksScreen._build_methods_card.row(field: str, caption: str, widget, tip: str) None¶
Add one parameter row and remember it under its field name.
spacr/qt/screens/make_masks.py:13671
- MakeMasksScreen._build_propagate_card.row(field: str, caption: str, widget, tip: str) None¶
Add one parameter row and remember it under its field name.
spacr/qt/screens/make_masks.py:14039
- MakeMasksScreen._end_blind.ask()¶
Confirm revealing field paths and recording the unblind event.
spacr/qt/screens/make_masks.py:10323
- MakeMasksScreen._offer_prompt_install.watch(dialog)¶
Put the install’s progress and its ending in the console.
spacr/qt/screens/make_masks.py:16154
- _Sam2SeedDialog.run.work()¶
Propagate the seeds off the GUI thread and report the labels.
spacr/qt/screens/make_masks.py:17888
- _counting_tiles.before_a_tile(_module, inputs)¶
Count this call’s tiles, or stop the run here.
spacr/qt/screens/make_masks.py:3720
- _uncertainty_snapshot._score(image)¶
Score primary TTA and the optional second model in one frame.
- Parameters:
image – one field.
- Returns:
its uncertainty over four or eight aligned passes.
spacr/qt/screens/make_masks.py:4851
- _uncertainty_snapshot.secondary(image)¶
Segment a transform with the captured second model.
- Parameters:
image – one transformed field.
- Returns:
labels, cell probability and vectors.
spacr/qt/screens/make_masks.py:4841
- _uncertainty_snapshot.segment(image)¶
- Parameters:
image – one transformed field.
- Returns:
its labels, cell probability and flow vectors under the captured detection settings.
spacr/qt/screens/make_masks.py:4825