Metadata-Version: 2.4
Name: JwzTumor
Version: 1.0.11
Summary: Breast ultrasound benign-malignant diagnosis with segmentation-assisted multi-task learning.
Author: SuShuheng
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/SuShuheng/JwzTumor
Project-URL: Documentation, https://github.com/SuShuheng/JwzTumor#readme
Project-URL: Repository, https://github.com/SuShuheng/JwzTumor
Project-URL: Issues, https://github.com/SuShuheng/JwzTumor/issues
Project-URL: Changelog, https://github.com/SuShuheng/JwzTumor/releases
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: torch>=2.0
Requires-Dist: numpy>=1.24
Requires-Dist: pandas>=2.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: tqdm>=4.65
Requires-Dist: typer>=0.9
Requires-Dist: rich>=13.0
Requires-Dist: scipy>=1.10
Requires-Dist: scikit-learn>=1.2
Requires-Dist: Pillow>=9.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Provides-Extra: server
Requires-Dist: fastapi>=0.100; extra == "server"
Requires-Dist: uvicorn>=0.20; extra == "server"
Requires-Dist: python-multipart>=0.0.6; extra == "server"
Requires-Dist: httpx[socks]>=0.27; extra == "server"
Dynamic: license-file

# JwzTumor

[![PyPI version](https://img.shields.io/pypi/v/JwzTumor.svg)](https://pypi.org/project/JwzTumor/)
[![PyPI downloads](https://img.shields.io/pypi/dm/JwzTumor.svg)](https://pypi.org/project/JwzTumor/)
[![Python](https://img.shields.io/pypi/pyversions/JwzTumor.svg)](https://pypi.org/project/JwzTumor/)
[![License](https://img.shields.io/pypi/l/JwzTumor.svg)](https://github.com/SuShuheng/JwzTumor/blob/master/LICENSE)

Breast ultrasound benign-malignant diagnosis with segmentation-assisted multi-task learning.

## Overview

JwzTumor implements **LABDG-Pre + DGBC-MTLNet + MPTC-Head**, a multi-task system for breast tumor diagnosis from ultrasound images:

- **LABDG-Pre**: Lesion-aware boundary-preserving domain-generalized preprocessing
- **DGBC-MTLNet**: Domain-generalized boundary-context collaborative multi-task network (CNN-Mamba hybrid)
- **MPTC-Head**: Morphology-prompted topology-consistent classification head (4-stream)

The system outputs: classification probability, segmentation mask, edge map, SDM, uncertainty map, and explainability visualizations.

## Project Structure

```
jwz-tumor/
  pyproject.toml
  configs/          # 8 YAML configs
  dataset/          # BUSBRA + BUSI data
  web/              # React + Vite + TypeScript frontend source
  scripts/          # Frontend packaging and maintenance scripts
  src/JwzTumor/
    cli/            # CLI entrypoints, including jwzt server
    data/           # Datasets, preprocessing, augmentation, cache, audit, collate
    models/         # LABDG-Pre, DGBCEncoder, MPP, decoder, MPTC-Head
    losses/         # 7 loss modules + factory
    training/       # Stage trainer, scheduler, callbacks
    inference/      # Predictor, TTA, postprocessing, visualization
    evaluation/     # Classification + segmentation metrics
    server/         # FastAPI inference server, /api/report, packaged static UI
      report/       # Rule/LLM/hybrid report generation
      ui/           # Built frontend files served by jwzt server --with-ui
    utils/          # Config, seed, logging, I/O, registry
  tests/            # Python tests
```

## Datasets

### BUSBRA (Training)
- Images: `bus_{id}-{side}.png`, Masks: `mask_{id}-{side}.png`
- Labels: `bus_data.csv` (Pathology: benign/malignant)
- CV: `5-fold-cv.csv` or `10-fold-cv.csv`
- Case-level splitting (no leak)

### BUSI (Testing)
- Directory-based: `benign/`, `malignant/`
- Skip: `normal-不使用/`
- Multi-mask: OR union in original size before resize
- RGB images converted to grayscale

## Installation

```bash
# Install from PyPI
pip install JwzTumor

# Using uv
uv pip install -e .

# Development
uv pip install -e ".[dev]"
```

## Quick Start

```bash
# Show version
jwzt version

# Export config template
jwzt get --name train_full --output configs/my_config.yaml

# Train
jwzt train --config configs/train_full.yaml --experiment-name exp001 --fold 1

# Stage-specific training
jwzt train --config configs/train_pretrain_pre.yaml --stage pretrain_pre --fold 1
jwzt train --config configs/train_pretrain_seg.yaml --stage pretrain_seg --fold 1
jwzt train --config configs/train_cls_head.yaml --stage train_cls --fold 1
jwzt train --config configs/train_finetune.yaml --stage finetune_all --fold 1

# Predict
jwzt pred --config configs/infer_busi.yaml --model checkpoints/best_auc.ckpt --data dataset/Dataset_BUSI_with_GT --output outputs/busi_predictions

# Evaluate
jwzt eval --config configs/eval_busi.yaml --pred outputs/busi_predictions --data dataset/Dataset_BUSI_with_GT

# Audit dataset
jwzt audit-data --data-root dataset --output outputs/dataset_audit_summary.json

# Debug training
jwzt train --config configs/train_debug.yaml --experiment-name debug_run
```

## Clinical Workbench UI

The project now includes a clinical auxiliary diagnosis workbench for breast ultrasound cases:

- frontend source lives in `web/`
- packaged static UI lives in `src/JwzTumor/server/ui/`
- backend report generation lives in `src/JwzTumor/server/report/`
- report API endpoint is `POST /api/report`

The frontend supports:

- single-image and dual-image diagnosis modes
- patient metadata and structured ultrasound field entry
- asynchronous inference progress
- editable structured report rendering
- browser-local draft/history persistence
- JSON export and PDF export

## Frontend Development

Install frontend dependencies:

```bash
cd web
npm install
```

Configure the API target with either `.env` or shell environment variables:

```bash
cp .env.example .env
```

`.env` example:

```bash
VITE_JWZT_API_BASE_URL=http://127.0.0.1:8000
```

Terminal temporary variable example:

```bash
cd web
VITE_JWZT_API_BASE_URL=https://your-api.example.com npm run dev -- --host 0.0.0.0 --port 5173
```

Start the standalone frontend dev server:

```bash
cd web
npm run dev -- --host 0.0.0.0 --port 5173
```

The frontend resolves the backend address in this order:

1. runtime `config.js` injected by the hosting server
2. `VITE_JWZT_API_BASE_URL`
3. fallback `http://127.0.0.1:8000`

## Inference Server

Install server dependencies:

```bash
pip install -e ".[server]"
```

Start the inference API:

```bash
jwzt server \
  --model checkpoints/best_auc.ckpt \
  --config configs/infer_busi.yaml \
  --host 0.0.0.0 \
  --port 8000
```

The same values can also be stored in a server YAML file. Generate an annotated example with:

```bash
jwzt get -t server -o configs/server.yaml
```

Then start from that file:

```bash
jwzt server --config configs/server.yaml
```

Precedence is CLI options and their environment variables first, then YAML `server:` / `report_llm:` sections, then built-in defaults.

Useful CLI environment variables are also supported, so system variables and one-shot terminal variables work without editing code:

```bash
export JWZT_MODEL=checkpoints/best_auc.ckpt
export JWZT_CONFIG=configs/infer_busi.yaml
export JWZT_HOST=0.0.0.0
export JWZT_PORT=8000
```

Or:

```bash
JWZT_MODEL=checkpoints/best_auc.ckpt JWZT_PORT=8000 jwzt server
```

Server-related environment variables map directly to CLI options and YAML keys:

```text
JWZT_MODEL                 -> server.model / --model
JWZT_HOST                  -> server.host / --host
JWZT_PORT                  -> server.port / --port
JWZT_DEVICE                -> server.device / --device
JWZT_TTA                   -> server.tta / --tta or --no-tta
JWZT_MAX_QUEUE             -> server.max_queue / --max-queue
JWZT_MAX_UPLOAD_MB         -> server.max_upload_mb / --max-upload-mb
JWZT_MAX_BATCH_SIZE        -> server.max_batch_size / --max-batch-size
JWZT_CORS_ORIGINS          -> server.cors_origins / --cors-origins
JWZT_LOG_DIR               -> server.log_dir / --log-dir
JWZT_WITH_UI               -> server.with_ui / --with-ui or --no-with-ui
JWZT_UI_DIR                -> server.ui_dir / --ui-dir
JWZT_UI_HOST               -> server.ui_host / --ui-host
JWZT_UI_PORT               -> server.ui_port / --ui-port
JWZT_UI_API_BASE_URL       -> server.ui_api_base_url / --ui-api-base-url
JWZT_NO_UI                 -> server.no_ui / --no-ui
```

## UI Hosting Modes

### Same-Port Hosting

Serve the packaged UI from the same FastAPI service and port:

```bash
jwzt server \
  --model checkpoints/best_auc.ckpt \
  --config configs/infer_busi.yaml \
  --with-ui \
  --host 0.0.0.0 \
  --port 8000
```

This serves:

- API: `http://127.0.0.1:8000/api/...`
- UI: `http://127.0.0.1:8000/`

### Separate UI Port

Serve the API and UI on different ports:

```bash
jwzt server \
  --model checkpoints/best_auc.ckpt \
  --config configs/infer_busi.yaml \
  --with-ui \
  --host 0.0.0.0 \
  --port 8000 \
  --ui-host 0.0.0.0 \
  --ui-port 8080 \
  --ui-api-base-url http://127.0.0.1:8000
```

Equivalent environment variables:

```bash
export JWZT_UI_HOST=0.0.0.0
export JWZT_UI_PORT=8080
export JWZT_UI_API_BASE_URL=http://127.0.0.1:8000
```

If you already have an external built frontend directory, you can override the packaged UI:

```bash
jwzt server \
  --model checkpoints/best_auc.ckpt \
  --config configs/infer_busi.yaml \
  --with-ui \
  --ui-dir /absolute/path/to/web/dist
```

## Runtime `config.js`

When the UI is served by `jwzt server`, the backend injects a runtime `config.js` file at `/config.js`:

```js
window.__JWZT_CONFIG__ = { apiBaseUrl: "http://127.0.0.1:8000" };
```

This lets the same built frontend work in:

- local standalone development
- same-port packaged serving
- separate-port packaged serving
- reverse-proxy or public-domain deployments

## Report API

Clinical report generation is exposed by:

```text
POST /api/report
```

Supported `report_mode` values:

- `rule`: deterministic local rule template
- `llm`: editable sections generated by the configured LLM
- `hybrid`: rule template with LLM refinement and rule fallback on failure

The frontend uses `/api/report` first and falls back to a local rule composer if report generation fails.

## OpenAI-Compatible LLM Configuration

`/api/report` can use any OpenAI-Compatible chat completions provider. Configure it with environment variables:

```bash
export JWZT_REPORT_LLM_BASE_URL=https://your-openai-compatible.example.com/v1
export JWZT_REPORT_LLM_API_KEY=sk-...
export JWZT_REPORT_LLM_MODEL=gpt-4.1-mini
```

Optional tuning variables:

```bash
export JWZT_REPORT_LLM_TIMEOUT=60
export JWZT_REPORT_LLM_MAX_RETRIES=2
export JWZT_REPORT_LLM_TEMPERATURE=0.2
```

The same provider settings can be placed in the server YAML:

```yaml
report_llm:
  base_url: https://your-openai-compatible.example.com/v1
  api_key: sk-...
  model: gpt-4.1-mini
  timeout: 60
  max_retries: 2
  temperature: 0.2
```

Equivalent CLI flags are available:

```bash
jwzt server \
  --config configs/server.yaml \
  --report-llm-base-url https://your-openai-compatible.example.com/v1 \
  --report-llm-api-key sk-... \
  --report-llm-model gpt-4.1-mini
```

If these variables are absent, the report service stays in rule-only fallback mode for editable sections.

## Rebuild Packaged UI

After changing the frontend, rebuild and copy the static bundle into the Python package:

```bash
./scripts/build_web_ui.sh
```

## Training Strategy

Four-stage training:
1. **pretrain_pre**: Train LABDG-Pre only (coarse localization, structure maps)
2. **pretrain_seg**: Train DGBC-MTLNet segmentation (encoder + decoder)
3. **train_cls**: Train MPTC-Head only (freeze encoder + preprocessing)
4. **finetune_all**: End-to-end fine-tuning with all losses

## Metrics

**Classification**: AUC, Accuracy, Sensitivity, Specificity, Precision, F1
**Segmentation**: Dice, IoU, HD95, Boundary-F1

## Stargazers over time

[![Stargazers over time](https://starchart.cc/SuShuheng/JwzTumor.svg)](https://starchart.cc/SuShuheng/JwzTumor)

## License

Apache-2.0
