hepatocyte-app
Deep-learning segmentation and cell morphometry on liver H&E whole-slide images — five morphological classes, a watershed instance count, and a cohort viewer — built out end to end on a cohort of four slides, and carrying no licence file at all.
Purpose
Segment liver H&E slides into morphological classes and count instances per class. The five emitted classes are balloon dystrophy, mesenchymal cells, non-nuclear, relatively normal, and steatosis (plus background), which places the task in the steatohepatitis family rather than the tumour-classification family.
The scope limitation is stated explicitly by the project itself, and that is worth recording rather than merely praising. Both CITATION.cff and .zenodo.json on the companion extension say the model emits five morphological classes and that no clinical grade or score (NAS, SAF, fibrosis) is derived, and both mark the work research-and-education-only. Against a wiki that keeps finding models whose stated claims outrun their evidence, an explicit non-claim — naming the score the output could be mistaken for, and disclaiming it — is the unusual thing here.
This is a different task on the same cohort as Liver Tru-Cut — Tumour Origin AI. Both read from K:\liver-tru-cut-medical; that project asks which primary a metastasis came from, this one segments hepatocyte morphology. They are siblings sharing a data root, not the same work.
Data used
The liver tru-cut cohort on the K: drive, in three stages — rawData/, extractedData/, and data/hepatocyte-app-pipeline-output/. Four slides have been extracted and all four processed, so the pipeline is built out well ahead of the cohort. Nothing here is a result yet; there is no evaluation, no validation split and no accuracy figure anywhere in the repository.
Slides carry ANON-prefixed pseudonyms throughout — in the committed test request, in the pipeline output paths and in the case directories. No accession number appears anywhere.
The clinical source of truth is a FileMaker Pro database (filemaker_io.py), read either from exports or live over ODBC. The module is a reader only. Its credential parameters default to empty strings and every credential appearing in the repository is a placeholder in a docstring — pwd="yourpassword", pwd="...", pwd="secret" — so no secret is committed. That was checked across all tracked files rather than assumed, which matters because this repository is public.
Methods
Verified against the source rather than the README.
- Inference. A TorchScript checkpoint (
weights/baseline-1-jit.pth, ~180 MB) applied patch-wise at 512 px, batched, with patches reassembled into large canvases. - Morphometry. Per class, a binary mask is cleaned, then instances are separated by distance transform → marker detection → watershed (
morphometry/separator.py, usingscipy.ndimageandskimage.segmentation.watershed). Markers come from eitherpeak_local_maxor h-maxima depending on configuration, with a fallback to plain connected components when no peaks are found. Counts are the number of labels per class, written to an Excel report. Background (class 0) is excluded by configuration. - Tunables live in
morphometry/config.pyas a per-class dataclass —min_cell_area,min_distance,morph_kernel_size,h_threshold,use_h_maxima, erosion options — with defaults of 100 px minimum area and 15 px minimum distance, overridable per class. Worth knowing: the instance count is a function of these thresholds, so any count is only comparable against another run with the same configuration. - Viewer. A Flask cohort backend (
viewer/server.py) serving the whole cohort with no pre-generated tile files, plus a static snapshot mode (viewer/build_viewer.py) for handing one case to a colleague. The backend exists for a concrete reason recorded inCLAUDE.md: ~50k JPEG tiles per slide against a 256 KB cluster on theK:drive would waste roughly 13–14 GB per slide, so tiles are generated in memory instead. - Entry point.
main.pywith--case,--slide,--force,--listand--no-mpp; slides are discovered by walking the extracted tree and a slide counts as processed when itsmorphometry_report.xlsxexists.
Magnification handling, which is the transferable part
The model has a target resolution and the cohort does not have a single one. Rather than assume, needs_mpp_run() reads openslide.mpp-x from each slide, compares it against target_mpp = 0.25 µm/px, and skips slides already at that scale (40×, ratio ≈ 1.0). For a 20× slide it generates a second inference pass into predict_patches_mpp_adjusted/, and the viewer discovers any predict_patches_* directory and offers each as a toggle — so a pathologist compares native-resolution against 40×-equivalent inference on the same slide.
rerun_with_mpp.py does the field-of-view matching explicitly: read the full 512×512 level-0 area, downscale to ~256 to match the field of view, upscale back to 512 for model input, so the returned mask maps 1:1 onto the level-0 area. CLAUDE.md records that an earlier version read only 256 px at level 0 and produced misaligned overlays.
Showing both runs rather than silently picking one is the honest handling of a confound, and it is the direct counterpart to the fixed-downsample bug documented on Patch Extraction — same institution, same class of problem, opposite outcome.
Current state / open questions
No licence file exists in this repository. There is no LICENSE, no COPYING, and no licence statement in README.md or CLAUDE.md. The repository is public, so default copyright applies and no reuse is permitted.
That matters more than it would on its own, because of an asymmetry with the companion QuPath Hepatocyte Extension: the extension is Apache-2.0-intended, ships a catalog.json for public installation into QuPath, and carries Zenodo metadata inviting citation — while every line of substance it drives lives here, unlicensed. So the distributable wrapper offers rights over a dependency that grants none. Anyone who installs the extension from its catalog cannot legally use what it calls. Both halves are the owner’s to resolve; the asymmetry is the clearer statement of the problem than either half alone.
Neither repository is runnable by an outsider. The TorchScript checkpoint is gated — “🔒 available on request from the maintainer” — so a reader can inspect the code and cannot reproduce anything. That is consistent with the stated research-and-education scope, but it is what “public” means here in practice, and it should not be mistaken for a reproducible release.
A note on authorship, which bears on the licence question rather than on quality. morphometry/config.py carries a Russian-language docstring in an otherwise English codebase, which suggests that module did not originate with the repository owner. Who wrote which parts is not recorded anywhere, and provenance is exactly what a licence decision needs to rest on. [unverified]
The instance-count method has a documented ceiling nobody has tested here. Watershed on a distance transform is the standard way to split touching nuclei and it fails predictably on clumped, overlapping or highly irregular objects. Ballooned hepatocytes and macrovesicular steatosis are large and irregular by definition, which is the regime where marker-based separation is least reliable. No agreement study, no manual count comparison and no sensitivity analysis over min_distance / min_cell_area exists in the repository, so the counts are currently unvalidated outputs rather than measurements.
K:/hepatocyte-app-qupath is a QuPath project directory, not this extension — it holds project.qpproj, classifiers/, data/, scripts/ and reports/, and it is deliberately not a git repository. That matches the rule on QuPath Tooling that .qpproj files for cohorts containing identifiers stay off git and on the share.
Open questions for the next review: whether the cohort grows beyond four slides and what the evaluation protocol will be when it does; whether counts will be compared against a manual reference before any of them are reported; whether a licence is added here, which is the blocking issue for the extension being installable in good faith; and whether the FileMaker database becomes the join key linking this project’s outputs to Liver Tru-Cut — Tumour Origin AI’s cohort table.
Related: QuPath Hepatocyte Extension — the distribution layer that drives this pipeline from inside QuPath, and where the licence-text and catalog findings live.
Related: Liver Tru-Cut — Tumour Origin AI — the sibling project on the same K:\liver-tru-cut-medical cohort, asking a different question of the same slides.
Related: Patch Extraction — this project’s per-slide MPP check and dual-run comparison are the worked counterexample to the fixed-downsample bug recorded there.
Related: QuPath Tooling — the cluster index, which now carries this pair.
Related: Memorial LIS Extracts — the FileMaker database here plays the same role for the liver cohort that the LIS extracts play elsewhere, and neither is currently linked to the other.
Derived from: working tree read 2026-07-26 at commit a3518a9 (2026-07-12) — README.md, CLAUDE.md, main.py, filemaker_io.py, segmentation/pipeline.py, morphometry/{config,counter,separator}.py, viewer/{server,build_viewer,rerun_with_mpp}.py, qupath/{test_request.json,test_result.geojson}, .gitmodules, plus git grep over all tracked files for credentials and the GitHub REST API for visibility and licence state.