perceptimg 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.
Files changed (108) hide show
  1. perceptimg-0.1.0/LICENSE +21 -0
  2. perceptimg-0.1.0/MANIFEST.in +5 -0
  3. perceptimg-0.1.0/PKG-INFO +478 -0
  4. perceptimg-0.1.0/README.md +434 -0
  5. perceptimg-0.1.0/examples/basic_usage.py +27 -0
  6. perceptimg-0.1.0/perceptimg/__init__.py +84 -0
  7. perceptimg-0.1.0/perceptimg/__main__.py +6 -0
  8. perceptimg-0.1.0/perceptimg/adapters/__init__.py +19 -0
  9. perceptimg-0.1.0/perceptimg/adapters/pil_adapter.py +221 -0
  10. perceptimg-0.1.0/perceptimg/cli.py +374 -0
  11. perceptimg-0.1.0/perceptimg/core/__init__.py +0 -0
  12. perceptimg-0.1.0/perceptimg/core/analyzer.py +113 -0
  13. perceptimg-0.1.0/perceptimg/core/batch/__init__.py +528 -0
  14. perceptimg-0.1.0/perceptimg/core/batch/cache.py +93 -0
  15. perceptimg-0.1.0/perceptimg/core/batch/config.py +90 -0
  16. perceptimg-0.1.0/perceptimg/core/batch/processor.py +144 -0
  17. perceptimg-0.1.0/perceptimg/core/batch.py +54 -0
  18. perceptimg-0.1.0/perceptimg/core/checkpoint.py +341 -0
  19. perceptimg-0.1.0/perceptimg/core/distributed.py +394 -0
  20. perceptimg-0.1.0/perceptimg/core/interfaces.py +126 -0
  21. perceptimg-0.1.0/perceptimg/core/metrics.py +205 -0
  22. perceptimg-0.1.0/perceptimg/core/metrics_exporter.py +303 -0
  23. perceptimg-0.1.0/perceptimg/core/optimizer.py +316 -0
  24. perceptimg-0.1.0/perceptimg/core/policy.py +187 -0
  25. perceptimg-0.1.0/perceptimg/core/rate_limiter.py +181 -0
  26. perceptimg-0.1.0/perceptimg/core/report.py +71 -0
  27. perceptimg-0.1.0/perceptimg/core/retry.py +244 -0
  28. perceptimg-0.1.0/perceptimg/core/strategy.py +157 -0
  29. perceptimg-0.1.0/perceptimg/engines/__init__.py +0 -0
  30. perceptimg-0.1.0/perceptimg/engines/apng_engine.py +41 -0
  31. perceptimg-0.1.0/perceptimg/engines/avif_engine.py +47 -0
  32. perceptimg-0.1.0/perceptimg/engines/base.py +42 -0
  33. perceptimg-0.1.0/perceptimg/engines/heif_engine.py +49 -0
  34. perceptimg-0.1.0/perceptimg/engines/jxl_engine.py +46 -0
  35. perceptimg-0.1.0/perceptimg/engines/pillow_engine.py +50 -0
  36. perceptimg-0.1.0/perceptimg/engines/webp_engine.py +49 -0
  37. perceptimg-0.1.0/perceptimg/exceptions.py +23 -0
  38. perceptimg-0.1.0/perceptimg/formats/__init__.py +16 -0
  39. perceptimg-0.1.0/perceptimg/formats/apng.py +18 -0
  40. perceptimg-0.1.0/perceptimg/formats/avif.py +22 -0
  41. perceptimg-0.1.0/perceptimg/formats/gif.py +18 -0
  42. perceptimg-0.1.0/perceptimg/formats/heif.py +27 -0
  43. perceptimg-0.1.0/perceptimg/formats/jpeg.py +35 -0
  44. perceptimg-0.1.0/perceptimg/formats/jxl.py +27 -0
  45. perceptimg-0.1.0/perceptimg/formats/png.py +20 -0
  46. perceptimg-0.1.0/perceptimg/formats/tiff.py +21 -0
  47. perceptimg-0.1.0/perceptimg/formats/webp.py +22 -0
  48. perceptimg-0.1.0/perceptimg/perceptimg/__main__.py +8 -0
  49. perceptimg-0.1.0/perceptimg/tests/test_cli_defaults.py +37 -0
  50. perceptimg-0.1.0/perceptimg/tests/test_cli_policy_flags.py +24 -0
  51. perceptimg-0.1.0/perceptimg/tests/test_engine_fallback.py +38 -0
  52. perceptimg-0.1.0/perceptimg/tests/test_engine_priority.py +37 -0
  53. perceptimg-0.1.0/perceptimg/tests/test_engine_priority_selection.py +53 -0
  54. perceptimg-0.1.0/perceptimg/tests/test_optimize_bytes_original.py +15 -0
  55. perceptimg-0.1.0/perceptimg/tests/test_optimize_from_analysis.py +15 -0
  56. perceptimg-0.1.0/perceptimg/tests/test_optimizer_memory.py +26 -0
  57. perceptimg-0.1.0/perceptimg/utils/__init__.py +0 -0
  58. perceptimg-0.1.0/perceptimg/utils/heuristics.py +166 -0
  59. perceptimg-0.1.0/perceptimg/utils/image_io.py +130 -0
  60. perceptimg-0.1.0/perceptimg/utils/logging_config.py +61 -0
  61. perceptimg-0.1.0/perceptimg/utils/validation.py +36 -0
  62. perceptimg-0.1.0/perceptimg.egg-info/PKG-INFO +478 -0
  63. perceptimg-0.1.0/perceptimg.egg-info/SOURCES.txt +106 -0
  64. perceptimg-0.1.0/perceptimg.egg-info/dependency_links.txt +1 -0
  65. perceptimg-0.1.0/perceptimg.egg-info/entry_points.txt +2 -0
  66. perceptimg-0.1.0/perceptimg.egg-info/requires.txt +14 -0
  67. perceptimg-0.1.0/perceptimg.egg-info/top_level.txt +1 -0
  68. perceptimg-0.1.0/pyproject.toml +118 -0
  69. perceptimg-0.1.0/setup.cfg +4 -0
  70. perceptimg-0.1.0/tests/__init__.py +0 -0
  71. perceptimg-0.1.0/tests/test_additional_coverage.py +153 -0
  72. perceptimg-0.1.0/tests/test_analyzer.py +19 -0
  73. perceptimg-0.1.0/tests/test_batch.py +255 -0
  74. perceptimg-0.1.0/tests/test_cli_coverage.py +47 -0
  75. perceptimg-0.1.0/tests/test_cli_default_outputs.py +22 -0
  76. perceptimg-0.1.0/tests/test_cli_main.py +70 -0
  77. perceptimg-0.1.0/tests/test_cli_main_default.py +22 -0
  78. perceptimg-0.1.0/tests/test_cli_main_default_out.py +22 -0
  79. perceptimg-0.1.0/tests/test_cli_main_entrypoint.py +42 -0
  80. perceptimg-0.1.0/tests/test_engines.py +73 -0
  81. perceptimg-0.1.0/tests/test_engines_extended.py +225 -0
  82. perceptimg-0.1.0/tests/test_engines_success.py +53 -0
  83. perceptimg-0.1.0/tests/test_enterprise.py +270 -0
  84. perceptimg-0.1.0/tests/test_heuristics_edge_case.py +12 -0
  85. perceptimg-0.1.0/tests/test_heuristics_line56.py +11 -0
  86. perceptimg-0.1.0/tests/test_image_io_errors.py +100 -0
  87. perceptimg-0.1.0/tests/test_logging_and_validation.py +14 -0
  88. perceptimg-0.1.0/tests/test_logging_formatter.py +32 -0
  89. perceptimg-0.1.0/tests/test_metrics.py +24 -0
  90. perceptimg-0.1.0/tests/test_metrics_perceptual_score.py +15 -0
  91. perceptimg-0.1.0/tests/test_metrics_perceptual_score_zero.py +13 -0
  92. perceptimg-0.1.0/tests/test_metrics_score.py +13 -0
  93. perceptimg-0.1.0/tests/test_optimizer.py +65 -0
  94. perceptimg-0.1.0/tests/test_optimizer_branches.py +64 -0
  95. perceptimg-0.1.0/tests/test_optimizer_branches_more.py +82 -0
  96. perceptimg-0.1.0/tests/test_optimizer_errors.py +40 -0
  97. perceptimg-0.1.0/tests/test_optimizer_no_candidates.py +23 -0
  98. perceptimg-0.1.0/tests/test_optimizer_no_candidates_path.py +29 -0
  99. perceptimg-0.1.0/tests/test_optimizer_policy_accepts.py +27 -0
  100. perceptimg-0.1.0/tests/test_optimizer_policy_reject.py +29 -0
  101. perceptimg-0.1.0/tests/test_policy.py +62 -0
  102. perceptimg-0.1.0/tests/test_policy_edge_cases.py +134 -0
  103. perceptimg-0.1.0/tests/test_policy_from_json_error.py +8 -0
  104. perceptimg-0.1.0/tests/test_strategy_edge.py +28 -0
  105. perceptimg-0.1.0/tests/test_strategy_policy_combinations.py +17 -0
  106. perceptimg-0.1.0/tests/test_utils_and_formats.py +98 -0
  107. perceptimg-0.1.0/tests/test_utils_edges.py +41 -0
  108. perceptimg-0.1.0/tests/test_validation_edges.py +10 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marc Rivero Lopez
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.
@@ -0,0 +1,5 @@
1
+ include LICENSE
2
+ include README.md
3
+ include pyproject.toml
4
+ recursive-include perceptimg *.py
5
+ recursive-include examples *.py *.png *.jpg
@@ -0,0 +1,478 @@
1
+ Metadata-Version: 2.4
2
+ Name: perceptimg
3
+ Version: 0.1.0
4
+ Summary: Perceptual, policy-driven image optimization engine for modern workflows
5
+ Author-email: Marc Rivero Lopez <mriverolopez@gmail.com>
6
+ Maintainer-email: Marc Rivero Lopez <mriverolopez@gmail.com>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/seifreed/perceptimg
9
+ Project-URL: Documentation, https://github.com/seifreed/perceptimg#readme
10
+ Project-URL: Repository, https://github.com/seifreed/perceptimg
11
+ Project-URL: Issues, https://github.com/seifreed/perceptimg/issues
12
+ Project-URL: Changelog, https://github.com/seifreed/perceptimg/releases
13
+ Project-URL: Funding, https://buymeacoffee.com/seifreed
14
+ Keywords: image,optimization,compression,perceptual,ssim,psnr,webp,avif,jpeg-xl,heif,batch-processing,policy-driven
15
+ Classifier: Development Status :: 5 - Production/Stable
16
+ Classifier: Environment :: Console
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Multimedia :: Graphics
24
+ Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
25
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
26
+ Classifier: Typing :: Typed
27
+ Classifier: Framework :: AsyncIO
28
+ Requires-Python: >=3.13
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE
31
+ Requires-Dist: pillow>=10.0.0
32
+ Requires-Dist: numpy>=1.26.0
33
+ Requires-Dist: scikit-image>=0.22.0
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7.4; extra == "dev"
36
+ Requires-Dist: pytest-cov>=4.1; extra == "dev"
37
+ Requires-Dist: ruff>=0.4; extra == "dev"
38
+ Requires-Dist: black>=24.0; extra == "dev"
39
+ Requires-Dist: mypy>=1.8; extra == "dev"
40
+ Requires-Dist: bandit>=1.7; extra == "dev"
41
+ Provides-Extra: redis
42
+ Requires-Dist: redis>=4.5.0; extra == "redis"
43
+ Dynamic: license-file
44
+
45
+ <p align="center">
46
+ <img src="https://img.shields.io/badge/perceptimg-Image%20Optimization-blue?style=for-the-badge" alt="perceptimg">
47
+ </p>
48
+
49
+ <h1 align="center">perceptimg</h1>
50
+
51
+ <p align="center">
52
+ <strong>Perceptual, policy-driven image optimization for modern workflows</strong>
53
+ </p>
54
+
55
+ <p align="center">
56
+ <a href="https://pypi.org/project/perceptimg/"><img src="https://img.shields.io/pypi/v/perceptimg?style=flat-square&logo=pypi&logoColor=white" alt="PyPI Version"></a>
57
+ <a href="https://pypi.org/project/perceptimg/"><img src="https://img.shields.io/pypi/pyversions/perceptimg?style=flat-square&logo=python&logoColor=white" alt="Python Versions"></a>
58
+ <a href="https://github.com/seifreed/perceptimg/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="License"></a>
59
+ <a href="https://github.com/seifreed/perceptimg/actions"><img src="https://img.shields.io/github/actions/workflow/status/seifreed/perceptimg/ci.yml?style=flat-square&logo=github&label=CI" alt="CI Status"></a>
60
+ <a href="https://codecov.io/gh/seifreed/perceptimg"><img src="https://codecov.io/gh/seifreed/perceptimg/branch/main/graph/badge.svg?style=flat-square" alt="Coverage"></a>
61
+ </p>
62
+
63
+ <p align="center">
64
+ <a href="https://github.com/seifreed/perceptimg/stargazers"><img src="https://img.shields.io/github/stars/seifreed/perceptimg?style=flat-square" alt="GitHub Stars"></a>
65
+ <a href="https://github.com/seifreed/perceptimg/issues"><img src="https://img.shields.io/github/issues/seifreed/perceptimg?style=flat-square" alt="GitHub Issues"></a>
66
+ <a href="https://buymeacoffee.com/seifreed"><img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-support-yellow?style=flat-square&logo=buy-me-a-coffee&logoColor=white" alt="Buy Me a Coffee"></a>
67
+ </p>
68
+
69
+ ---
70
+
71
+ ## Overview
72
+
73
+ **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.
74
+
75
+ ### Key Features
76
+
77
+ | Feature | Description |
78
+ |---------|-------------|
79
+ | **Policy-Driven** | Declarative constraints for size, quality, and use case |
80
+ | **Content-Aware** | Analyzes images for text, faces, and edge density |
81
+ | **Multi-Format** | JPEG, PNG, WebP, AVIF, JXL, HEIF, GIF, TIFF, APNG |
82
+ | **Perceptual Metrics** | SSIM, PSNR, and weighted perceptual scoring |
83
+ | **Explainable Results** | Full decision trail for every optimization |
84
+ | **Batch Processing** | Parallel processing with progress callbacks |
85
+ | **Enterprise Ready** | Checkpointing, retry, rate limiting, Prometheus metrics |
86
+ | **Clean Architecture** | Dependency inversion, modular engines, extensible |
87
+
88
+ ### Supported Formats
89
+
90
+ ```
91
+ Core Formats JPEG, PNG, TIFF, GIF (always available via Pillow)
92
+ Modern Formats WebP, AVIF, JPEG XL (JXL), HEIF/HEIC, APNG (codec-dependent)
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Installation
98
+
99
+ ### From PyPI (Recommended)
100
+
101
+ ```bash
102
+ pip install perceptimg
103
+ ```
104
+
105
+ ### From Source
106
+
107
+ ```bash
108
+ git clone https://github.com/seifreed/perceptimg.git
109
+ cd perceptimg
110
+ python3 -m venv venv
111
+ source venv/bin/activate # Windows: venv\Scripts\activate
112
+ pip install -e ".[dev]"
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Quick Start
118
+
119
+ ```python
120
+ from perceptimg import optimize, Policy
121
+
122
+ # Define policy constraints
123
+ policy = Policy(
124
+ max_size_kb=150,
125
+ min_ssim=0.97,
126
+ preserve_text=True,
127
+ target_use_case="web"
128
+ )
129
+
130
+ # Optimize image
131
+ result = optimize("input.png", policy)
132
+
133
+ # Access results
134
+ print(f"Format: {result.report.chosen_format}")
135
+ print(f"Size: {result.report.size_before_kb:.1f}KB → {result.report.size_after_kb:.1f}KB")
136
+ print(f"SSIM: {result.report.ssim:.4f}")
137
+ print(f"Reasons: {result.report.reasons}")
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Usage
143
+
144
+ ### Command Line Interface
145
+
146
+ ```bash
147
+ # Basic optimization
148
+ perceptimg optimize input.png --policy policy.json --out output.webp
149
+
150
+ # With inline constraints
151
+ perceptimg optimize input.png --max-size-kb 100 --min-ssim 0.95 --formats webp,avif
152
+
153
+ # JSON output
154
+ perceptimg optimize input.png --policy policy.json --log-json
155
+ ```
156
+
157
+ ### Available Options
158
+
159
+ | Option | Description |
160
+ |--------|-------------|
161
+ | `--policy` | Path to JSON policy file |
162
+ | `--out` | Output file path |
163
+ | `--max-size-kb` | Maximum output size in KB |
164
+ | `--min-ssim` | Minimum SSIM threshold (0.0-1.0) |
165
+ | `--formats` | Preferred formats (comma-separated) |
166
+ | `--preserve-text` | Prefer text clarity |
167
+ | `--preserve-faces` | Prefer face quality |
168
+ | `--log-json` | Structured JSON logging |
169
+ | `--log-level` | Log level (DEBUG, INFO, WARNING) |
170
+
171
+ ---
172
+
173
+ ## Policy Configuration
174
+
175
+ ### Python API
176
+
177
+ ```python
178
+ from perceptimg import Policy
179
+
180
+ policy = Policy(
181
+ max_size_kb=120,
182
+ min_ssim=0.98,
183
+ preserve_text=True,
184
+ preserve_faces=True,
185
+ allow_lossy=True,
186
+ preferred_formats=("webp", "avif", "jpeg"),
187
+ target_use_case="mobile",
188
+ )
189
+ ```
190
+
191
+ ### JSON Configuration
192
+
193
+ ```json
194
+ {
195
+ "max_size_kb": 150,
196
+ "min_ssim": 0.97,
197
+ "preserve_text": true,
198
+ "preserve_faces": false,
199
+ "allow_lossy": true,
200
+ "preferred_formats": ["webp", "avif", "jpeg"],
201
+ "target_use_case": "web"
202
+ }
203
+ ```
204
+
205
+ ---
206
+
207
+ ## Batch Processing
208
+
209
+ ### Basic Batch
210
+
211
+ ```python
212
+ from perceptimg import Policy, optimize_batch
213
+
214
+ policy = Policy(max_size_kb=100, min_ssim=0.9)
215
+ images = ["img1.png", "img2.png", "img3.png"]
216
+
217
+ result = optimize_batch(images, policy)
218
+ print(f"Success: {len(result.successful)}/{result.total}")
219
+ print(f"Success rate: {result.success_rate:.1%}")
220
+ ```
221
+
222
+ ### Async Processing
223
+
224
+ ```python
225
+ import asyncio
226
+ from perceptimg import Policy, optimize_batch_async
227
+
228
+ async def main():
229
+ policy = Policy(max_size_kb=100)
230
+ result = await optimize_batch_async(images, policy)
231
+ print(f"Processed {result.total} images")
232
+
233
+ asyncio.run(main())
234
+ ```
235
+
236
+ ### Memory-Efficient Streaming
237
+
238
+ ```python
239
+ from perceptimg import Policy, optimize_lazy
240
+
241
+ policy = Policy(max_size_kb=100)
242
+
243
+ for path, result in optimize_lazy(large_image_list, policy):
244
+ if isinstance(result, Exception):
245
+ print(f"Error {path}: {result}")
246
+ else:
247
+ print(f"OK {path}: {result.report.size_after_kb:.1f}KB")
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Enterprise Features
253
+
254
+ ### Checkpoint/Resume
255
+
256
+ ```python
257
+ from perceptimg import Policy, optimize_batch_with_checkpoint
258
+
259
+ result = optimize_batch_with_checkpoint(
260
+ images,
261
+ policy,
262
+ checkpoint_path="checkpoint.json",
263
+ checkpoint_interval=10, # Save every 10 images
264
+ )
265
+ # Resume after interruption by calling again with same checkpoint_path
266
+ ```
267
+
268
+ ### Retry with Exponential Backoff
269
+
270
+ ```python
271
+ from perceptimg import Policy, optimize_batch_with_retry
272
+ from perceptimg.core.retry import RetryConfig
273
+
274
+ retry_config = RetryConfig(max_retries=3, base_delay_ms=100)
275
+
276
+ result = optimize_batch_with_retry(
277
+ images,
278
+ policy,
279
+ retry_config=retry_config,
280
+ continue_on_error=True,
281
+ )
282
+ ```
283
+
284
+ ### Rate Limiting
285
+
286
+ ```python
287
+ from perceptimg import Policy, optimize_batch_with_rate_limit
288
+ from perceptimg.core.rate_limiter import RateLimitConfig
289
+
290
+ rate_limit = RateLimitConfig(requests_per_second=5)
291
+
292
+ result = optimize_batch_with_rate_limit(
293
+ images,
294
+ policy,
295
+ rate_limit=rate_limit,
296
+ )
297
+ ```
298
+
299
+ ### Prometheus Metrics
300
+
301
+ ```python
302
+ from perceptimg import Policy, optimize_batch_with_metrics
303
+ from perceptimg.core.metrics_exporter import MetricsCollector
304
+
305
+ metrics = MetricsCollector()
306
+ result, stats = optimize_batch_with_metrics(images, policy, metrics=metrics)
307
+
308
+ print(f"Average SSIM: {stats['average_ssim']:.2f}")
309
+ print(f"Compression ratio: {stats['average_compression_ratio']:.1%}")
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Architecture
315
+
316
+ ```
317
+ perceptimg/
318
+ ├── adapters/ # Framework adapters (PIL)
319
+ │ └── pil_adapter.py
320
+ ├── core/ # Domain logic
321
+ │ ├── interfaces.py # Abstractions (ImageAdapter Protocol)
322
+ │ ├── analyzer.py # Content analysis
323
+ │ ├── metrics.py # SSIM, PSNR, perceptual scoring
324
+ │ ├── optimizer.py # Orchestration
325
+ │ ├── policy.py # Declarative constraints
326
+ │ ├── strategy.py # Candidate generation
327
+ │ └── batch/ # Batch processing
328
+ ├── engines/ # Format-specific encoders
329
+ │ ├── webp_engine.py
330
+ │ ├── avif_engine.py
331
+ │ └── ...
332
+ └── utils/ # IO, heuristics, logging
333
+ ```
334
+
335
+ ### Clean Architecture Compliance
336
+
337
+ | Layer | Responsibility |
338
+ |-------|----------------|
339
+ | **Core** | Business logic, domain models, policies |
340
+ | **Adapters** | Framework-specific implementations |
341
+ | **Engines** | Format-specific encoding |
342
+ | **Utils** | Infrastructure services |
343
+
344
+ ---
345
+
346
+ ## Extensibility
347
+
348
+ ### Custom Optimization Engine
349
+
350
+ ```python
351
+ from perceptimg.engines.base import OptimizationEngine, EngineResult
352
+ from perceptimg.core.optimizer import Optimizer, register_engine
353
+
354
+ class MyCustomEngine(OptimizationEngine):
355
+ format = "custom"
356
+ priority = 100
357
+
358
+ @property
359
+ def is_available(self) -> bool:
360
+ return True
361
+
362
+ def optimize(self, image, strategy) -> EngineResult:
363
+ # Custom optimization logic
364
+ return EngineResult(data=optimized_bytes, format="custom", quality=90)
365
+
366
+ # Register custom engine
367
+ optimizer = Optimizer()
368
+ register_engine(optimizer, MyCustomEngine())
369
+ ```
370
+
371
+ ### Custom Analyzer
372
+
373
+ ```python
374
+ from perceptimg.core.analyzer import Analyzer
375
+
376
+ class CustomAnalyzer(Analyzer):
377
+ def analyze(self, image):
378
+ result = super().analyze(image)
379
+ # Add custom heuristics
380
+ result.custom_score = self._compute_custom(image)
381
+ return result
382
+ ```
383
+
384
+ ---
385
+
386
+ ## API Reference
387
+
388
+ ### OptimizationResult
389
+
390
+ | Field | Type | Description |
391
+ |-------|------|-------------|
392
+ | `image_bytes` | `bytes` | Optimized image data |
393
+ | `image` | `PIL.Image` | Pillow image object |
394
+ | `report` | `OptimizationReport` | Detailed results |
395
+
396
+ ### OptimizationReport
397
+
398
+ | Field | Type | Description |
399
+ |-------|------|-------------|
400
+ | `chosen_format` | `str` | Selected format |
401
+ | `quality` | `int` | Quality setting |
402
+ | `size_before_kb` | `float` | Original size |
403
+ | `size_after_kb` | `float` | Optimized size |
404
+ | `ssim` | `float` | SSIM score |
405
+ | `psnr` | `float` | PSNR score |
406
+ | `perceptual_score` | `float` | Weighted quality score |
407
+ | `reasons` | `list[str]` | Decision trail |
408
+
409
+ ---
410
+
411
+ ## Requirements
412
+
413
+ - Python 3.13+
414
+ - Pillow (PIL fork)
415
+ - NumPy
416
+ - scikit-image
417
+ - See [pyproject.toml](pyproject.toml) for full dependencies
418
+
419
+ ---
420
+
421
+ ## Quality
422
+
423
+ This project enforces:
424
+
425
+ | Tool | Purpose |
426
+ |------|---------|
427
+ | `ruff` | Linting |
428
+ | `black` | Formatting |
429
+ | `mypy` | Type checking |
430
+ | `bandit` | Security analysis |
431
+ | `pytest` | Testing (145 tests) |
432
+
433
+ ```bash
434
+ # Run all quality checks
435
+ ruff check perceptimg tests
436
+ black perceptimg tests --check
437
+ mypy perceptimg --ignore-missing-imports
438
+ bandit -r perceptimg -q --exclude perceptimg/tests
439
+ pytest tests/
440
+ ```
441
+
442
+ ---
443
+
444
+ ## Contributing
445
+
446
+ Contributions are welcome! Please feel free to submit a Pull Request.
447
+
448
+ 1. Fork the repository
449
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
450
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
451
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
452
+ 5. Open a Pull Request
453
+
454
+ ---
455
+
456
+ ## Support the Project
457
+
458
+ If you find perceptimg useful, consider supporting its development:
459
+
460
+ <a href="https://buymeacoffee.com/seifreed" target="_blank">
461
+ <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50">
462
+ </a>
463
+
464
+ ---
465
+
466
+ ## License
467
+
468
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
469
+
470
+ **Attribution Required:**
471
+ - Author: **Marc Rivero** | [@seifreed](https://github.com/seifreed)
472
+ - Repository: [github.com/seifreed/perceptimg](https://github.com/seifreed/perceptimg)
473
+
474
+ ---
475
+
476
+ <p align="center">
477
+ <sub>Made with dedication for the image optimization community</sub>
478
+ </p>