segmantR no longer starts or installs Python by itself. Without a
configured Python, recent 'reticulate' versions download one with 'uv',
and the backend checks (for example sg_protocol_list(check_available = TRUE) or a StarDist run through sg_predict_model()) triggered that.
The Cellpose, StarDist and Mesmer backends now count as available only
when Python is already running, when RETICULATE_PYTHON or
RETICULATE_PYTHON_ENV names one, or with options(segmantR.python = TRUE); see "Selecting Python" in ?sg_setup_python.
sg_segment_threshold() treats NA pixels, such as nodata values, as
background. An NA inside an object used to stop the morphological
opening with an error, and with method = "adaptive" it blanked the
part of the image below and to the right of it.
The TIFF reader behind sg_import_interchange() and dataset and
training masks reads compressed single-sample uint32 values from 2^31
as unsigned (they came back negative or NA), compressed float images
(which failed) and compressed palette images as their colour indices
(as uncompressed ones already were) instead of colour map values.
Planar-separate multi-sample images, which came back with wrong values,
and images with fewer than 8 bits per sample or compressed 64-bit
samples now signal a DTYPE_MISMATCH error.
On R < 4.5, SHA-256 digests from the 'openssl' fallback are plain
strings like those of tools::sha256sum() instead of carrying the class
"hash", and tools::sha256sum() is looked up at run time, so checks on
older R no longer report it as a missing object.
sg_roi_write_qupath() records the level of the written coordinates as
annotatr_level in the feature metadata, as sg_roi_write_geojson()
does in its level property; it used to record the ROI's own level.
A layer style with named colours keeps the colours of labels that are not in the layer yet; adding another label first used to drop them, so those labels later received palette colours.
The Macenko stain estimate of sg_stain_deconvolve() (the default
method) points the principal axes into the positive optical density
octant before it measures stain angles. With the arbitrary sign of the
axes, the stain vectors were wrong for about half of simulated H&E
images and haematoxylin and eosin were mixed; results change for those
images.
sg_create_annotation_task() with both region and initial_mask
crops the mask to the region before scoring patches; the
region-relative patches used to be scored on the wrong part of the mask.
Compressed (for example LZW) multi-sample TIFFs such as RGB images are
read with the integer values of their declared dtype, as uncompressed
ones are; they came back scaled to [0, 1]. This affects
sg_import_interchange() and dataset and training masks stored as TIFF.
Every exported function has an example. The examples run except where
they need Python (sg_setup_python(), sg_train_cellpose(),
sg_active_learning_loop() and the Cellpose, StarDist and Mesmer
segmentation calls, which stay in \dontrun{}); Shiny launches run only
in interactive sessions. The help topics example_images and
example_masks, which described data objects that do not exist, are
folded into ?sg_example_image. sgCanvasOutput() and
renderSgCanvas() explain a missing 'htmlwidgets'. Unused packages are
dropped from Suggests.
The README and CITATION.cff no longer cite the Zenodo DOI, which is not
registered yet; it will be added once the Zenodo record is published.
sg_run_app() restores the previous value of the option
segmantR.app_env when the app stops.
sg_example_image() and sg_example_mask() no longer reset the
caller's random number stream; the example data are unchanged.
PNG export in sg_export_mask() closes its graphics device when
drawing fails.
sg_setup_python() installs cellpose rather than the cellpose[gui]
desktop extra when gpu = TRUE, installs into the environment type it
created, and names use_condaenv() after creating a conda environment.
A layer created without labels keeps the colour of its style, so the
first ROI label added with sg_roi_layer_add() receives it instead of
a palette colour.
ROI constructors match the reserved ... names author, source and
id exactly; attributes such as idx or source_file no longer
overwrite them by partial matching.
sg_roi_intersect() and sg_roi_difference() return an ROI with an
empty polygon when the result is empty. They used to warn about a
repaired geometry and return an ROI without geometry that broke
sg_roi_area(), sg_roi_centroid() and layer summaries.
sg_read_envi() keeps a matrix when one band is read from a one-row or
one-column window; it used to drop to a vector and fail.
uint32 TIFF planes and 4-byte ENVI integers decode the value 2^31 (and
ENVI int32 -2^31) exactly instead of as NA. 8- and 16-bit reading is
unchanged.
sg_create_annotation_task() records the image's id as image_id
instead of NA.
sg_prepare_training_data() rounds tile offsets back to pixel indices
instead of truncating them, so tiles of images with a fractional
downsample are no longer cut one pixel too high or too far left.
Grouped dataset splits (sg_dataset_manifest(),
sg_prepare_training_data()) no longer leave a requested split empty
when there are enough groups to fill every split, for example three
subjects with split = c(train = 0.8, validation = 0.1, test = 0.1).
sg_roi_write_geojson() records the level of the written coordinates
in each feature's level property. It used to record the ROI's own
level, so sg_roi_read_geojson() rescaled ROIs drawn at a level other
than the export level.
sg_import_interchange() accepts bundles whose integer image was
exported with image_tiff; the float64 TIFF planes are restored to
integer storage before the content digest is checked.
sg_roi_mask() no longer rasterises right triangles (and other
polygons with two distinct x and y values) as their bounding rectangle;
the rectangle fast path now requires four corners and axis-aligned edges.
The morphological opening in sg_segment_threshold() (default
open = 5) clips its erosion and dilation windows at the image edge.
Objects touching the border keep their full extent instead of losing a
band of open %/% 2 pixels, border objects at least open %/% 2 + 1
pixels thick survive the opening, and images smaller than open in a
dimension (for example 3 x 10 pixels) no longer fail.
Bilinear upsampling in sg_preprocess() no longer extrapolates at the
top and left edges, where it could produce values outside the input
range (for example negative intensities).
The "ruifrok" stain matrix of sg_stain_deconvolve() (also the
Macenko fallback) now holds the haematoxylin and eosin vectors; its
second vector was the DAB vector, so the eosin channel measured DAB.
sg_stain_deconvolve() reads stain matrices column-wise, as documented,
for the built-in Ruifrok vectors, Macenko estimates and user matrices
alike; it used to mix rows and columns and returned wrong
concentrations. Macenko estimates put haematoxylin first.
Morphology features no longer fail on single-pixel cells (shape
descriptors are NA there), which also fixes sg_filter_cells() and
CSV export of such masks. Line-shaped cells get eccentricity 1 instead
of NA, so max_eccentricity removes them. Solidity uses the convex
hull of the pixel squares and circularity is capped at 1; both could
exceed 1 before.
sg_evaluate_segmentation() reports ap50 and ap75 as the
instance-segmentation average precision TP / (TP + FP + FN) used by
Cellpose and StarDist; they previously repeated the F1 score.
sg_segment_watershed() computes the distance transform inside the
foreground and floods only foreground pixels, level by level along the
surface. Previously every image came back as one object covering the
whole plane, and membrane_channel had no effect; results change.
The watershed fix above and the morphological opening fixes change the
masks of the bundled protocols watershed.distance.v1,
watershed.h_minima.v1, threshold.otsu.v1, threshold.triangle.v1
and threshold.adaptive.v1, although their ids, versions and protocol
digests are unchanged. Run records tell the results apart by
runtime$segmantR_version.
Standardize the named v5 package logo across README, documentation pages and available application headers, with matching light and dark artwork.
sg_image_channels(), sg_read_window(), sg_read_field() and
sg_read_fields() read rectangular pixel windows of single channels from
multi-channel TIFF/QPTIFF images through GDAL; sg_display_range(),
sg_slide_display_ranges() and sg_display_levels() derive display
ranges from slide samples and from the signal of called-positive cells.
imager moves to Suggests.
Adds independent sg_roi_* histology annotation objects, layers, project
provenance, geometry operations, GeoJSON/QuPath exchange and integer ROI masks.
A metadata-only frame records explicit pyramid dimensions and pixel calibration.
These APIs do not require hyperannotatR (formerly annotatR) or an HSI backend.
Existing instance-mask APIs remain unchanged. QuPath UUID derivation and legacy
metadata keys are retained.
Direct QuPath ROI exports retain imported measurements, metadata, image planes and nucleus geometry. ROI mask statistics reject invalid physical calibration.
Added an independent cell annotation canvas adapted from annotatR, with rectangle, polygon, freehand, circle, edit and erase tools. Corrections preserve image binding, reject overlaps and stale revisions, and enter the existing staged review workflow. No hyperannotatR dependency is required.
The sg_interchange_capabilities() profile partner_annotatR is renamed
partner_hyperannotatR for the planned HSI partner contract; its status
remains planned.
Physical areas use current array pixel calibration once after resampling. Level-0 geometry and OME spacing are unchanged; corrected downsampled measurement assets have new digests and require explicit revalidation. Resampling rejects nonfinite targets and unsupported anisotropic pixels before interpolation.
PNG mask previews allocate colours for present labels, including sparse IDs. Label cleanup avoids unnecessary integer overflow and rejects splits that would exhaust the supported label range before changing labels.
Generate canonical numeric digits from the exact binary64 value and apply explicit ties-to-even rounding, avoiding platform-dependent printf behaviour. Exact binary inputs cover Windows halfway cases and display-separator changes.
Canonical JSON numbers now use the shortest round-tripping decimal for large integer-valued doubles, use JSON parsing consistently across platforms, and ignore the display decimal separator. Corrected text changes digests for affected values; re-export affected manifests as new versions rather than rewriting historical checksums. Existing authenticated fixtures are unchanged.
Count and process only present mask labels without phantom feature or export rows. Filtering still renumbers retained objects and now maps border flags to those output labels. Reject non-finite, fractional, negative and out-of-range labels before integer conversion; full uint32 IDs above 2147483647 remain unsupported.
Preserve the exact bytes of authenticated synthetic QuPath fixtures during Git checkout, including when automatic CRLF conversion is enabled. Existing fixture contents and integrity digests are unchanged.
All additions are backward compatible: existing functions keep their
signatures and defaults, and objects created by segmantR 0.1.0 remain valid.
See system.file("interop", "INTEROP.md", package = "segmantR") for the
contracts, migration notes and security boundaries.
inst/schema/segmantR-interchange-v1/
(manifest, image, mask, legend, integrity, GeoJSON profile, run, runtime,
protocol, capabilities, dataset manifest, model card, QuPath run.json,
StarDist model manifest).sg_interchange_capabilities(), sg_interchange_manifest(),
sg_export_interchange(), sg_import_interchange(),
sg_validate_interchange() and sg_hash_assets(): bundles with exact
integer label TIFF, pixel-edge GeoJSON with stable object ids, legend,
canonical long-format measurements (CSV, optional Parquet), optional
OME-TIFF image and RDS, and a SHA-256 integrity.json inventory whose
digest follows the qupflowR bundle rule.sg_image gains optional id, plane, origin, bands,
value_semantics, source_digest, transform_digest, provenance;
sg_mask gains mask_type, legend, plane, origin, review state,
provenance, id.sg_mask_legend(),
sg_mask_revision(), sg_mask_status(), sg_stage_mask(),
sg_review_mask(), sg_replace_mask(). Imports and predictions are staged;
reviewed masks are only replaced with the expected revision and
overwrite = TRUE.inst/protocols/: threshold.otsu.v1,
threshold.adaptive.v1, threshold.triangle.v1, watershed.distance.v1,
watershed.h_minima.v1, propagate.voronoi.v1,
postprocess.label-cleanup.v1, and the optional stardist.2d.v1,
cellpose.2d.v1, mesmer.2d.v1.sg_protocol_list(), sg_protocol_schema(), sg_protocol_get(),
sg_protocol_validate(), sg_protocol_run() with schema-valid run
envelopes that record the exact delegate call; sg_cleanup_labels().sg_select_channel() with registered band operations, sg_read_envi()
and sg_envi_info() for windowed ENVI reads with explicit value semantics.sg_dataset_manifest(), sg_prepare_training_data() and
sg_validate_training_manifest() with grouped, leakage-free splits.sg_train_stardist() (optional Python runtime, checked after all data
checks) and the backend-agnostic sg_predict_model().sg_package_model() now writes a versioned model_card.json,
runtime.json, dataset_manifest.json, license.json and
checksums.sha256 with relative paths (weights keep their directory
structure or are referenced by hash); sg_load_model() checks entry
names, checksums, card version and backend. Archives from 0.1.0 still load
(reported as unverified).sg_export_qupath()/sg_import_qupath() and readable Groovy templates
(inst/qupath/) for interactive and QuPath script runs driven by
run.json; sg_stardist_capabilities(), sg_stardist_manifest(),
sg_stardist_parameter_map(). Exercised with QuPath 0.7.0 and
qupath-extension-stardist 0.6.0 in an opt-in test lane.sg_app() builds the app from package code (protocol form from the
registry, staged review with explicit overwrite, bundle export);
sg_run_app() launches it. sg_control_capabilities() reports the planned
control service.sg_segment_propagate() gains engine; the default "auto" keeps the
previous behaviour.sg_apply_corrections() and sg_filter_cells() keep the image binding,
legend and review history of the input mask.sg_segment_threshold(method = "adaptive") no longer errors with
"replacement has length zero". The internal integral-image lookup now
treats out-of-range indices as contributing zero, so adaptive
thresholding works for all images.sg_read_image() returned transposed arrays for images read through
imager or EBImage (both store [x, y]); arrays are now [y, x, channel].sg_preprocess() now keeps the history it documents.sg_segment_cellpose() works with Cellpose 3.x, whose eval() no longer
accepts tile..segmantR archives).