Perceptual, policy-driven image optimization engine for modern workflows
Project description
perceptimg
Perceptual, policy-driven image optimization for modern workflows
Overview
perceptimg is a Python library for perceptual, policy-driven image optimization. Unlike traditional tools that optimize for file size or a single quality knob, perceptimg analyzes image content, interprets declarative policies, tests multiple strategies, and selects the best candidate based on perceptual quality metrics.
Key Features
| Feature | Description |
|---|---|
| Policy-Driven | Declarative constraints for size, quality, and use case |
| Content-Aware | Analyzes images for text, faces, and edge density |
| Multi-Format | JPEG, PNG, WebP, AVIF, JXL, HEIF, GIF, TIFF, APNG |
| Perceptual Metrics | SSIM, PSNR, and weighted perceptual scoring |
| Explainable Results | Full decision trail for every optimization |
| Batch Processing | Parallel processing with progress callbacks |
| Enterprise Ready | Checkpointing, retry, rate limiting, Prometheus metrics |
| Clean Architecture | Dependency inversion, modular engines, extensible |
Supported Formats
Core Formats JPEG, PNG, TIFF, GIF (always available via Pillow)
Modern Formats WebP, AVIF, JPEG XL (JXL), HEIF/HEIC, APNG (codec-dependent)
Installation
From PyPI (Recommended)
pip install perceptimg
From Source
git clone https://github.com/seifreed/perceptimg.git
cd perceptimg
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -e ".[dev]"
Releases
Releases are created from version tags and are published by .github/workflows/release.yml.
git tag v0.1.2
git push origin v0.1.2
The release workflow:
- builds the wheel and sdist from the tagged commit,
- publishes to PyPI with Trusted Publishing / OIDC,
- attaches the built artifacts to the GitHub Release.
Before the first publish, add a PyPI trusted publisher for this repository using the workflow file release.yml and the GitHub environment pypi. The tag version must match project.version in pyproject.toml.
Quick Start
from perceptimg import optimize, Policy
# Define policy constraints
policy = Policy(
max_size_kb=150,
min_ssim=0.97,
preserve_text=True,
target_use_case="web"
)
# Optimize image
result = optimize("input.png", policy)
# Access results
print(f"Format: {result.report.chosen_format}")
print(f"Size: {result.report.size_before_kb:.1f}KB → {result.report.size_after_kb:.1f}KB")
print(f"SSIM: {result.report.ssim:.4f}")
print(f"Reasons: {result.report.reasons}")
Usage
Command Line Interface
# Basic optimization
perceptimg input.png --policy policy.json --out output.webp
# With inline constraints
perceptimg input.png --max-size-kb 100 --min-ssim 0.95 --formats webp,avif
# JSON output
perceptimg input.png --policy policy.json --log-json
# Batch processing with shell-expanded globs
perceptimg --batch images/*.png --output-dir optimized/
Available Options
| Option | Description |
|---|---|
--policy |
Path to JSON policy file |
--out |
Output file path for single-image mode only |
--max-size-kb |
Maximum output size in KB |
--min-ssim |
Minimum SSIM threshold (0.0-1.0) |
--formats |
Preferred formats (comma-separated) |
--preserve-text |
Prefer text clarity |
--preserve-faces |
Prefer face quality |
--log-json |
Structured JSON logging |
--log-level |
Log level (DEBUG, INFO, WARNING) |
For batch processing, use --output-dir instead of --out.
Use either --input-dir or positional input paths/globs for batch input, not both.
Policy Configuration
Python API
from perceptimg import Policy
policy = Policy(
max_size_kb=120,
min_ssim=0.98,
preserve_text=True,
preserve_faces=True,
allow_lossy=True,
preferred_formats=("webp", "avif", "jpeg"),
target_use_case="mobile",
)
JSON Configuration
{
"max_size_kb": 150,
"min_ssim": 0.97,
"preserve_text": true,
"preserve_faces": false,
"allow_lossy": true,
"preferred_formats": ["webp", "avif", "jpeg"],
"target_use_case": "web"
}
Batch Processing
Basic Batch
from perceptimg import Policy, optimize_batch
policy = Policy(max_size_kb=100, min_ssim=0.9)
images = ["img1.png", "img2.png", "img3.png"]
result = optimize_batch(images, policy)
print(f"Success: {len(result.successful)}/{result.total}")
print(f"Success rate: {result.success_rate:.1%}")
Async Processing
import asyncio
from perceptimg import Policy, optimize_batch_async
async def main():
policy = Policy(max_size_kb=100)
result = await optimize_batch_async(images, policy)
print(f"Processed {result.total} images")
asyncio.run(main())
Memory-Efficient Streaming
from perceptimg import Policy, optimize_lazy
policy = Policy(max_size_kb=100)
for path, result in optimize_lazy(large_image_list, policy):
if isinstance(result, Exception):
print(f"Error {path}: {result}")
else:
print(f"OK {path}: {result.report.size_after_kb:.1f}KB")
Enterprise Features
Checkpoint/Resume
from perceptimg import Policy, optimize_batch_with_checkpoint
result = optimize_batch_with_checkpoint(
images,
policy,
checkpoint_path="checkpoint.json",
checkpoint_interval=10, # Save every 10 images
)
# Resume after interruption by calling again with same checkpoint_path
Retry with Exponential Backoff
from perceptimg import Policy, optimize_batch_with_retry
from perceptimg.core.retry import RetryConfig
retry_config = RetryConfig(max_retries=3, base_delay_ms=100)
result = optimize_batch_with_retry(
images,
policy,
retry_config=retry_config,
continue_on_error=True,
)
Rate Limiting
from perceptimg import Policy, optimize_batch_with_rate_limit
from perceptimg.core.rate_limiter import RateLimitConfig
rate_limit = RateLimitConfig(requests_per_second=5)
result = optimize_batch_with_rate_limit(
images,
policy,
rate_limit=rate_limit,
)
Prometheus Metrics
from perceptimg import Policy, optimize_batch_with_metrics
from perceptimg.core.metrics_exporter import MetricsCollector
metrics = MetricsCollector()
result, stats = optimize_batch_with_metrics(images, policy, metrics=metrics)
print(f"Average SSIM: {stats['average_ssim']:.2f}")
print(f"Compression ratio: {stats['average_compression_ratio']:.1%}")
Architecture
perceptimg/
├── adapters/ # Framework adapters (PIL)
│ └── pil_adapter.py
├── core/ # Domain logic
│ ├── interfaces.py # Abstractions (ImageAdapter Protocol)
│ ├── analyzer.py # Content analysis
│ ├── metrics.py # SSIM, PSNR, perceptual scoring
│ ├── optimizer.py # Orchestration
│ ├── policy.py # Declarative constraints
│ ├── strategy.py # Candidate generation
│ └── batch/ # Batch processing
├── engines/ # Format-specific encoders
│ ├── webp_engine.py
│ ├── avif_engine.py
│ └── ...
└── utils/ # IO, heuristics, logging
Clean Architecture Compliance
| Layer | Responsibility |
|---|---|
| Core | Business logic, domain models, policies |
| Adapters | Framework-specific implementations |
| Engines | Format-specific encoding |
| Utils | Infrastructure services |
Extensibility
Custom Optimization Engine
from perceptimg.engines.base import OptimizationEngine, EngineResult
from perceptimg.core.optimizer import Optimizer, register_engine
class MyCustomEngine(OptimizationEngine):
format = "custom"
priority = 100
@property
def is_available(self) -> bool:
return True
def optimize(self, image, strategy) -> EngineResult:
# Custom optimization logic
return EngineResult(data=optimized_bytes, format="custom", quality=90)
# Register custom engine
optimizer = Optimizer()
register_engine(optimizer, MyCustomEngine())
Custom Analyzer
from perceptimg.core.analyzer import Analyzer
class CustomAnalyzer(Analyzer):
def analyze(self, image):
result = super().analyze(image)
# Add custom heuristics
result.custom_score = self._compute_custom(image)
return result
API Reference
OptimizationResult
| Field | Type | Description |
|---|---|---|
image_bytes |
bytes |
Optimized image data |
image |
PIL.Image |
Pillow image object |
report |
OptimizationReport |
Detailed results |
OptimizationReport
| Field | Type | Description |
|---|---|---|
chosen_format |
str |
Selected format |
quality |
int |
Quality setting |
size_before_kb |
float |
Original size |
size_after_kb |
float |
Optimized size |
ssim |
float |
SSIM score |
psnr |
float |
PSNR score |
perceptual_score |
float |
Weighted quality score |
reasons |
list[str] |
Decision trail |
Requirements
- Python 3.13+
- Pillow (PIL fork)
- NumPy
- scikit-image
- See pyproject.toml for full dependencies
Quality
This project enforces:
| Tool | Purpose |
|---|---|
ruff |
Linting |
black |
Formatting |
mypy |
Type checking |
bandit |
Security analysis |
pytest |
Testing (145 tests) |
# Run all quality checks
ruff check perceptimg tests
black perceptimg tests --check
mypy perceptimg --ignore-missing-imports
bandit -r perceptimg -q --exclude perceptimg/tests
pytest tests/
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Support the Project
If you find perceptimg useful, consider supporting its development:
License
This project is licensed under the MIT License - see the LICENSE file for details.
Attribution Required:
- Author: Marc Rivero | @seifreed
- Repository: github.com/seifreed/perceptimg
Made with dedication for the image optimization community
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 perceptimg-0.1.2.tar.gz.
File metadata
- Download URL: perceptimg-0.1.2.tar.gz
- Upload date:
- Size: 109.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a7ff2c8c2be1323a3f1856a7fba2e6fb85197606ee7cc8c5441e40f15439ab31
|
|
| MD5 |
af9a369cb9db10fb947c20f3a18b3b8b
|
|
| BLAKE2b-256 |
842d677ec90a5317d6771f4a29a747ebb3ed734266eecf94ca1d6cb345d01d01
|
Provenance
The following attestation bundles were made for perceptimg-0.1.2.tar.gz:
Publisher:
release.yml on seifreed/perceptimg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
perceptimg-0.1.2.tar.gz -
Subject digest:
a7ff2c8c2be1323a3f1856a7fba2e6fb85197606ee7cc8c5441e40f15439ab31 - Sigstore transparency entry: 1270782837
- Sigstore integration time:
-
Permalink:
seifreed/perceptimg@804961260a77f163a9049241a1583b09e8406bfd -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/seifreed
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@804961260a77f163a9049241a1583b09e8406bfd -
Trigger Event:
push
-
Statement type:
File details
Details for the file perceptimg-0.1.2-py3-none-any.whl.
File metadata
- Download URL: perceptimg-0.1.2-py3-none-any.whl
- Upload date:
- Size: 97.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
15e80ecaea9020b34f05971ce9928479ffc586255ab76353aca2b701e2d680fc
|
|
| MD5 |
4fb24f596623adf3c4a6e4497707d77f
|
|
| BLAKE2b-256 |
46fbb54dba889a942bdef62907d87a1962f16c230a0939edbcac26de9bbe2c73
|
Provenance
The following attestation bundles were made for perceptimg-0.1.2-py3-none-any.whl:
Publisher:
release.yml on seifreed/perceptimg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
perceptimg-0.1.2-py3-none-any.whl -
Subject digest:
15e80ecaea9020b34f05971ce9928479ffc586255ab76353aca2b701e2d680fc - Sigstore transparency entry: 1270782848
- Sigstore integration time:
-
Permalink:
seifreed/perceptimg@804961260a77f163a9049241a1583b09e8406bfd -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/seifreed
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@804961260a77f163a9049241a1583b09e8406bfd -
Trigger Event:
push
-
Statement type: