QuPath Hepatocyte Extension

A QuPath 0.6+ extension that runs the hepatocyte-app liver segmentation model on selected annotations and imports the result back as classified polygons — packaged for public distribution, but its catalog points at a release that does not exist and its licence file is not the Apache text it says it is.

Purpose

The distribution layer for hepatocyte-app. It adds an Extensions → Hepatocyte menu to QuPath with two working entries — import a folder of slides into the project and set them to brightfield H&E, and run inference on the selected area annotation(s) — plus a Settings dialog and two further items marked “Coming in a later release”.

It exists as a separate repository, and is consumed by the app as a git submodule pinned at 06dd4a0 (v0.1.0-1-g06dd4a0). It gets its own page rather than a paragraph on the app’s page because it is a separately citable artefact — its own tag, CITATION.cff, .zenodo.json and distribution catalog.json — and because two of the findings below live nowhere else.

Inference runs outside QuPath. The extension writes a request JSON, invokes the app’s Python bridge (hepatocyte_qupath_inference.py) in the app’s own venv, and reads back GeoJSON polygons which it imports as classified annotations with per-class cell-count measurements. That design is worth noting: QuPath is the interface and the annotation store, not the compute environment, so the extension never needs to bundle PyTorch.

Data used

None of its own. It operates on whatever project the user has open, and delegates to the hepatocyte-app checkout whose path is set in its Settings dialog. The committed test_request.json and test_result.geojson in the app repository are the round-trip fixtures; the GeoJSON carries only objectType, classification and parent_roi_id — no patient fields.

Methods

Java against QuPath 0.6.0, built with Gradle (Kotlin DSL), JavaFX 21, group io.github.sbalci, version 0.1.0. Three Java classes (HepatocyteExtension, HepatocyteConfig, HepatocyteSettingsDialog), a META-INF/services registration, and two Groovy resource scripts (import-slides.groovy, run-inference.groovy) that carry the actual behaviour. QuPath dependencies are compileOnly, which is correct for an extension.

Current state / open questions

Created 2026-07-12 and last pushed the same day; this is a one-day-old artefact at the time of review, tagged v0.1.0. That context matters for both findings below — they read as an unfinished release process rather than neglect.

The catalog points at a release that does not exist

catalog.json is the file QuPath reads when a user adds this as an extension catalog. It advertises release v0.1.0 with the asset

https://github.com/sbalci/qupath-extension-hepatocyte/releases/download/v0.1.0/qupath-extension-hepatocyte-0.1.0.jar

Checked against the GitHub API on 2026-07-26: the tag v0.1.0 exists, the repository has zero releases, and that URL returns HTTP 404. README.md compounds it, pointing readers to the Releases page for the JAR and displaying a release badge.

So a user who adds the catalog to QuPath sees the extension offered and cannot install it. The JAR itself is fine and already built — build/libs/qupath-extension-hepatocyte-0.1.0.jar, 29,791 bytes, dated 2026-07-12 — so this is a publishing step that was never run, not a build problem. Creating the GitHub release and attaching that JAR closes it.

Context added 2026-07-26, and it strengthens rather than softens this. The same owner has cut thirteen releases with attached JARs on QuPath Atölye Extension and a working release on QuPath Patoloji Atlası Extension, with both catalogs resolving. The release process is established and routine here; this repository is the one where it was not run. That makes it an unfinished step rather than an unfamiliar one — and the shortest path is to copy what those two already do.

The LICENSE file is not the Apache 2.0 text it declares

To be precise about what is and is not in question: the intent is not ambiguous — five separate declarations say Apache-2.0 and nothing contradicts that. What is defective is the file, and since a licence file is the instrument that actually makes the grant, a defective file is a real problem rather than a cosmetic one. The distinction matters for the fix: this needs one file replaced, not a decision made.

Update, 2026-07-26 — this is not a one-off, it is a template. The LICENSE here and the one in QuPath Atölye Extension are byte-identical, md5 f4ce3d08f26e7b19c13bdd5c5d849052, and that file carries all four alterations. So a defective Apache template was reused across the group’s two Apache-intended QuPath extensions. A depth-3 scan of every LICENSE under D:/ and K:/ found exactly those two copies and no others, so the spread is currently bounded — but the fix is to replace the template and then re-check any repo scaffolded from it, not to patch this file alone. Where the template came from is unrecorded. [unverified]

Every human-readable declaration says Apache-2.0 — CITATION.cff, .zenodo.json, the README badge, the README’s own Licence section, and the boilerplate at the foot of LICENSE itself. But the GitHub API reports license: NOASSERTION, meaning its detector could not match the file to a known licence.

Diffing the file against the canonical text fetched from apache.org shows why. The operative wording differs in four places, verified independently and reproduced by a second check:

Section Canonical Apache-2.0 This file
§1, “Contribution” “for the purpose of discussing and improving the Work” “for the purpose of tracking or improving the Work”
§4 redistribution retain “all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain…” “all copyright, license, in any NOTICE text file distributed as part of the Derivative Works”
§6, Trademarks “except as required for reasonable and customary use in describing the origin” “except as required for describing the origin”
§9 heading “Accepting Warranty or Additional Liability “Accepting Warranty or Support

The §4 change is the substantive one: it drops the requirement to retain patent, trademark and attribution notices, and the replacement is grammatically broken — it grafts on a fragment of the separate NOTICE-file clause.

Three qualifications, because this is easy to overstate:

  • The line count is not the evidence. The file is 188 lines against the canonical 202, but 11 of those 14 lines are the normal, expected replacement of the instructional APPENDIX with a filled-in copyright notice, which every correctly-licensed Apache project does. The word-level diff of §§1, 4, 6 and 9 is the finding; the line count alone would mislead.
  • Intent is not determinable from the text. A bad template, a manual retype, and a generated file are all consistent with what is there. Nothing here supports a claim about why.
  • Which text governs is not a question to settle here. Every other declaration says Apache-2.0 and the file’s own trailing paragraph says Apache-2.0, while its operative sections say something else. That is a conflict to route to whoever advises on licensing, not one to resolve in favour of the tidier reading. The practical fix is one file copied from apache.org/licenses/LICENSE-2.0.txt, keeping the existing copyright line.

The dependency it distributes is unlicensed

This extension is Apache-2.0-intended and packaged for public installation. hepatocyte-app, which holds the model bridge and every line of substance the extension drives, has no licence file at all. The wrapper offers rights over a dependency that grants none, so installing from the catalog would not confer permission to use what it calls. Of the two licence problems here, this is the one with a concrete consequence.

Note also that neither repository is runnable by an outsider regardless: the TorchScript checkpoint is gated behind “available on request”, and the README says so plainly.

Smaller things

  • The Zenodo and CITATION metadata are unusually complete for a v0.1.0 — ORCID, affiliation, keywords, a references block correctly citing Bankhead et al. 2017 for QuPath itself, and an explicit statement that no NAS/SAF/fibrosis score is derived. The scope discipline is the same as on hepatocyte-app and it is the right instinct.
  • Two menu items ship as “Coming in a later release” placeholders — whole-project inference and tissue auto-detection.

Open questions: whether the release gets cut so the catalog resolves; whether the licence file is replaced with canonical text and a licence added to the app; and whether the extension will be submitted to the QuPath community catalog, which would make both of the above prerequisites rather than housekeeping.

Related: hepatocyte-app — the pipeline this drives, the source of the model and the Python bridge, and the unlicensed dependency described above.

Related: QuPath Tooling — the cluster index this now appears on.

Related: QuPath Annotation Workflow — this extension reads QuPath area annotations and writes classified polygons back, so it sits inside that workflow rather than beside it, and GeoJSON is again the interchange format.

Derived from: submodule working tree read 2026-07-26 at 06dd4a0LICENSE (diffed against the canonical text fetched from apache.org), README.md, CITATION.cff, .zenodo.json, catalog.json, build.gradle.kts, gradle.properties, src/main/java/** and src/main/resources/scripts/*.groovy, plus the GitHub REST API for releases, tags and licence state and an HTTP check of the catalog’s advertised asset URL.