blitzid 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- blitzid-0.1.0/.dockerignore +9 -0
- blitzid-0.1.0/.env.example +37 -0
- blitzid-0.1.0/.github/workflows/ci.yml +31 -0
- blitzid-0.1.0/.github/workflows/docker.yml +62 -0
- blitzid-0.1.0/.github/workflows/publish.yml +31 -0
- blitzid-0.1.0/.gitignore +59 -0
- blitzid-0.1.0/.pre-commit-config.yaml +14 -0
- blitzid-0.1.0/CHANGELOG.md +138 -0
- blitzid-0.1.0/CONTRIBUTING.md +39 -0
- blitzid-0.1.0/Dockerfile +22 -0
- blitzid-0.1.0/LICENSE +21 -0
- blitzid-0.1.0/PKG-INFO +413 -0
- blitzid-0.1.0/README.md +353 -0
- blitzid-0.1.0/docker-compose.yml +28 -0
- blitzid-0.1.0/docs/coding-standard.md +169 -0
- blitzid-0.1.0/images/README.md +12 -0
- blitzid-0.1.0/images/bub_der_personalausweis_kopie.jpg +0 -0
- blitzid-0.1.0/images/nl_td1_id_specimen.jpg +0 -0
- blitzid-0.1.0/images/ny_license_plate_wrap.jpg +0 -0
- blitzid-0.1.0/images/nys_driver_license_sample.webp +0 -0
- blitzid-0.1.0/images/solvay_conference_1927.jpg +0 -0
- blitzid-0.1.0/images/td3_passport_specimen.jpg +0 -0
- blitzid-0.1.0/images/tlc_driver_license_sample.png +0 -0
- blitzid-0.1.0/models/.gitkeep +0 -0
- blitzid-0.1.0/models/README.md +34 -0
- blitzid-0.1.0/pyproject.toml +122 -0
- blitzid-0.1.0/scripts/README.md +73 -0
- blitzid-0.1.0/scripts/benchmark.py +231 -0
- blitzid-0.1.0/scripts/demo_cli.py +175 -0
- blitzid-0.1.0/scripts/download_models.py +64 -0
- blitzid-0.1.0/scripts/face_detector_demo.py +267 -0
- blitzid-0.1.0/scripts/midv500.py +632 -0
- blitzid-0.1.0/scripts/ocr_demo.py +81 -0
- blitzid-0.1.0/src/blitzid/__init__.py +49 -0
- blitzid-0.1.0/src/blitzid/_image.py +180 -0
- blitzid-0.1.0/src/blitzid/_models.py +142 -0
- blitzid-0.1.0/src/blitzid/api/__init__.py +11 -0
- blitzid-0.1.0/src/blitzid/api/_analyze.py +129 -0
- blitzid-0.1.0/src/blitzid/api/_app.py +429 -0
- blitzid-0.1.0/src/blitzid/api/_jobs.py +399 -0
- blitzid-0.1.0/src/blitzid/api/_schemas.py +35 -0
- blitzid-0.1.0/src/blitzid/api/_upload.py +89 -0
- blitzid-0.1.0/src/blitzid/exceptions.py +17 -0
- blitzid-0.1.0/src/blitzid/face/__init__.py +5 -0
- blitzid-0.1.0/src/blitzid/face/_face.py +122 -0
- blitzid-0.1.0/src/blitzid/face/_scrfd.py +151 -0
- blitzid-0.1.0/src/blitzid/face/detector.py +481 -0
- blitzid-0.1.0/src/blitzid/py.typed +0 -0
- blitzid-0.1.0/src/blitzid/reading/__init__.py +5 -0
- blitzid-0.1.0/src/blitzid/reading/document.py +399 -0
- blitzid-0.1.0/src/blitzid/reading/mrz.py +355 -0
- blitzid-0.1.0/src/blitzid/reading/ocr.py +152 -0
- blitzid-0.1.0/tests/conftest.py +56 -0
- blitzid-0.1.0/tests/test_api.py +1006 -0
- blitzid-0.1.0/tests/test_detector.py +256 -0
- blitzid-0.1.0/tests/test_detector_helpers.py +314 -0
- blitzid-0.1.0/tests/test_document.py +197 -0
- blitzid-0.1.0/tests/test_exceptions.py +42 -0
- blitzid-0.1.0/tests/test_image.py +185 -0
- blitzid-0.1.0/tests/test_models.py +109 -0
- blitzid-0.1.0/tests/test_mrz.py +360 -0
- blitzid-0.1.0/tests/test_ocr.py +118 -0
- blitzid-0.1.0/tests/test_refactored.py +99 -0
- blitzid-0.1.0/uv.lock +1670 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# blitzid environment variables — copy to .env and adjust.
|
|
2
|
+
# All optional; unset means default behavior.
|
|
3
|
+
|
|
4
|
+
# Directory for all model weights (SCRFD + RapidOCR). Default: the
|
|
5
|
+
# platformdirs user cache (~/.cache/blitzid/models on Linux).
|
|
6
|
+
# For docker compose: point at a mounted volume so containers share
|
|
7
|
+
# one copy instead of re-downloading on every start, e.g.:
|
|
8
|
+
# volumes: [ "./models:/models" ]
|
|
9
|
+
# environment: { BLITZID_MODELS_DIR: /models }
|
|
10
|
+
#BLITZID_MODELS_DIR=/models
|
|
11
|
+
|
|
12
|
+
# Set to 1 to run the OCR/MRZ integration tests (downloads RapidOCR
|
|
13
|
+
# models, needs network). Unit test suites skip them otherwise.
|
|
14
|
+
#BLITZID_OCR_INTEGRATION=1
|
|
15
|
+
|
|
16
|
+
# ── HTTP API (blitzid[api] extra) ─────────────────────────────
|
|
17
|
+
|
|
18
|
+
# Redis the API's job store talks to. The docker-compose redis
|
|
19
|
+
# (persistence disabled — jobs live in RAM only) is wired for you;
|
|
20
|
+
# override only for external Redis instances.
|
|
21
|
+
#BLITZID_API_REDIS_URL=redis://redis:6379/0
|
|
22
|
+
|
|
23
|
+
# Upload size cap in MB (413 above it).
|
|
24
|
+
#BLITZID_API_MAX_UPLOAD_MB=20
|
|
25
|
+
|
|
26
|
+
# Lifetime of every job key in seconds (result is 410 Gone after).
|
|
27
|
+
#BLITZID_API_JOB_TTL_SECONDS=900
|
|
28
|
+
|
|
29
|
+
# Queue capacity (503 + Retry-After when full).
|
|
30
|
+
#BLITZID_API_MAX_QUEUED_JOBS=50
|
|
31
|
+
|
|
32
|
+
# Analyses running at once per API process.
|
|
33
|
+
#BLITZID_API_MAX_CONCURRENT_JOBS=2
|
|
34
|
+
|
|
35
|
+
# Claim lease in seconds — a worker crash past this lease re-runs the
|
|
36
|
+
# job instead of losing it (lease sweeper).
|
|
37
|
+
#BLITZID_API_JOB_LEASE_SECONDS=120
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_call:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v6.0.2
|
|
14
|
+
- uses: astral-sh/setup-uv@v8.1.0
|
|
15
|
+
- run: uv sync --extra dev
|
|
16
|
+
- run: uv run ruff check src/ tests/ scripts/
|
|
17
|
+
- run: uv run ruff format --check src/ tests/ scripts/
|
|
18
|
+
- run: uv run mypy src/ scripts/
|
|
19
|
+
|
|
20
|
+
test:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
strategy:
|
|
23
|
+
matrix:
|
|
24
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v6.0.2
|
|
27
|
+
- uses: astral-sh/setup-uv@v8.1.0
|
|
28
|
+
with:
|
|
29
|
+
python-version: ${{ matrix.python-version }}
|
|
30
|
+
- run: uv sync --extra dev
|
|
31
|
+
- run: uv run pytest tests/ -v
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: Docker
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
uses: ./.github/workflows/ci.yml
|
|
10
|
+
|
|
11
|
+
release:
|
|
12
|
+
needs: test
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
packages: write
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v6.0.2
|
|
19
|
+
|
|
20
|
+
- name: Build image (amd64)
|
|
21
|
+
run: |
|
|
22
|
+
docker build \
|
|
23
|
+
-t ghcr.io/malvavisc0/blitzid:$GITHUB_REF_NAME \
|
|
24
|
+
-t ghcr.io/malvavisc0/blitzid:latest .
|
|
25
|
+
|
|
26
|
+
- name: Smoke test (compose up, health, analyze, crop)
|
|
27
|
+
env:
|
|
28
|
+
BLITZID_IMAGE_TAG: ${{ github.ref_name }}
|
|
29
|
+
run: |
|
|
30
|
+
docker compose up -d --no-build --wait
|
|
31
|
+
test "$(docker compose images -q api)" = "$(docker image inspect -f '{{.Id}}' ghcr.io/malvavisc0/blitzid:$GITHUB_REF_NAME)"
|
|
32
|
+
curl -fsS http://localhost:8000/health | grep -q '"status":"ok"'
|
|
33
|
+
JOB=$(curl -fsS \
|
|
34
|
+
-F image=@images/nl_td1_id_specimen.jpg \
|
|
35
|
+
-F types=face,ocr,mrz http://localhost:8000/analyze)
|
|
36
|
+
ID=$(echo "$JOB" | jq -r .job_id)
|
|
37
|
+
for _ in $(seq 1 60); do
|
|
38
|
+
RESULT=$(curl -fsS http://localhost:8000/jobs/$ID)
|
|
39
|
+
STATUS=$(echo "$RESULT" | jq -r .status)
|
|
40
|
+
if [ "$STATUS" = "done" ] || [ "$STATUS" = "failed" ]; then break; fi
|
|
41
|
+
sleep 1
|
|
42
|
+
done
|
|
43
|
+
echo "$RESULT" | jq .
|
|
44
|
+
test "$STATUS" = "done"
|
|
45
|
+
echo "$RESULT" | grep -q "DE BRUIJN"
|
|
46
|
+
curl -fsS \
|
|
47
|
+
-F image=@images/nl_td1_id_specimen.jpg \
|
|
48
|
+
-F side=front http://localhost:8000/crop \
|
|
49
|
+
| jq -e '.face_found == true' > /dev/null
|
|
50
|
+
docker compose down -v
|
|
51
|
+
|
|
52
|
+
- name: Login to GHCR
|
|
53
|
+
uses: docker/login-action@v3
|
|
54
|
+
with:
|
|
55
|
+
registry: ghcr.io
|
|
56
|
+
username: ${{ github.actor }}
|
|
57
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
58
|
+
|
|
59
|
+
- name: Push image
|
|
60
|
+
run: |
|
|
61
|
+
docker push ghcr.io/malvavisc0/blitzid:$GITHUB_REF_NAME
|
|
62
|
+
docker push ghcr.io/malvavisc0/blitzid:latest
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
uses: ./.github/workflows/ci.yml
|
|
10
|
+
|
|
11
|
+
publish:
|
|
12
|
+
needs: test
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment: pypi
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
id-token: write
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v6.0.2
|
|
20
|
+
- uses: astral-sh/setup-uv@v8.1.0
|
|
21
|
+
|
|
22
|
+
- name: Verify tag matches the package version
|
|
23
|
+
run: |
|
|
24
|
+
VERSION=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
|
|
25
|
+
test "v$VERSION" = "$GITHUB_REF_NAME"
|
|
26
|
+
|
|
27
|
+
- name: Build sdist and wheel
|
|
28
|
+
run: uv build
|
|
29
|
+
|
|
30
|
+
- name: Publish to PyPI
|
|
31
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
blitzid-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Virtual environments
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
ENV/
|
|
10
|
+
|
|
11
|
+
# Packaging / build
|
|
12
|
+
build/
|
|
13
|
+
dist/
|
|
14
|
+
*.egg-info/
|
|
15
|
+
.eggs/
|
|
16
|
+
|
|
17
|
+
# Testing / coverage
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
coverage.xml
|
|
21
|
+
htmlcov/
|
|
22
|
+
|
|
23
|
+
# Type checking / linters
|
|
24
|
+
.mypy_cache/
|
|
25
|
+
.pyre/
|
|
26
|
+
.ruff_cache/
|
|
27
|
+
|
|
28
|
+
# Jupyter
|
|
29
|
+
.ipynb_checkpoints/
|
|
30
|
+
|
|
31
|
+
# IDEs / editors
|
|
32
|
+
.vscode/
|
|
33
|
+
.idea/
|
|
34
|
+
*.swp
|
|
35
|
+
*.swo
|
|
36
|
+
|
|
37
|
+
# OS files
|
|
38
|
+
.DS_Store
|
|
39
|
+
Thumbs.db
|
|
40
|
+
|
|
41
|
+
# Project artifacts
|
|
42
|
+
results/
|
|
43
|
+
|
|
44
|
+
# Large model files (downloaded at runtime)
|
|
45
|
+
models/**
|
|
46
|
+
!models/
|
|
47
|
+
!models/.gitkeep
|
|
48
|
+
!models/README.md
|
|
49
|
+
|
|
50
|
+
# Logs
|
|
51
|
+
*.log
|
|
52
|
+
|
|
53
|
+
# Env files
|
|
54
|
+
.env
|
|
55
|
+
.env.*
|
|
56
|
+
!.env.example
|
|
57
|
+
|
|
58
|
+
results/*
|
|
59
|
+
data/*
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.15.12
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
9
|
+
rev: v6.0.0
|
|
10
|
+
hooks:
|
|
11
|
+
- id: trailing-whitespace
|
|
12
|
+
- id: end-of-file-fixer
|
|
13
|
+
- id: check-yaml
|
|
14
|
+
- id: check-added-large-files
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.1.0] - 2026-10-01
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- HTTP API (`blitzid[api]` extra: fastapi, uvicorn, python-multipart,
|
|
12
|
+
redis) exposing the analyses as async jobs: `POST /analyze`
|
|
13
|
+
(multipart upload + `types` form field; eager validation with
|
|
14
|
+
422/400/413/415/503), `GET /jobs/{id}` (claim-once results via
|
|
15
|
+
GETDEL — later reads get `410 Gone`; TTL-bounded), `GET /health`
|
|
16
|
+
for orchestration (`503` when the job store is unreachable). Jobs
|
|
17
|
+
run in per-process worker threads bounded by
|
|
18
|
+
`BLITZID_API_MAX_CONCURRENT_JOBS` with per-engine locks; a Redis
|
|
19
|
+
queue (persistence off — RAM only) with an atomically enforced cap,
|
|
20
|
+
TTL, raw-byte job payloads, and token-owned claims: worker
|
|
21
|
+
heartbeats extend the claim lease, a lost claim discards that
|
|
22
|
+
worker's result (the re-claimed worker's result wins), and the
|
|
23
|
+
claim-lease sweeper re-runs only jobs whose worker actually died. Env
|
|
24
|
+
knobs: `BLITZID_API_REDIS_URL`, `BLITZID_API_MAX_UPLOAD_MB`,
|
|
25
|
+
`BLITZID_API_JOB_TTL_SECONDS`, `BLITZID_API_MAX_QUEUED_JOBS`,
|
|
26
|
+
`BLITZID_API_MAX_CONCURRENT_JOBS`, `BLITZID_API_JOB_LEASE_SECONDS`.
|
|
27
|
+
- Docker deployment: multi-stage `Dockerfile` (weights baked to
|
|
28
|
+
`/models`, non-root, python-urllib HEALTHCHECK) and
|
|
29
|
+
`docker-compose.yml` (api + RAM-only `redis:8-alpine` with
|
|
30
|
+
persistence disabled). Tag pushes publish
|
|
31
|
+
`ghcr.io/malvavisc0/blitzid` via `.github/workflows/docker.yml`,
|
|
32
|
+
gated on the (now reusable) CI gate plus a compose smoke test that
|
|
33
|
+
submits a real face/ocr/mrz job and polls it to completion.
|
|
34
|
+
- PyPI publishing: tag pushes matching the package version build and
|
|
35
|
+
upload the sdist/wheel to PyPI via `.github/workflows/publish.yml`
|
|
36
|
+
(trusted publishing — no API tokens), gated on the same CI matrix.
|
|
37
|
+
- `DocumentCropper` / `QualityReport` (reading domain, pure CV):
|
|
38
|
+
locates the largest plausible document quad via Canny edges +
|
|
39
|
+
contour approximation, warps it into an axis-aligned canonical crop
|
|
40
|
+
at the quad's own aspect, and quality-checks it (`document_found`,
|
|
41
|
+
`aspect_ratio` near ID-1/ID-3, `resolution`, `sharpness`,
|
|
42
|
+
`brightness`, side-aware `face_present` — never fails a document).
|
|
43
|
+
Optional `FaceDetectorDNN` injection powers the face check
|
|
44
|
+
(`front` + no face → warn; `back` → not expected) without adding a
|
|
45
|
+
runtime `reading → face` dependency; an optional `detector_lock`
|
|
46
|
+
serializes the check against other users of a shared detector. The
|
|
47
|
+
`POST /crop` API endpoint wraps it: QC verdict plus the canonical
|
|
48
|
+
`crop_base64` JPEG.
|
|
49
|
+
- `BLITZID_MODELS_DIR` environment variable: relocates all model-weight
|
|
50
|
+
downloads (SCRFD + RapidOCR) when set; defaults to the platformdirs
|
|
51
|
+
user cache as before. `scripts/download_models.py` pre-fetches all
|
|
52
|
+
weights into a directory (default `models/`) for Docker images.
|
|
53
|
+
- `MRZReader` / `MRZRecord` / `MRZError`: ICAO 9303 machine-readable zone
|
|
54
|
+
reading on top of the `ocr` extra. Selects MRZ lines from OCR output
|
|
55
|
+
(30/36/44 chars over the MRZ charset, reassembled by bbox position),
|
|
56
|
+
validates every check digit plus the letter-only fields (document
|
|
57
|
+
code, issuer, nationality — the ones without check-digit protection),
|
|
58
|
+
and parses TD1, TD2, and TD3 layouts. No fuzzy OCR-error correction:
|
|
59
|
+
an unreadable zone raises `MRZError` with the failing field.
|
|
60
|
+
Document numbers longer than their field follow the ICAO
|
|
61
|
+
convention — no printed check digit, overflow at the start of
|
|
62
|
+
`optional_data1` — and are accepted (the composite check digit
|
|
63
|
+
still covers the full number).
|
|
64
|
+
- `RapidOCRReader` / `OCRText` and the `blitzid[ocr]` extra: OCR text
|
|
65
|
+
reading for ID documents via RapidOCR (PP-OCR ONNX models on
|
|
66
|
+
onnxruntime CPU). Models download on first use to
|
|
67
|
+
`user_cache_dir("blitzid")/models/rapidocr`; `read()` returns text
|
|
68
|
+
lines sorted by confidence. Core installs stay OCR-free.
|
|
69
|
+
`scripts/ocr_demo.py` demonstrates the reader from the CLI.
|
|
70
|
+
- `detect_face_landmarks()`, returning the new frozen `Face` dataclass
|
|
71
|
+
(`bbox`, `confidence`, `landmarks`). SCRFD decodes its 5-point keypoint
|
|
72
|
+
head — right eye, left eye, nose tip, right mouth corner, left mouth
|
|
73
|
+
corner — previously discarded. Existing `detect_face()` tuples are
|
|
74
|
+
unchanged.
|
|
75
|
+
- Positive-path tests on repo image fixtures: the specimen ID card (exactly
|
|
76
|
+
one face — its portrait) and the 1927 conference photo (29 people). Also
|
|
77
|
+
fails fast with `ModelError` on an unexpected SCRFD ONNX output layout.
|
|
78
|
+
- Public `FaceDetectorDNN.cache_enabled` property (scripts no longer read
|
|
79
|
+
the private `_cache` attribute).
|
|
80
|
+
- `face_detector_demo.py` validates `--run` demo names and exits with the
|
|
81
|
+
valid set on typos; cache CLI knobs are documented as custom-preset-only.
|
|
82
|
+
- Detection caching with configurable LRU cache.
|
|
83
|
+
- Multi-scale detection for improved recall on small faces.
|
|
84
|
+
- Typed package (`py.typed` marker).
|
|
85
|
+
|
|
86
|
+
### Changed
|
|
87
|
+
|
|
88
|
+
- Replaced the SSD ResNet-10 Caffe detector with **SCRFD-2.5G** (InsightFace
|
|
89
|
+
`buffalo_m` ONNX weights) running on **onnxruntime CPU**. OpenCV 5 removed
|
|
90
|
+
the Caffe importer; inference now fails fast with `ModelError` instead of
|
|
91
|
+
falling back to other models or providers.
|
|
92
|
+
- `FaceDetectorDNN` gains a `det_size` parameter (default `640x640`, multiples
|
|
93
|
+
of 32) and letterboxes input images so detections map back to original image
|
|
94
|
+
coordinates without aspect distortion.
|
|
95
|
+
- `DetectionMetrics.backend` now reports `"ONNXRuntime"`.
|
|
96
|
+
- `detect_faces_batch()` now omits failed images from the result dict; an
|
|
97
|
+
empty list always means "processed, zero faces". Failed images are still
|
|
98
|
+
logged. Previously failures were indistinguishable from legitimate
|
|
99
|
+
zero-face results.
|
|
100
|
+
- `DetectionMetrics.faces` now carries `Face` records (landmarks
|
|
101
|
+
accessible) instead of legacy `(x, y, w, h, confidence)` tuples.
|
|
102
|
+
**Breaking** for consumers unpacking metrics entries as tuples.
|
|
103
|
+
- Cache hits report real elapsed cache-lookup time in
|
|
104
|
+
`DetectionMetrics.processing_time` instead of a hardcoded `0.0`.
|
|
105
|
+
- `scales` runs exactly as configured: `_normalize_scales` no longer
|
|
106
|
+
silently injects a `1.0` pass, so `scales=(1.5,)` costs one inference,
|
|
107
|
+
not two.
|
|
108
|
+
- Model downloads stream to a `.tmp` file in 64 KB chunks instead of
|
|
109
|
+
reading the whole file into memory; the socket timeout now bounds the
|
|
110
|
+
transfer.
|
|
111
|
+
|
|
112
|
+
### Fixed
|
|
113
|
+
|
|
114
|
+
- Model download no longer raises `ModelError` after a successful transfer:
|
|
115
|
+
the post-download size log stat'ed the already-renamed `.tmp` file. Only
|
|
116
|
+
affected fresh installs (cached models never re-downloaded).
|
|
117
|
+
- Detection cache keys now cover image `shape` and `dtype` in addition to
|
|
118
|
+
pixel bytes — arrays sharing byte content but differing in layout no
|
|
119
|
+
longer collide on one cached result.
|
|
120
|
+
- Restored the 10000px `MAX_DIMENSION` guard in image validation, lost in
|
|
121
|
+
the SCRFD migration; oversized inputs fail fast with `ImageError`
|
|
122
|
+
instead of allocating multi-GB arrays.
|
|
123
|
+
- `midv500_face_test.py` face counts now match the crops actually
|
|
124
|
+
considered for saving when `--max-faces-per-image` truncates the list.
|
|
125
|
+
|
|
126
|
+
### Removed
|
|
127
|
+
|
|
128
|
+
- The DeepFace backend entirely: `FaceDetectorDeepFace`,
|
|
129
|
+
`blitzid[deepface]` extra, the `deepface`/`tf-keras` dependencies,
|
|
130
|
+
`tests/test_deepface.py`, and the `analyze()` API. SCRFD via onnxruntime
|
|
131
|
+
is the single detection backend.
|
|
132
|
+
- CUDA backend support: `require_cuda` parameters, the CUDA/CPU auto-fallback
|
|
133
|
+
chain in `ModelManager.load_network`, the `CUDAConfigError` alias, and
|
|
134
|
+
`scripts/cuda_diagnostics.py`.
|
|
135
|
+
- Dead dataset-annotation helpers in `scripts/midv500_download.py` with zero
|
|
136
|
+
call sites (`list_annotation_paths_recursively`, `calculate_intersect_area`,
|
|
137
|
+
`get_bbox_inside_image`).
|
|
138
|
+
- `FaceDetectorDNN.backend_type` compat alias (superseded by `backend`).
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Contributing to BlitzID
|
|
2
|
+
|
|
3
|
+
## Development setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Clone and install with dev dependencies
|
|
7
|
+
git clone https://github.com/malvavisc0/blitzid.git
|
|
8
|
+
cd blitzid
|
|
9
|
+
uv sync --extra dev
|
|
10
|
+
|
|
11
|
+
# Install pre-commit hooks
|
|
12
|
+
uv run pre-commit install
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Running checks locally
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Lint
|
|
19
|
+
uv run ruff check src/ tests/ scripts/
|
|
20
|
+
uv run ruff format --check src/ tests/ scripts/
|
|
21
|
+
|
|
22
|
+
# Type checking
|
|
23
|
+
uv run mypy src/ scripts/
|
|
24
|
+
|
|
25
|
+
# Tests
|
|
26
|
+
uv run pytest tests/ -v
|
|
27
|
+
|
|
28
|
+
# Complexity + dead code (see docs/coding-standard.md)
|
|
29
|
+
uvx radon cc src scripts tests -s | grep -E '\-\s(C|D|E|F)\s' # must be empty
|
|
30
|
+
uvx vulture src/ tests/ scripts/ --min-confidence 60
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Pull request guidelines
|
|
34
|
+
|
|
35
|
+
1. Fork the repo and create a feature branch from `main`.
|
|
36
|
+
2. Ensure all checks pass (`ruff`, `mypy`, `pytest`).
|
|
37
|
+
3. Add or update tests for any new functionality.
|
|
38
|
+
4. Update `CHANGELOG.md` under `[Unreleased]`.
|
|
39
|
+
5. Open a PR with a clear description of the change.
|
blitzid-0.1.0/Dockerfile
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# ── Builder: install deps and bake model weights ──────────────
|
|
2
|
+
FROM python:3.12-slim AS builder
|
|
3
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
COPY README.md pyproject.toml uv.lock ./
|
|
6
|
+
COPY src/ src/
|
|
7
|
+
COPY scripts/ scripts/
|
|
8
|
+
RUN uv sync --frozen --no-dev --extra api --extra ocr
|
|
9
|
+
RUN uv run --no-sync python scripts/download_models.py --models-dir /models
|
|
10
|
+
|
|
11
|
+
# ── Runtime: venv + baked weights, non-root ─────────────────────
|
|
12
|
+
FROM python:3.12-slim
|
|
13
|
+
COPY --from=builder /app /app
|
|
14
|
+
COPY --from=builder /models /models
|
|
15
|
+
ENV BLITZID_MODELS_DIR=/models
|
|
16
|
+
ENV PATH=/app/.venv/bin:$PATH
|
|
17
|
+
RUN useradd --create-home appuser
|
|
18
|
+
USER appuser
|
|
19
|
+
EXPOSE 8000
|
|
20
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
|
21
|
+
CMD ["python", "-c", "import sys, urllib.request; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).status == 200 else 1)"]
|
|
22
|
+
CMD ["uvicorn", "blitzid.api:app", "--host", "0.0.0.0", "--port", "8000"]
|
blitzid-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Malvavisc0
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|