Changes in version 0.2.0 - 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. Interchange (segmantR-interchange-v1) - Public JSON Schema family under 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. - Staged/reviewed workflow with content revisions: 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. Declarative protocols (segmantR-protocol-v1) - Registry in 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. Training, models and prediction - 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). QuPath and StarDist - 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. Shiny - 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. Changes in behaviour - Attaching the package no longer initialises Python to list backends; optional backends are checked when used. - 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. Bug fixes - 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. Changes in version 0.1.0 - Initial release. - Classical segmentation: adaptive thresholding, watershed, Voronoi propagation. - Deep learning wrappers: Cellpose, StarDist, Mesmer (via reticulate). - Human-in-the-loop annotation and correction workflow. - Custom model training and portable model export (.segmantR archives). - Feature extraction: intensity, morphology, texture, location. - Shiny application with six tabs for interactive segmentation workflows. - Export to GeoJSON, QuPath, CSV, SpatialExperiment. - Bundled synthetic sample data for demonstrations.