Explainable image quality inspection for OCR, document scanning and upload workflows.
Project description
CaptureTrust
One clean package and one explainable report that answer a single question about an image: is it good enough to accept, and if not, what should the person do to fix it?
CaptureTrust inspects an uploaded image and estimates whether it is visually suitable for downstream workflows — OCR, document scanning, form uploads, receipts and invoices, marketplace listings, KYC pre-checks, and general photo quality validation — then returns an explainable, actionable report.
Features
- Blur detection — variance of Laplacian, Tenengrad focus, and edge density blended into an explainable severity, not a single magic number.
- Resolution & aspect ratio — dimensions, megapixels, and unusual-ratio labelling against configurable minimums.
- Lighting & exposure — brightness statistics, under/over-exposure, dynamic range, and uneven-lighting estimation.
- Glare detection — HSV high-value / low-saturation regions cleaned with morphology and ranked by area.
- Noise estimation — a conservative high-frequency residual heuristic that avoids confusing natural texture with sensor noise.
- Shadow estimation — best-effort illumination-shadow coverage in LAB space.
- Orientation — conservative dominant-line rotation estimate (report only).
- Document framing — best-effort quadrilateral detection with candidate scoring, corner visibility, tilt, perspective and framing quality.
- Experimental screen-recapture heuristic (opt-in, off by default) — combines several weak visual signals into a labelled risk. Never a fraud verdict, and prone to false positives, so you must enable it deliberately.
- Explainable scoring — deterministic, fully traceable per-check deductions.
- Friendly recommendations — specific, deduplicated, non-accusatory advice.
- CLI + batch — inspect single files or whole folders, human or JSON output.
Why CaptureTrust exists
Teams that accept user-uploaded images usually end up stitching together a pile of one-off OpenCV scripts for blur, lighting, glare, framing, and so on — each with its own thresholds and no shared explanation. CaptureTrust replaces that with one lightweight, GPU-free package and one report that says why an image might not be usable and what to do about it. It is intuitive for beginners and architected for contributors and production use.
CaptureTrust never claims an image is "fake", "fraudulent", "tampered", or "invalid". It produces a visual quality estimate, not a fraud decision.
Installation
pip install capturetrust
CaptureTrust is lightweight and GPU-free. It depends only on numpy and
opencv-python-headless, and runs on Linux, Windows, macOS, Docker, and
headless cloud environments.
Quickstart
from capturetrust import inspect_image
report = inspect_image("photo.jpg")
print(report.summary())
print(report.to_dict())
Google Colab
!pip install capturetrust
from google.colab import files
from capturetrust import inspect_image
uploaded = files.upload()
filename = next(iter(uploaded))
report = inspect_image(filename)
print(report.to_json())
Jupyter Notebook
!pip install capturetrust
from capturetrust import inspect_image
report = inspect_image("scan.png")
report.to_dict()
Local Python script
from capturetrust import inspect_image, CaptureTrustConfig
config = CaptureTrustConfig(min_width=1024, min_height=768, acceptable_score=75)
report = inspect_image("id_card.jpg", config=config)
if not report.is_acceptable:
for issue, tip in zip(report.issues, report.recommendations):
print(f"- {issue}: {tip}")
Inspecting a NumPy array
import cv2
from capturetrust import inspect_array
image = cv2.imread("photo.jpg") # BGR, grayscale, or RGBA all accepted
report = inspect_array(image)
print(report.score, report.is_acceptable)
Command-line interface
capturetrust inspect image.jpg
capturetrust inspect image.jpg --json
capturetrust inspect image.jpg --pretty
capturetrust inspect image.jpg --min-width 1024 --min-height 768
capturetrust inspect image.jpg --disable-document-check
capturetrust inspect image.jpg --enable-screen-check # opt in to experimental check
capturetrust batch ./images
capturetrust batch ./images --json-output results.json --workers 4
capturetrust --version
Exit codes: 0 acceptable, 1 not acceptable, 2 processing error,
3 usage error.
Batch processing
from capturetrust import inspect_batch
paths = ["a.jpg", "b.png", "c.webp"]
reports = inspect_batch(paths, workers=4) # output order matches input order
for path, report in zip(paths, reports):
print(path, report.score, report.is_acceptable)
Example report
{
"score": 65,
"is_acceptable": false,
"confidence": "medium",
"issues": [
"Image is blurry",
"Heavy shadows detected"
],
"recommendations": [
"Retake the image while holding the camera steady and ensuring the subject is in focus.",
"Reposition the light source or subject to avoid strong shadows."
],
"warnings": [
"Screen recapture analysis is experimental and should not be treated as proof."
],
"metrics": {
"width": 640,
"height": 480,
"blur_score": 42.8,
"average_brightness": 79.5,
"glare_ratio": 0.0,
"noise_score": 0.02,
"shadow_ratio": 0.17,
"document_detected": false,
"screen_recapture_score": 0.0,
"screen_recapture_risk": "low"
},
"checks": {
"blur": { "enabled": true, "passed": false, "severity": "high" },
"shadow": { "enabled": true, "passed": false, "severity": "medium" }
},
"metadata": {
"library_version": "0.1.0",
"image_source_type": "numpy_array",
"processing_time_ms": 12.4
}
}
Presets
Image quality is context-dependent: a document scan must be genuinely sharp for OCR, while an artistic photo with a deliberately blurred background (bokeh) should pass. No single threshold serves both, so choose the preset that matches your content:
from capturetrust import inspect_image, CaptureTrustConfig
# Strict - documents, receipts, ID cards (must be sharp and well framed)
report = inspect_image("scan.jpg", config=CaptureTrustConfig.for_documents())
# Lenient - general and artistic photography (accepts smooth subjects, bokeh)
report = inspect_image("portrait.jpg", config=CaptureTrustConfig.for_photos())
# General purpose (the default)
report = inspect_image("photo.jpg", config=CaptureTrustConfig.balanced())
Any preset accepts overrides, e.g. CaptureTrustConfig.for_photos(acceptable_score=50).
Configuration
Every threshold is adjustable without editing library code:
from capturetrust import CaptureTrustConfig, inspect_image
config = CaptureTrustConfig(
min_width=1024,
min_height=768,
acceptable_score=75,
blur_threshold=110.0,
glare_threshold=0.12,
enable_document_check=True,
enable_screen_recapture_check=True,
)
report = inspect_image("photo.jpg", config=config)
All fields are validated in __post_init__: dimensions must be positive,
scores must be within 0–100, ratios within 0–1, brightness thresholds must be
ordered sensibly, and the screen-recapture medium threshold must be below the
high threshold.
Explainable scoring
Scoring starts at 100 and applies traceable deductions by check and severity.
The full explanation is included in the report under
metrics.score_explanation:
{
"starting_score": 100,
"adjustments": [
{ "check": "blur", "severity": "high", "deduction": 40, "reason": "Low focus sharpness detected" },
{ "check": "glare", "severity": "medium", "deduction": 15, "reason": "Large bright low-saturation regions detected" }
],
"final_score": 55
}
Scores are deterministic for the same input and config, always clamped to 0–100, and never heavily penalise document framing when no document is present. See docs/scoring.md for the full deduction table and how to override it.
Supported file formats
.jpg, .jpeg, .png, .webp, .bmp, .tiff / .tif, and .heic / .heif (iPhone photos).
Array inputs may be OpenCV BGR, single-channel grayscale, or RGBA; they are normalised internally to BGR without mutating the caller's array.
Performance expectations
CaptureTrust is CPU-only and typically inspects a normal-sized photo in tens of
milliseconds on commodity hardware. Very large images cost more; downscaling
before inspection is a reasonable optimisation for high-throughput pipelines.
Batch mode can use a thread pool via the workers argument.
Ethical limitations
CaptureTrust estimates visual quality only. It is not an anti-fraud, tamper-detection, or identity-verification system. Do not use its output as the sole basis for legal, financial, employment, academic, insurance, or identity decisions. High-impact contexts require human review. See docs/limitations.md.
False positive warning
Heuristics can misfire. Fabrics, printed pages, brick walls, mesh, halftone print, heavy compression, and genuinely patterned subjects can trigger noise, shadow, or screen-recapture signals even when the image is perfectly fine. Treat every signal as a possibility to review, not a conclusion.
Screen-recapture warning
The screen-recapture check is experimental. It looks for visual characteristics sometimes associated with photographing a phone, tablet, or monitor (frequency peaks, moiré-like energy, repeating line patterns, reflection cues). It can produce both false positives and false negatives and must never be treated as proof of recapture or fraud. Every report that runs this check includes an explicit warning to that effect.
Security and privacy
- Images are processed locally in your own Python environment and are not uploaded anywhere by this library.
- No network calls, no telemetry, no data collection.
- No temporary files or logs of image content during normal operation.
- Malformed images are handled as safely as practical via typed exceptions.
See SECURITY.md.
Contributing
Contributions are welcome! Please read CONTRIBUTING.md and our Code of Conduct. The quality gate is:
ruff check .
ruff format --check .
mypy src
pytest
What's included in 1.0.0
- Blur, resolution, lighting, glare, shadow, and noise detection
- Document framing check (are all four corners visible)
- Experimental screen-recapture detection (off by default)
- HEIC/HEIF support (iPhone photos), alongside JPEG/PNG/WebP/BMP/TIFF
- Three ready-made presets:
for_documents(),for_photos(),balanced() - EXIF-aware image loading (photos are always measured in their correct upright orientation)
- CLI (
capturetrust inspect,capturetrust batch) - Batch processing with optional multithreading
- Full test suite, type-checked, linted
Known limitations
- A blurry subject in front of a genuinely sharp background (or the reverse)
cannot be reliably told apart from pixels alone. See
docs/limitations.mdfor the full explanation. - Glare detection is calibrated against good photos, not against confirmed bad-glare examples - if precise glare tuning matters for your use case, provide labelled examples.
- Optional subject detection (face/document region focusing) is available but off by default: measured against a labelled photo set, it did not improve accuracy over the default centre-region approach.
Roadmap
Future direction depends on real-world feedback from this release. Candidates under consideration: a trained subject-detection model to address the known blurry-subject limitation, broader glare calibration against real bad-glare examples, and expanded format/locale testing.
License
Released under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file capturetrust-1.0.0.tar.gz.
File metadata
- Download URL: capturetrust-1.0.0.tar.gz
- Upload date:
- Size: 48.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c424e8f2382e9c6a88ef45331dba6e2237355b51526a2b4c027094de40c11922
|
|
| MD5 |
d2bc9339ada9b0d648a54da76faadb26
|
|
| BLAKE2b-256 |
f814d19eeaa9ff4e637fe6b57079ac7ec67d877df450155e07ccc56aeea8a782
|
File details
Details for the file capturetrust-1.0.0-py3-none-any.whl.
File metadata
- Download URL: capturetrust-1.0.0-py3-none-any.whl
- Upload date:
- Size: 44.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4fd6f213e2cf4d1ee8861307a7f77e3c33ae5d8e16c27a6092e7be1d1de65ef1
|
|
| MD5 |
8d51a27e5f1d23d0733d514dbeac8323
|
|
| BLAKE2b-256 |
fb0db5e0f7701d7c16cc5e7ab17ab635d84ee573873f09234e80f8d243917cf1
|