spacr.qt.screens.embeddings¶
Workflow inputs and outputs¶
Embeddings¶
Encode object images with a chosen model and channel policy. Retain object identities and encoder provenance when supplying the features to downstream exploration.
With alpha features enabled, Save for Similar crops stores the full embedding matrix in the measurements database that supplied the loaded objects. It requires the original database object identities and verified encoder weights. Annotate can then search those stored vectors with compatible model, channel and preprocessing identities. Folder-loaded crops use the standalone Python example to save matrices; they cannot use the database save action.
Open: Home → Embeddings.
Inputs and outputs below include conditional alternatives. The guidance and handoff notes say which route applies.
Inputs
Measured objects — measurements/measurements.db; object tables depend on the enabled cell, nucleus, pathogen and organelle masks. Relevant tables, depending on the route:
cell,nucleus,pathogen,cytoplasm. Relevant columns, depending on the route:plateID,rowID,columnID,fieldID.Object crops — data/**/*_png when save_png is enabled; png_list indexes saved crops. Supported workflows can instead stream crops from merged arrays and masks. Relevant tables, depending on the route:
png_list. Relevant columns, depending on the route:png_path,prcfo.
Outputs
Image embeddings — Object-indexed encoder features, with channel policy and encoder provenance. The encoding API returns features; persistence is caller-dependent.
Before this module
Measure: Retain encoder and channel-policy provenance.
After this module
Image UMAP: Supply the encoder feature table with matching object IDs; do not assume every GUI route automatically persists it.
Embeddings — a label-free vector for every object, beside the measured panel.
The upstream Cell-DINO, OpenPhenom and SubCell models expose Python interfaces; this screen runs them without code. It also accepts an explicitly mapped, digest-verified local official Cell-DINO checkpoint.
The screen runs the same embedding engine, spacr.embeddings, that the
Python API exposes, without requiring any Python.
What it is for. Giving every object a vector that no label went into. The measured panel says how big a cell is and how bright each stain was; an embedding describes what the cell looks like, in a few hundred numbers a pretrained backbone produces without being told what to look for. It complements the panel rather than replacing it: a phenotype that no measured column captures can still be represented in the vector.
What it needs. Crops, and a choice about the channels. The crops come
from the row at the top of the screen, which is spacr.crop_loader:
either a measurements.db, narrowed to one object class, one plate or
any condition its crop table supports, or a folder of crop PNGs. Whichever
it is, the pixels are read the way every other screen reads them –
spacr.crops.resolve_crop_source() decides between the crops already
under data/ and cutting them from merged/*.npy, and says which it
chose. Nothing has to be prepared first: a plate that has been through
Measure with crop output on is ready.
What it produces. One row per object and one column per dimension,
named emb_c<channel>_<dimension> under the per-channel policy and
emb_<dimension> under projection, with the model-zoo entry that made
them – backbone, policy and the checksum of the weights on this machine.
Two runs’ dimension 17 are the same number and not the same thing unless
all three match, which is why the entry is part of the result rather than
a log line. The preview shows the first few dimensions of the first fifty
objects. The alpha Save for Similar crops action stores the complete matrix
in the database that supplied the loaded objects, so Annotate can search
those vectors. Folder and programmatically supplied crops have no database
object identity and cannot use this action.
What to do next. Treat the columns as a feature source, not as a result. They are consumed exactly as the measured panel is – a reduction to see whether the objects separate, a regression against the perturbation, a retrieval to find the objects most like one you picked. The screen repeats the corresponding warning under the table: a single dimension is not a phenotype, and these numbers can only be interpreted through an analysis that uses the whole vector.
All the arithmetic is in spacr.embeddings and none is here. This module
is the surface, and three of the engine’s decisions shape it:
The channel policy is the first control on the screen. How channels are combined must not be decided implicitly by whatever the backbone expects. Per-channel encoding keeps the channel in every column name and costs N forward passes; projection mixes the channels into three and costs one. Those produce columns of identical width and dtype that mean entirely different things, so the screen makes the choice explicit and says what each one costs.
A single dimension is not a phenotype, and the screen states this under the
table. The table shows the first few dimensions so the user can see that
the embedding ran; the caption says they are not interpretable individually.
Showing emb_c2_017 = 0.43 without that caption invites reading one
dimension as a phenotype.
The encoder is identified with the checksum of its weights. The run
summary carries the model-zoo entry from
spacr.embeddings.encoder_entry() – backbone, policy and the digest of
the weights on this machine – because two runs’ dimension 17 are the same
number and not the same thing unless all three match.
The embedding runs off the GUI thread through
spacr.qt.job_runner.JobRunner, like every other compute in the Qt
layer: a plate of crops through a backbone is minutes, and threaded=False
runs the identical code inline so a test drives the shipped path.
register() is not called at import; read its docstring.
Classes¶
Pick a crop source and a channel policy, and embed every object. |
Functions¶
|
Factory handed to |
|
Put Embeddings in the app registry. Idempotent. |
Module Contents¶
- class spacr.qt.screens.embeddings.EmbeddingsScreen(parent=None, *, threaded: bool = True)[source]¶
Bases:
PySide6.QtWidgets.QWidgetPick a crop source and a channel policy, and embed every object.
The crop loader above the encoder controls is the screen’s entry point:
spacr.crop_loaderplans the selection, this screen pages through it on a worker thread, and each finished load callsset_crops().- Parameters:
parent – the usual Qt parent.
threaded –
Falseruns the embedding and the crop load inline, emitting the same signals in the same order, so a test drives the screen synchronously without the behaviour diverging.
Build the screen: the controls above, the preview table below.
- choose_path() str[source]¶
Ask for the database or the folder, and remember the answer.
- Returns:
what was chosen, or
''when the dialog was dismissed, so a caller can tell a cancel from a choice.
- closeEvent(event)[source]¶
Discard pending results and drain all runners before the widget goes.
Crop loading and classifier training are asked to stop first; further training progress is ignored. Each
shutdownwaits a bounded time, then parks any worker that outlasts it. A parked training worker can finish writing its checkpoint without updating this closed GUI view; it is not forcibly terminated.- Parameters:
event – Qt’s close event, passed to the base class unchanged. The close is accepted even if a worker must be parked.
- crop_query()[source]¶
The
spacr.crop_loader.CropQuerythe controls describe.- Returns:
the query a press of Load would run, which is what makes the panel testable without pressing anything.
- is_busy() bool[source]¶
Whether crop loading, embedding, training or classifier application is in flight.
Includes the training and application runners until their workers have retired, even after their final results have reached the screen.
- load_crops() None[source]¶
Plan the selection, then read it a page at a time.
The load runs as two jobs. Planning is a count and a select, so it comes back in milliseconds and the screen can say what was matched – or why nothing was – before committing to read a single pixel. Only then does the second job start, and it reports after every page.
- set_crops(crops: numpy.ndarray, *, label: str = '') None[source]¶
Hand the screen an
(objects, height, width, channels)stack.The channel axis comes last. Getting it backwards does not raise here; it reaches the model and fails there with a message about tensor shapes.
That layout is what
spacr.cropsproduces and whatspacr.embeddings.embed_array()documents.- Parameters:
crops – the object stack, channels last. Not copied: the screen keeps this array and hands it to the backbone, so a caller that mutates it afterwards changes what gets embedded.
- Raises:
ValueError – when the stack is not four-dimensional.
- spec()[source]¶
The
spacr.embeddings.EmbeddingSpecthe controls describe.
- spacr.qt.screens.embeddings.make_embeddings_screen(app_key: str | None = None) PySide6.QtWidgets.QWidget[source]¶
Factory handed to
spacr.qt.app.register_app().
- spacr.qt.screens.embeddings.register() bool[source]¶
Put Embeddings in the app registry. Idempotent.
Called from
spacr.qt.SELF_REGISTERING_MODULES, not at import, so importing this module to reachEmbeddingsScreenfrom a test or a notebook does not mutate process-wide state.SECTION_DATA, beside the other things that turn a table into numbers. An embedding is a feature source, not an analysis: it produces columns that the reductions, the regressions and the hit calling then consume exactly as they consume the measured panel.
Nested helpers¶
- EmbeddingsScreen._apply_dino_classifier.finished(answer)¶
Retain outputs only for the still-current, uncancelled crop selection.
spacr/qt/screens/embeddings.py:1987
- EmbeddingsScreen._apply_dino_classifier.work()¶
Apply the actual artifact to unchanged full raw channel-last crops.
spacr/qt/screens/embeddings.py:1974
- EmbeddingsScreen._cell_dino_dialog.accept_mapping() None¶
Keep only plain configuration values after the form closes.
spacr/qt/screens/embeddings.py:912
- EmbeddingsScreen._cell_dino_dialog.choose_file() None¶
Choose local weights without downloading or accepting terms.
spacr/qt/screens/embeddings.py:830
- EmbeddingsScreen._cell_dino_dialog.sync_width() None¶
Show the planes the chosen factory takes; none when adaptive.
spacr/qt/screens/embeddings.py:889
- EmbeddingsScreen._cell_dino_dialog.use_installed() None¶
Fill the file and its detected model; the digest is read on OK.
spacr/qt/screens/embeddings.py:848
- EmbeddingsScreen._dino_classifier_dialog.choose_table()¶
Choose an explicit row-aligned label and acquisition-group table.
spacr/qt/screens/embeddings.py:1618
- EmbeddingsScreen._dino_classifier_dialog.start()¶
Retain the form when a path or metadata validation refuses the run.
spacr/qt/screens/embeddings.py:1684
- EmbeddingsScreen._learn_from_well_labels.work()¶
Score and fit off the GUI thread.
spacr/qt/screens/embeddings.py:1247
- EmbeddingsScreen._load_cell_example.fetched(result, error) None¶
Read the plate once it is on disk, or say why it is not.
spacr/qt/screens/embeddings.py:2236
- EmbeddingsScreen._load_demo_crops.finished(answer)¶
Select the CPU example and attach its row-aligned labels.
spacr/qt/screens/embeddings.py:2193
- EmbeddingsScreen._load_demo_crops.work()¶
Unpack and read the small local example off the GUI thread.
spacr/qt/screens/embeddings.py:2180
- EmbeddingsScreen._on_planned.work()¶
Read the pages on a worker thread.
emitis a bound signal and the only thing here that reaches the GUI: Qt queues it onto the receiving thread, so the progress line moves without this function ever touching a widget.spacr/qt/screens/embeddings.py:2506
- EmbeddingsScreen._pretrain_dino._done(summary)¶
Accept only a completion belonging to this screen’s current run.
spacr/qt/screens/embeddings.py:1569
- EmbeddingsScreen._pretrain_dino._progress(epoch, total, loss)¶
Relay backend reports without touching widgets on the worker thread.
spacr/qt/screens/embeddings.py:1554
- EmbeddingsScreen._pretrain_dino.work()¶
Train off the GUI thread.
spacr/qt/screens/embeddings.py:1561
- EmbeddingsScreen._read_cell_example.finished(answer)¶
Select the CPU DINO choice and attach the annotations.
spacr/qt/screens/embeddings.py:2277
- EmbeddingsScreen._read_cell_example.work()¶
Plan, read and label the annotated crops.
spacr/qt/screens/embeddings.py:2257
- EmbeddingsScreen._read_database_choices.done(answer) None¶
Put the answer in the two combos.
spacr/qt/screens/embeddings.py:2368
- EmbeddingsScreen._read_database_choices.work()¶
Ask the database what it holds. Two cheap indexed reads.
The stat runs here, on the worker, because it is the part that blocks on a slow mount. A path that is not a file yet is not an error – it is one somebody is halfway through typing – so it answers with nothing to offer rather than raising, which would put a refusal in the status line for every keystroke.
spacr/qt/screens/embeddings.py:2353
- EmbeddingsScreen._save_classifier_application.finished(saved)¶
Report the exported snapshot only while its source result is current.
spacr/qt/screens/embeddings.py:2101
- EmbeddingsScreen._save_classifier_application.work()¶
Write every output column with saved class names and channel provenance.
spacr/qt/screens/embeddings.py:2085
- EmbeddingsScreen._save_for_similarity.finished(answer)¶
A newer crop selection keeps its own status and controls.
spacr/qt/screens/embeddings.py:2866
- EmbeddingsScreen._save_for_similarity.work()¶
Persist vectors without blocking the graphical thread.
spacr/qt/screens/embeddings.py:2855
- EmbeddingsScreen._show_classifier_application.show_view()¶
Render the selected real output without rerunning or changing the model.
spacr/qt/screens/embeddings.py:2045
- EmbeddingsScreen._subcell_channels_dialog.accept_mapping() None¶
Store only a complete four-index choice, then close.
spacr/qt/screens/embeddings.py:723
- EmbeddingsScreen._train_dino_classifier.cancelled()¶
Honor both the application’s worker cancellation and the local stop.
spacr/qt/screens/embeddings.py:1864
- EmbeddingsScreen._train_dino_classifier.done(summary)¶
Publish actual artifact metadata only to the run’s original crop selection.
spacr/qt/screens/embeddings.py:1880
- EmbeddingsScreen._train_dino_classifier.progress(epoch, total, train_loss, validation_loss)¶
Relay worker progress without reading or changing GUI widgets.
spacr/qt/screens/embeddings.py:1857
- EmbeddingsScreen._train_dino_classifier.work()¶
Filter the frozen row selection before calling the real trainer on raw planes.
spacr/qt/screens/embeddings.py:1870
- EmbeddingsScreen._use_embeddings.work()¶
Map or classify off the GUI thread.
spacr/qt/screens/embeddings.py:1083
- EmbeddingsScreen.embed.finished(answer)¶
Publish only to the crop selection that produced the vectors.
spacr/qt/screens/embeddings.py:2800
- EmbeddingsScreen.embed.work()¶
Run the backbone off the GUI thread.
The import is inside because it pulls torch in: a user who never opens this screen should not pay for it, and a user who does should pay for it once, here, rather than at launch.
spacr/qt/screens/embeddings.py:2783
- EmbeddingsScreen.load_crops.work()¶
Ask what matches. No pixels are read here.
spacr/qt/screens/embeddings.py:2437