spacr.qt.widgets.model_share

Add a local model to the zoo, and optionally share it on Hugging Face.

WHY THERE IS NO BUILT-IN TOKEN. Uploading to one account from every user’s machine would mean shipping a write token inside spaCR. A write token is not an upload permit: it can also rewrite and DELETE every model in that account, and anyone who installs spaCR can read it out of the package. So the upload uses the token belonging to whoever is running it:

  • huggingface-cli login, or the HF_TOKEN / HUGGING_FACE_HUB_TOKEN environment variable.

It publishes to SHARE_REPO when that token may write there – the owner can grant that per person in the repository’s settings – and otherwise to the uploader’s own namespace, which always works and is never destructive to somebody else. Either way the model card carries the same table, so a shared model is readable in the same terms as a bundled one.

Functions

card(→ str)

The model card, carrying the same table every spaCR model card uses.

central_contribute(→ str)

Send a contribution folder through the central upload Space.

central_upload(→ str)

Publish through the central endpoint. Returns its reply.

community_layout(→ str)

"boxes" for figure pages with well boxes, "masks" for everything else.

community_name(→ str)

A community target name as it appears in a repository id.

community_readme(→ str)

A README for a new community dataset, in the layout this module writes.

community_repo(→ str)

The dataset repository community training data for target goes to.

conscience_for(→ str)

The short text shown beside Upload, asking for careful annotations.

contribute(→ str)

Send a contribution folder as a pull request. Returns its URL.

ensure_community_repo(→ str)

Make sure the community dataset exists; create it with a README if not.

find_token(→ Optional[str])

The uploader's own Hugging Face token, or None.

masks_dataset_target(→ str)

The community target a user-named image-and-mask dataset goes to.

pack_training_data(→ str)

Pack a training-data folder into ONE uncompressed tar. Returns its path.

pair_images_and_masks(→ Dict[str, Any])

Match an images folder to a masks folder by file name.

pairs_match(→ bool)

Whether a pair_images_and_masks() report may be uploaded.

read_label_mask(→ Any)

A label mask file as a 2-D array.

seed_changes(→ Dict[str, Any])

Which of spaCR's proposed plaques the contributor kept, edited or removed.

share(→ str)

Upload a checkpoint and its card. Returns the model page URL.

slugify(→ str)

A repository-safe folder name.

target_repo(→ Tuple[str, bool])

Where this token may publish: the shared repo, or its own namespace.

write_contribution(→ pathlib.Path)

Lay a contribution out on disk exactly as the dataset README describes.

yolo_lines(→ list)

Well boxes as YOLO label lines, 0 cx cy w h normalised to 0-1.

Module Contents

spacr.qt.widgets.model_share.card(fields: Dict[str, Any], filename: str, sha256: str, repo_id: str, folder: str) → str[source]

The model card, carrying the same table every spaCR model card uses.

Parameters:
  • fields – the share form’s values: display_name, kind, trained_on, notes and the metrics (f1, aji, dice, train_loss, val_loss and the rest); a missing value is shown as unstated.

  • filename – file name of the checkpoint; also the heading when no display_name is given.

  • sha256 – hex SHA-256 digest of the checkpoint, printed on the card.

  • repo_id – Hugging Face repository the model is published to; accepted but not written into the card.

  • folder – folder inside the repository that holds the checkpoint.

spacr.qt.widgets.model_share.central_contribute(folder: Any, target: str) → str[source]

Send a contribution folder through the central upload Space.

For a contributor with no Hugging Face login: the folder goes to the Space (see tools/model_upload_space/) as one uncompressed tar, and the Space checks it and opens the pull request with its own token. The Space accepts only figures, plaques and community_<name> targets, about MAX_CONTRIBUTION_BYTES, the file types write_contribution() writes, and every image paired with its mask or labels and its meta file.

Parameters:
Returns:

the pull request’s URL.

Raises:

RuntimeError – when no endpoint is configured, the folder is over the limit, or the Space refuses it (with the Space’s reason).

spacr.qt.widgets.model_share.central_upload(path: str, fields: Dict[str, Any]) → str[source]

Publish through the central endpoint. Returns its reply.

Speaks the Gradio HTTP API directly with urllib rather than through gradio_client, because that package is not a spaCR dependency: when it was missing this raised, the caller fell back to the uploader’s own token, and the model went somewhere nobody was looking for it. A publish path that depends on an optional import is a publish path that silently does something else.

Parameters:
  • path – local checkpoint file to upload.

  • fields – the share form’s values; display_name, kind, trained_on and contact are sent as their own arguments, the whole mapping as JSON, and a non-empty train_data_dir is packed with pack_training_data() and uploaded too.

spacr.qt.widgets.model_share.community_layout(target: str) → str[source]

"boxes" for figure pages with well boxes, "masks" for everything else.

Parameters:

target – as for community_repo().

spacr.qt.widgets.model_share.community_name(name: str) → str[source]

A community target name as it appears in a repository id.

Parameters:

name – what the user or the caller calls the collection, e.g. "Toxoplasma PV"; lower-cased, and every run of characters other than a-z and 0-9 becomes one underscore.

spacr.qt.widgets.model_share.community_readme(target: str, *, purpose: str = '') → str[source]

A README for a new community dataset, in the layout this module writes.

Parameters:
  • target – as for community_repo().

  • purpose – what the data will train, one sentence; a general sentence when empty.

spacr.qt.widgets.model_share.community_repo(target: str) → str[source]

The dataset repository community training data for target goes to.

One dataset repository per collection, not folders of one repository: every spaCR training set on Hugging Face is its own dataset repository beside the model it trains, and each upload arrives on it as a pull request. "figures" and "plaques" are Plaque Assay’s two; any other name is einarolafsson/community_<name>.

Parameters:

target – "figures", "plaques", a name, or a full owner/repo id (used as it is).

spacr.qt.widgets.model_share.conscience_for(target: str) → str[source]

The short text shown beside Upload, asking for careful annotations.

Callers translate it with tr().

Parameters:

target – as for community_repo().

spacr.qt.widgets.model_share.contribute(folder: Any, target: str, token: str | None = None) → str[source]

Send a contribution folder as a pull request. Returns its URL.

A pull request rather than a commit, whoever sends it: every contribution is reviewed before it becomes training data, and any logged-in Hugging Face user may open one on a public dataset, so the uploader’s own token is enough and no write token ships with spaCR. A dataset that does not exist yet is created first by ensure_community_repo(), which only its owner can do.

The contributor’s own login is preferred. With no login, or when the login cannot create a dataset that does not exist yet, the folder goes through the central upload Space instead (central_contribute()), which opens the same pull request with its own token.

Parameters:
spacr.qt.widgets.model_share.ensure_community_repo(target: str, token: str, *, purpose: str = '') → str[source]

Make sure the community dataset exists; create it with a README if not.

Only the owner of the einarolafsson namespace can create one; for anyone else a missing repository is an error that says who to ask.

Parameters:
Returns:

the dataset’s URL.

spacr.qt.widgets.model_share.find_token() → str | None[source]

The uploader’s own Hugging Face token, or None.

spacr.qt.widgets.model_share.masks_dataset_target(name: str) → str[source]

The community target a user-named image-and-mask dataset goes to.

Always community_<name>, so a dataset the user calls “figures” or “plaques” never lands in Plaque Assay’s two collections by accident.

Parameters:

name – what the user calls it, e.g. "Toxoplasma vacuoles GFP".

Returns:

a target community_repo() maps to einarolafsson/community_<name>.

Raises:

ValueError – when name has no letters or digits.

spacr.qt.widgets.model_share.pack_training_data(folder: str, out_dir: str | None = None) → str[source]

Pack a training-data folder into ONE uncompressed tar. Returns its path.

Uncompressed on purpose: microscopy TIFFs are already poorly compressible and gzipping tens of gigabytes costs far more time than it saves bytes. Raises if the result would be larger than the endpoint accepts, BEFORE the upload is attempted, so a user does not wait out a transfer that was never going to be accepted.

Parameters:

folder – training-data folder to pack; it must exist, and its files must total no more than MAX_TRAIN_BYTES.

spacr.qt.widgets.model_share.pair_images_and_masks(images_dir: Any, masks_dir: Any) → Dict[str, Any][source]

Match an images folder to a masks folder by file name.

An image and a mask belong together when their names match without the extension, so a.png pairs with a.tif. Nothing is read but the folder listings.

Parameters:
  • images_dir – the folder of images.

  • masks_dir – the folder of label masks.

Returns:

pairs ((image, mask) paths, in name order), images and masks (the counts), no_mask (images with no mask of the same name), no_image (masks with no image), duplicates (names that match more than one file on one side), and problem (why the folders cannot be read, else empty). The folders match when problem, no_mask, no_image and duplicates are all empty and there is at least one pair.

spacr.qt.widgets.model_share.pairs_match(report: Dict[str, Any]) → bool[source]

Whether a pair_images_and_masks() report may be uploaded.

Parameters:

report – the report.

Returns:

True when every image has exactly one mask of the same name, every mask one image, and there is at least one pair.

spacr.qt.widgets.model_share.read_label_mask(path: Any) → Any[source]

A label mask file as a 2-D array.

Parameters:

path – a .tif/.tiff, .npy or ordinary image file.

Returns:

the labels; a colour image keeps its first channel.

spacr.qt.widgets.model_share.seed_changes(seed: Any, final: Any) → Dict[str, Any][source]

Which of spaCR’s proposed plaques the contributor kept, edited or removed.

Parameters:
  • seed – the label mask spaCR proposed, or None when it proposed nothing.

  • final – the label mask being contributed, same shape.

Returns:

{"kept", "edited", "removed"} as lists of seed label ids, and "added": how many final labels overlap no seeded plaque.

spacr.qt.widgets.model_share.share(path: str, fields: Dict[str, Any], token: str) → str[source]

Upload a checkpoint and its card. Returns the model page URL.

Parameters:
  • path – local checkpoint file to upload.

  • fields – the share form’s values; display_name names the staging folder and the whole mapping fills the model card.

  • token – Hugging Face access token used for every call.

spacr.qt.widgets.model_share.slugify(text: str) → str[source]

A repository-safe folder name.

Parameters:

text – display name or file name to convert; it is lower-cased and every run of characters other than a-z and 0-9 becomes one hyphen. An empty result becomes "model".

spacr.qt.widgets.model_share.target_repo(token: str) → Tuple[str, bool][source]

Where this token may publish: the shared repo, or its own namespace.

Parameters:

token – Hugging Face access token; it decides whether the shared repository is reachable and, if not, whose namespace is used.

Returns:

(repo_id, is_shared).

spacr.qt.widgets.model_share.write_contribution(target: str, items: Any, dest: Any, *, consent: Dict[str, Any], contribution_id: str = '', notes: str = '') → pathlib.Path[source]

Lay a contribution out on disk exactly as the dataset README describes.

Refuses the whole contribution when any image has no annotation: an image without boxes or masks teaches a model that it holds nothing, which is almost never true of an image somebody chose to send. In the masks layout it also refuses, before writing anything, any image whose mask is not the image’s own size, naming each one with both sizes: a mask of another size does not lie on its image.

Parameters:
  • target – "figures" (boxes layout), "plaques" or any other community name (masks layout); see community_repo().

  • items – one mapping per image. Boxes layout: name, image (the H x W x 3 page the boxes were drawn on), boxes (pixel (x0, y0, x1, y1)), and optionally source, paper (DOI, citation, PMCID) and provenance (which proposed boxes were kept, moved or deleted). Masks layout: name, source (the original file, copied unchanged), labels (the label mask) and optionally seed (spaCR’s proposed mask) and extra (anything else to record in the image’s meta file).

  • dest – the folder the contribution folder is made in.

  • consent – what the contributor agreed to; recorded verbatim in contribution.json.

  • contribution_id – the folder name; a date and a random suffix when empty.

  • notes – the contributor’s own words about the images, recorded in contribution.json when given.

Returns:

the contribution folder.

spacr.qt.widgets.model_share.yolo_lines(boxes: Any, width: int, height: int) → list[source]

Well boxes as YOLO label lines, 0 cx cy w h normalised to 0-1.

Parameters:
  • boxes – (x0, y0, x1, y1) pixel boxes; corners in either order, clipped to the image.

  • width – image width in pixels.

  • height – image height in pixels.

Returns:

one line per box with a positive area, class 0 (“plaque well”).