Skip to main content

perceptimg

perceptimg

Perceptual, policy-driven image optimization for modern workflows

PyPI Version Python Versions License CI Status Coverage

GitHub Stars GitHub Issues Buy Me a Coffee


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.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Support the Project

If you find perceptimg useful, consider supporting its development:

Buy Me A Coffee

License

This project is licensed under the MIT License - see the LICENSE file for details.

Attribution Required:


Made with dedication for the image optimization community

Metadata

Release files for perceptimg 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for perceptimg 0.1.2
File Size Uploaded
perceptimg-0.1.2.tar.gz 109.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for perceptimg 0.1.2
File Interpreter ABI Platform
perceptimg-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 207.7 kB

Release files / perceptimg-0.1.2.tar.gz

Download URL perceptimg-0.1.2.tar.gz
Size 109.9 kB
Tags Source
SHA-256 checksum
How to use checksums
a7ff2c8c2be1323a3f1856a7fba2e6fb85197606ee7cc8c5441e40f15439ab31
BLAKE2b-256 checksum
How to use checksums
842d677ec90a5317d6771f4a29a747ebb3ed734266eecf94ca1d6cb345d01d01
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 10, 2026.

Transparency log

Release files / perceptimg-0.1.2-py3-none-any.whl

Download URL perceptimg-0.1.2-py3-none-any.whl
Size 97.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
15e80ecaea9020b34f05971ce9928479ffc586255ab76353aca2b701e2d680fc
BLAKE2b-256 checksum
How to use checksums
46fbb54dba889a942bdef62907d87a1962f16c230a0939edbcac26de9bbe2c73
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page