Prepare a Pathologist GeoJSON
Produce one reviewable annotation GeoJSON and pair it with the exact H&E image whose level-0 pixel frame the coordinates use. GeoJSON contains vector annotations, not raster pixels; it cannot contain the H&E itself.
Establish the deliverable
Deliver at least:
case-id/
├── case-id_HE.ome.tif # or the original SVS/NDPI/MRXS understood by QuPath
├── case-id_annotations.geojson
└── legend-or-cover-note.txt
Use a QuPath project or ZIP around these files if the recipient prefers one package. Use a baked-overlay OME-TIFF only when toggleable annotations are unnecessary.
Register annotations to the H&E
Treat coordinate registration as the main correctness problem.
Identify what each annotation coordinate means: image pixels, microns, array-grid indices, thumbnail pixels, or another image's pixels.
Identify the exact H&E image to share and its level-0 width, height, pixel size, orientation, and crop.
Derive a transform into that image's level-0 pixel coordinates. Represent an affine transform as:
x_image = a*x + b*y + c y_image = d*x + e*y + fCheck x/y ordering explicitly. In image coordinates, x is column/horizontal and y is row/vertical; the origin is normally top-left.
Validate at least five asymmetric landmarks across the tissue, including corners and a biologically recognizable region. Never accept a transform based only on dimensions.
If annotations were created directly on the shared image, use the identity transform 1 0 0 0 1 0. If the transform is unknown, stop and register the data before creating the final GeoJSON.
Create one final GeoJSON per physical H&E image. Keep multiple ROIs or annotation methods in the same FeatureCollection as separate classified features; never combine coordinates from different slides.
Route by annotation format
- CSV/TSV with x, y, and labels: use the bundled table converter below.
- Existing GeoJSON polygons: preserve the geometries and combine same-image collections with the bundled merger.
- WKT or vector polygons in another format: read them with Shapely/GeoPandas or the relevant GIS library, apply the verified affine transform to every vertex, and serialize the resulting features with the same QuPath properties used by the table converter.
- Binary or labeled raster mask: extract contours with Rasterio, scikit-image, or OpenCV; preserve holes; transform mask-pixel coordinates into H&E level-0 pixels using the mask downsample and crop; simplify only enough to keep the file responsive without changing diagnostically important boundaries.
- Annotations created in QuPath: prefer QuPath's own GeoJSON export, then combine exports only if one final file is required.
For polygons and masks, compare total source area with transformed GeoJSON area after accounting for scale. Large disagreement usually indicates a level, units, or transform error.
Choose annotation geometry
- Preserve existing polygons whenever region boundaries already exist.
- Represent spatial-transcriptomics capture areas as squares or circles using their physical footprint converted to level-0 pixels:
size_px = size_um / image_mpp. - Represent cell detections as points only when the viewer and review task make points sufficiently visible; otherwise use cell outlines or small circles.
- Do not geometrically union different classes. Overlaps may be biologically meaningful.
- Use
annotationobjects for pathologist-review regions anddetectionobjects for individual cells or spots when that distinction matters downstream.
Convert a table into one GeoJSON
Use scripts/table_to_qupath_geojson.py for CSV/TSV data containing x, y, and class columns. Supply the annotation footprint explicitly rather than guessing it.
python3 scripts/table_to_qupath_geojson.py \
annotations.csv \
--output case-id_annotations.geojson \
--x-column px_col \
--y-column px_row \
--class-column cell_type \
--geometry square \
--size 60 \
--image-width 27552 \
--image-height 17696
Apply a known affine registration during conversion when needed:
python3 scripts/table_to_qupath_geojson.py annotations.tsv \
--output case-id_annotations.geojson \
--x-column x --y-column y --class-column label \
--geometry circle --size 40 \
--affine 3.48 0 125 0 3.48 84 \
--color-map class_colors.json
The color map must be a JSON object whose values are #RRGGBB strings or [r,g,b] arrays. Without one, the script assigns deterministic colors. Add identifiers or useful metadata with --id-column and repeated --property-column options.
Combine existing same-slide GeoJSON files
If annotations already exist as GeoJSON, use scripts/combine_geojson.py to create one FeatureCollection without changing its features:
python3 scripts/combine_geojson.py \
tumor.geojson necrosis.geojson immune.geojson \
--output case-id_annotations.geojson \
--source-property source_layer
Combine only layers registered to the same H&E pixel frame. This operation concatenates features; it does not register, reproject, union, or deduplicate them.
Validate before sharing
- Confirm the GeoJSON parses and the reported feature count matches the source annotation count.
- Open the exact H&E in QuPath.
- Import the GeoJSON with
File -> Import objects from file(wording varies slightly by QuPath version). - Confirm correct scale, orientation, crop, and position at widely separated landmarks.
- Confirm class names, colors, visibility, and opacity are usable.
- Check that annotations do not predominantly fall on slide background or outside image bounds.
- Save a QC screenshot and record the H&E filename, dimensions, pixel size, transform, source annotation version, and generation command in the cover note.
Do not share a final overlay that has only been validated numerically. A plausible feature count cannot detect mirrored, rotated, or consistently offset annotations.
Explain the review task
State what each object represents, the physical footprint or segmentation method, the annotation provenance, known registration uncertainty, requested pathology question, and how feedback should be returned. Keep the scientific question separate from technical opening instructions.