Metadata-Version: 2.4
Name: CervicalDoctor
Version: 0.4.0
Summary: Single-patient cervical image filtering, lesion segmentation, and multimodal diagnosis pipeline
Author: Shiminghai
License-Expression: AGPL-3.0-only
Project-URL: Documentation, https://pypi.org/project/CervicalDoctor/
Keywords: cervical,colposcopy,segmentation,diagnosis,medical-imaging
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Healthcare Industry
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: licenses/ULTRALYTICS_LICENSE
License-File: licenses/CERVICALDC_THIRD_PARTY_NOTICES.md
Requires-Dist: torch>=2.5
Requires-Dist: torchvision>=0.20
Requires-Dist: transformers<6,>=4.57
Requires-Dist: safetensors>=0.7
Requires-Dist: huggingface-hub>=0.34
Requires-Dist: cervicalseg>=0.2.0
Requires-Dist: numpy<3,>=1.26
Requires-Dist: Pillow>=10
Requires-Dist: PyYAML>=6
Requires-Dist: tqdm>=4.66
Requires-Dist: opencv-python-headless>=4.10
Requires-Dist: matplotlib>=3.8
Requires-Dist: scipy>=1.11
Requires-Dist: requests>=2.31
Requires-Dist: psutil>=5.9
Requires-Dist: pydicom>=3.0
Requires-Dist: pylibjpeg>=2.0
Requires-Dist: pylibjpeg-libjpeg>=2.3
Requires-Dist: pylibjpeg-openjpeg>=2.4
Provides-Extra: training
Requires-Dist: scikit-learn>=1.4; extra == "training"
Provides-Extra: build
Requires-Dist: build>=1.2; extra == "build"
Requires-Dist: twine>=6; extra == "build"
Dynamic: license-file

# CervicalDoctor

CervicalDoctor provides one complete single-patient pipeline and four independent tasks:

```python
import CervicalDoctor

CervicalDoctor.pipeline(...)
CervicalDoctor.detect(...)
CervicalDoctor.acetic(...)
CervicalDoctor.seg(...)
CervicalDoctor.classify(...)
```

The diagnosis class order is always `Ca, HSIL, LSIL, Normal`. Python reserves the word `class`, so
the public diagnosis function is named `classify`.

## Installation and model assets

Install the code package from PyPI:

```bash
pip install CervicalDoctor
```

The PyPI wheel intentionally does not contain the approximately 17 GB model bundle. Until the model
bundle is published separately, point the installed package at an existing CervicalDoctor asset
directory whose direct child is `ckpt/`:

```bash
export CERVICALDOCTOR_HOME=/path/to/CervicalDoctor-assets
```

The source checkout detects its own `ckpt/` automatically. Installed builds otherwise look under
`~/.cache/cervicaldoctor/`. A missing model raises an explicit error showing the expected path.

## Input contract

Except for standalone `acetic`, `image_path` may be either:

- one `.jpg`, `.jpeg`, `.png`, `.bmp`, `.dcm`, or `.dicom` file; or
- one folder whose direct image children all belong to the same patient.

Input folders are deliberately not scanned recursively. Standalone `acetic` requires a folder even
when it contains only one image. A folder containing no supported direct image files is rejected.

## Complete pipeline

```python
result = CervicalDoctor.pipeline(
    image_path="case_001",
    txt_path=None,
    filter=True,
    conf=0.25,
    out_dir=None,
    device="cuda",
)
```

When `filter=True`, every image is passed through cervix detection and acetic-image classification.
Only original images satisfying both conditions continue:

```text
at least one cervix detection AND acetic top-1 == vinegar
```

Detection visualizations are never sent to segmentation or diagnosis. When `filter=False`, both
dirty-data checks are skipped and every original input image continues directly. Segmentation is
mandatory in both modes because the released diagnosis model was trained with binary lesion masks.

If filtering keeps no image, the result has `status="no_eligible_images"` and no diagnosis is
fabricated.

The default folder-input output is `<image_path>/cervicaldoctor_output`. A typical result is:

```text
cervicaldoctor_output/
├── detect/
│   ├── image_1.jpg.detected.png
│   ├── image_2.png.detected.png
│   └── detect.json
├── acetic/
│   ├── image_2.png
│   └── acetic.json
├── filtered/
│   └── image_2.png
├── classification/
│   ├── segmentation/
│   │   ├── masks/
│   │   │   └── image_2.png.mask.png
│   │   ├── overlays/
│   │   │   └── image_2.png.overlay.png
│   │   └── segmentation.json
│   └── prediction.json
├── filter.json
└── pipeline_result.json
```

For a single-file input, the default is a sibling folder such as
`image_1_cervicaldoctor_output/`. Pass `out_dir` to choose another location. Existing output is
protected unless `overwrite=True` is set.

## Independent tasks

### Cervix detection

```python
result = CervicalDoctor.detect(
    image_path="case_001",       # file or folder
    conf=0.25,
    out_dir="detect_output",
    device="cuda",
)
```

Every input image/frame receives a `.detected.png` visualization, including images with no box.
`detect.json` and the returned dictionary contain box coordinates, confidence, `has_cervix`, source
identity, and visualization path.

### Acetic-image selection

```python
result = CervicalDoctor.acetic(
    image_path="case_001",       # folder is required
    out_dir="acetic_output",
    device="cuda",
)
```

The output folder contains zero or more original images whose top-1 class is `vinegar`, plus
`acetic.json`. This independent task does not require a cervix detection; the intersection rule is
applied only by `pipeline(filter=True)`.

### Binary lesion segmentation

```python
result = CervicalDoctor.seg(
    image_path="case_001",       # file or folder
    out_dir="seg_output",
    device="cuda",
)
```

`masks/` contains binary PNG masks with `0=background` and `255=lesion`. `overlays/` contains green
mask-on-original visualizations. `segmentation.json` maps every artifact to its source image/frame.

### Patient diagnosis

```python
result = CervicalDoctor.classify(
    image_path="case_001",       # file or folder; all images are one patient
    txt_path="txt.json",         # or None
    out_dir="classify_output",
    device="cuda",
)
```

`classify` always invokes the public binary segmenter internally. There is no public switch to skip
segmentation and no external `mask_path` argument. Multiple images are pooled into one patient-level
diagnosis. The return value includes probabilities, per-image attention, source gates, text gate,
the complete internal segmentation manifest, and the epoch-16 all-data checkpoint identity.

## Clinical JSON and cache

`txt_path=None` means age, HPV, and cytology are all unavailable. Its HealthGPT feature is shipped
as a precomputed release cache, so the 8B text encoder is not loaded.

A clinical JSON contains one patient's data:

```json
{
  "age": 45,
  "hpv": "HPV16 positive",
  "cytology": "HSIL"
}
```

The legacy keys `hpv_standardization` and `cytology_standardization` are also accepted. The JSON is
reread on every call. Cache identity is computed from the normalized prompt content rather than the
file path:

- unchanged content reuses `cache/inference_text/<content-hash>.pt`;
- changed content generates a different key and feature;
- the first unseen prompt loads HealthGPT; later calls do not.

## BMP and DICOM

BMP is decoded as an ordinary RGB raster. DICOM decoding handles:

- uncompressed and supported compressed pixel data;
- grayscale rescale, VOI LUT/windowing, and `MONOCHROME1` inversion;
- RGB and YBR color conversion;
- single-frame and multi-frame objects.

Each multi-frame DICOM frame is treated as one image belonging to the same patient. A safety limit
of 64 frames prevents accidental ingestion of long videos. DICOM is converted to temporary RGB PNG
only for model execution; temporary files are removed after the task. Detector and segmentation
visualizations are always PNG. Accepted single-frame DICOM files remain DICOM; accepted frames from
a multi-frame object are materialized as RGB PNG files so rejected frames cannot re-enter the next
stage.

Format support does not make the model clinically calibrated for unrelated grayscale modalities.
The released diagnosis model was trained on colposcopy imagery.

## Device

The NVIDIA build accepts:

```text
cpu
cuda
cuda:N
auto
```

Top-level functions default to `cuda`. `auto` selects CUDA when `torch.cuda.is_available()` is true.
Dynamic uncached clinical text is technically supported on CPU but loading HealthGPT-Pro-8B is slow
and memory intensive. `txt_path=None` avoids that load through the release cache.

An instance can reuse lazy-loaded models:

```python
doctor = CervicalDoctor.CervicalDoctor(device="cuda")
first = doctor.pipeline("case_001", filter=True)
second = doctor.classify("case_002", txt_path=None)
doctor.release()
```

## Active checkpoints

All active runtime assets are under `ckpt/`:

- `ckpt/cervicaldc/`: cervix detector and acetic-image classifier;
- `ckpt/cervicalseg-v0.2.0/`: binary lesion checkpoint used by `cervicalseg>=0.2.0`;
- `ckpt/cervicaldoctor/weights_full_final_epoch_016.pt`: the only active diagnosis weight;
- `ckpt/cervicaldoctor/text_cache/`: release cache for missing clinical information;
- `ckpt/dinov3-vits16-pretrain-lvd1689m/`: local DINOv3 model;
- `ckpt/healthgpt-pro-8b/`: local HealthGPT text encoder.

Patient-split and resumable full-training checkpoints are stored under `backups/` and are never
loaded by the public API. CervicalDC's required Ultralytics runtime is integrated under
`cervical_doctor/_vendor/`; the package does not import the sibling CervicalDC project.

## Verification

Run API/model contract tests:

```bash
python scripts/run_smoke_tests.py
```

Verify every declared model asset:

```bash
python scripts/verify_assets.py
```

## Training source

The original patient-level training implementation remains available through `train.py` and
`train_full.py`. The release diagnosis weight was trained for 16 epochs on the filtered
train+validation+test union after epoch count selection by the preceding patient-split experiment.
The active model uses DINOv3 ViT-S/16, binary CervicalSeg lesion ROI weights, Haar LH/HL/HH sources,
per-image five-source gating, patient attention MIL, and frozen HealthGPT clinical features.

The code package is distributed independently from the large model assets. Hugging Face model
publishing and automatic asset download can be added without changing the public inference API.
