coriro 1.0.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.
coriro-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Saad Irfan (saadirfan.com)
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,18 @@
1
+ include LICENSE
2
+ include README.md
3
+ include THIRD_PARTY_NOTICES.md
4
+ include coriro/py.typed
5
+
6
+ exclude .gitignore
7
+ recursive-exclude docs *
8
+ recursive-exclude tests *
9
+ recursive-exclude benchmark *
10
+ recursive-exclude coriro/adapters *
11
+ recursive-exclude coriro/benchmark *
12
+ recursive-exclude .coriro-audit *
13
+ recursive-exclude examples *
14
+ recursive-exclude *.egg-info *
15
+
16
+ global-exclude *.pyc
17
+ global-exclude __pycache__
18
+ global-exclude .DS_Store
coriro-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,335 @@
1
+ Metadata-Version: 2.4
2
+ Name: coriro
3
+ Version: 1.0.0
4
+ Summary: Color measurement instrument for vision-language model (VLM) pipelines
5
+ Author-email: Saad Irfan <saad@coriro.org>
6
+ License: MIT
7
+ Project-URL: Homepage, https://coriro.org
8
+ Project-URL: Documentation, https://coriro.org/docs
9
+ Project-URL: Source Code, https://github.com/coriro-org/coriro
10
+ Project-URL: Bug Tracker, https://github.com/coriro-org/coriro/issues
11
+ Keywords: color,extraction,vlm,vision-language-model,oklch,palette,sidecar,measurement
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Multimedia :: Graphics
24
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
25
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.9
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE
30
+ Requires-Dist: numpy>=1.20.0
31
+ Requires-Dist: Pillow>=8.0.0
32
+ Provides-Extra: text
33
+ Requires-Dist: pytesseract>=0.3.0; extra == "text"
34
+ Provides-Extra: accents
35
+ Requires-Dist: scipy>=1.7.0; extra == "accents"
36
+ Provides-Extra: cnn
37
+ Requires-Dist: torch>=2.0.0; extra == "cnn"
38
+ Requires-Dist: timm>=0.9.0; extra == "cnn"
39
+ Provides-Extra: all
40
+ Requires-Dist: pytesseract>=0.3.0; extra == "all"
41
+ Requires-Dist: scipy>=1.7.0; extra == "all"
42
+ Requires-Dist: torch>=2.0.0; extra == "all"
43
+ Requires-Dist: timm>=0.9.0; extra == "all"
44
+ Provides-Extra: dev
45
+ Requires-Dist: pytest>=7.0; extra == "dev"
46
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
47
+ Dynamic: license-file
48
+
49
+ # Coriro
50
+
51
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
52
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/downloads/)
53
+
54
+ Color measurement runtime for vision-language and multimodal inference pipelines.
55
+
56
+ Coriro extracts color data from image pixels — dominant colors, weighted palettes, spatial distribution — and passes them alongside the image into any vision-language or multimodal model pipeline.
57
+
58
+ Core runtime is pure Python. No GPU required. Zero lock-in.
59
+
60
+ ```python
61
+ from coriro import measure
62
+
63
+ m = measure("image.png")
64
+
65
+ m.to_json() # Raw measurement JSON (schema)
66
+ m.to_xml() # XML block for context injection
67
+ m.to_prompt() # Natural language for system prompts
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Why Coriro exists
73
+
74
+ It reads actual image pixels, extracts palettes in perceptually uniform OKLCH, consolidates near-identical colors, catches high-saturation outliers, and maps spatial distribution. The results are output as structured data (JSON, XML, natural language) that any pipeline can consume.
75
+
76
+ The output includes measurement criteria, coverage thresholds, collapse distances, and palette caps, so the receiving model knows exactly what was measured and what was excluded.
77
+
78
+ The most immediate reason to use it: multimodal and vision-language models lose color precision during vision encoding. Patch-based encoders downsample and average pixels, so the language model generates color values that are sometimes close, often wrong. Coriro runs pixel-level colorimetry outside the model and passes the results as plain text, the channel where language models are already precise.
79
+
80
+ But the value extends beyond compensating for encoder limitations. Even a model with perfect color perception does not produce a consolidated, weighted palette in a perceptually uniform color space with spatial distribution and measurement metadata. That is a measurement task. Coriro's output is useful as pipeline data, whether the model's vision is approximate or exact.
81
+
82
+ ```
83
+ ┌──────────────────────────────────────────────────────┐
84
+ │ VLM Pipeline │
85
+ │ │
86
+ │ ┌──────────┐ ┌────────────────────────────────┐ │
87
+ │ │ Image │───▶│ Vision Encoder │ │
88
+ │ └──────────┘ │ Sees layout, structure, UX │ │
89
+ │ │ │ Approximate color │ │
90
+ │ │ └────────────────┬───────────────┘ │
91
+ │ │ │ │
92
+ │ ▼ ▼ │
93
+ │ ┌──────────┐ ┌────────────────────────────────┐ │
94
+ │ │ Coriro │───▶│ Language Model Context │ │
95
+ │ │ measure()│ │ Image + Color sidecar data │ │
96
+ │ └──────────┘ │ = Complete information │ │
97
+ │ Pixel-level └────────────────────────────────┘ │
98
+ │ colorimetry │
99
+ │ (sidecar) │
100
+ └──────────────────────────────────────────────────────┘
101
+ ```
102
+
103
+ **The sidecar principle:** Coriro output runs *alongside* the image, not as a replacement. The model gets the image for layout, hierarchy, and semantics, plus measured color data for precise implementation. Remove Coriro from the pipeline and the model works exactly as before. No weights are modified. No configuration is changed.
104
+
105
+ ---
106
+
107
+ ## Use cases
108
+
109
+ - **VLM color grounding** — Provide measured palettes as structured data alongside the image, compensating for vision-encoder color loss documented in published benchmarks across many models.
110
+ - **Screenshot-to-code** — Provide exact hex values and spatial color distribution so code generation uses measured colors instead of vision-inferred estimates.
111
+ - **Product color metadata** — Attach verifiable weighted palettes to product listings for perceptual color search, cross-SKU consistency checks, and return-reduction workflows.
112
+ - **Design system compliance** — Measure rendered screenshots against design token definitions using perceptual ΔE distance to catch cross-platform drift.
113
+ - **Accessibility contrast auditing** — Compute contrast from rendered pixels in perceptually uniform OKLCH, including text over gradients and background images that DOM-only scanners can miss.
114
+ - **Color-aware asset search** — Index images by weighted palette and spatial distribution for retrieval by color proportion, placement, and perceptual similarity.
115
+
116
+ ---
117
+
118
+ ## Install
119
+
120
+ ```bash
121
+ pip install coriro
122
+ ```
123
+
124
+ Core installation requires only `numpy` and `Pillow`. Optional passes have separate dependencies:
125
+
126
+ | Feature | Install | Requires |
127
+ |---------|---------|----------|
128
+ | Text colors | `pip install coriro[text]` | `pytesseract` + system Tesseract OCR |
129
+ | Accent regions | `pip install coriro[accents]` | `scipy` |
130
+ | CNN smoothing | `pip install coriro[cnn]` | `torch`, `timm` |
131
+ | All features | `pip install coriro[all]` | All of the above |
132
+
133
+ ---
134
+
135
+ ## Quick start
136
+
137
+ ### Measure an image
138
+
139
+ ```python
140
+ from coriro import measure
141
+
142
+ m = measure("image.png")
143
+
144
+ # Dominant color (OKLCH)
145
+ print(m.dominant.hex) # hex from a real pixel in the cluster
146
+ print(m.dominant.L) # Lightness (0.0–1.0)
147
+ print(m.dominant.C) # Chroma (0.0–~0.32)
148
+ print(m.dominant.is_achromatic) # True when C < 0.02
149
+
150
+ # Palette (weighted, most dominant first)
151
+ for wc in m.palette:
152
+ print(f"{wc.color.hex} — {wc.weight:.0%}")
153
+
154
+ # Spatial distribution
155
+ region = m.spatial.get_region("R1C1") # Top-left
156
+ print(region.dominant.hex)
157
+ ```
158
+
159
+ ### Optional passes
160
+
161
+ Three additional passes are available. Each is off by default and independently toggleable:
162
+
163
+ ```python
164
+ m = measure(
165
+ "image.png",
166
+ smooth=True, # CNN pixel stabilization (requires torch + timm)
167
+ include_text=True, # Text foreground colors via OCR (requires pytesseract)
168
+ include_accents=True, # Solid accent region detection (requires scipy)
169
+ )
170
+ ```
171
+
172
+ > **CNN smoothing** stabilizes color surfaces before extraction — reducing noise from compression artifacts, gradient banding, and anti-aliasing. It is off by default because it requires `torch` + `timm` (`pip install coriro[cnn]`). If your environment already includes `torch` + `timm`, consider enabling `smooth=True` for improved measurement quality.
173
+
174
+ > **Latency control:** Coriro processes images at full resolution by default (`max_pixels=0`). For latency-sensitive pipelines, set `max_pixels` to cap the pixel count before processing. Because color measurement is statistical, equivalent palettes are usually preserved at reduced resolution.
175
+
176
+ ### Pipeline integration
177
+
178
+ ```python
179
+ from coriro import measure
180
+ from coriro.runtime import to_tool_output
181
+
182
+ m = measure("image.png", include_text=True)
183
+
184
+ # Structured measurement — pass to your pipeline
185
+ coriro_data = to_tool_output(m, consolidated=True)
186
+ ```
187
+
188
+ The consolidated format produces:
189
+
190
+ ```json
191
+ {
192
+ "tool": "coriro_color_measurement",
193
+ "measurement": {
194
+ "version": "1.0",
195
+ "scope": "area_dominant_surfaces",
196
+ "coverage": "complete",
197
+ "thresholds": { "min_area_pct": 1.0, "delta_e_collapse": 0.03 },
198
+ "palette_cap": 5,
199
+ "spatial_role": "diagnostic"
200
+ },
201
+ "dominant": { "hex": "#1A1A2E", "oklch": "L0.23/C0.04/H283" },
202
+ "palette": [
203
+ { "hex": "#1A1A2E", "oklch": "L0.23/C0.04/H283", "weight": 0.62 },
204
+ { "hex": "#E8453C", "oklch": "L0.63/C0.20/H28", "weight": 0.23 },
205
+ { "hex": "#F5F5F5", "oklch": "L0.97/C0.00", "weight": 0.15 }
206
+ ]
207
+ }
208
+ ```
209
+
210
+ The `measurement` block tells the receiving system: *this palette is complete above 1% area with ΔE > 0.03 between entries.* Any color not listed is below those thresholds. Omission becomes signal.
211
+
212
+ ### Advanced integration
213
+
214
+ The simplest use is appending Coriro output as text alongside the image in a VLM prompt when building your system prompts. But the schema supports integration at any level of your architecture.
215
+
216
+ Measurements are normalized numerical values (L, C, H in perceptually uniform ranges, weights summing to 1.0) with explicit thresholds. Depending on your pipeline, you can:
217
+
218
+ - Convert palette values into auxiliary feature vectors for multimodal fusion
219
+ - Map measurements through a projection layer into your model's embedding space
220
+ - Inject color features as auxiliary tokens alongside vision embeddings
221
+ - Concatenate structured color vectors with image embeddings before downstream processing
222
+ - Use measurements as conditioning signals for adapters, LoRA modules, or FiLM-style modulation layers
223
+ - Feed data into custom embedding or feature store pipelines
224
+
225
+ Because Coriro exposes explicit perceptual thresholds (ΔE collapse distance, coverage floor), these measurements function as deterministic, reproducible conditioning signals. Not heuristic approximations that vary with prompt phrasing.
226
+
227
+ Coriro does not prescribe how the data is consumed. It provides structured, perceptually grounded color measurements. Whether you integrate them as prompt metadata, auxiliary embeddings, model conditioning, or pipeline features is implementation-specific.
228
+
229
+ For serialization formats and code examples, see the [documentation](https://coriro.org/docs).
230
+
231
+ ---
232
+
233
+ ## Deployment
234
+
235
+ Coriro is a Python library, not an HTTP server. It integrates in two common ways:
236
+
237
+ - **Local Python pipeline (notebooks, scripts, backend workers)** — Import `measure()` in the same Python process that builds model requests, tool payloads, or feature pipelines. Coriro output is available as JSON, XML, or natural language. Pass it however your architecture consumes structured data.
238
+ - **Hosted sidecar API (for JS/Next.js/Vercel frontends)** — Run Coriro behind a small Python service that you host separately. Your frontend sends the image, receives the color measurement payload, and integrates it into your pipeline.
239
+
240
+ This package ships the measurement and serialization library only. If you need an HTTP endpoint, wrap it in any Python web framework. A minimal FastAPI example:
241
+
242
+ ```python
243
+ from fastapi import FastAPI, UploadFile
244
+ from coriro import measure
245
+ import tempfile, os
246
+
247
+ app = FastAPI()
248
+
249
+ @app.post("/measure")
250
+ async def measure_image(file: UploadFile):
251
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
252
+ tmp.write(await file.read())
253
+ tmp.close()
254
+ m = measure(tmp.name)
255
+ os.unlink(tmp.name)
256
+ return m.to_dict()
257
+ ```
258
+
259
+ That's the entire service — `pip install coriro fastapi uvicorn python-multipart`, run with `uvicorn app:app`, and your frontend has a color measurement endpoint.
260
+
261
+ ---
262
+
263
+ ## What it measures
264
+
265
+ ### Core (always on)
266
+
267
+ - **Dominant color** — The single most prominent color, extracted from the weighted palette
268
+ - **Area-ranked global palette** — Colors ordered by pixel area, represented in perceptually uniform OKLCH with implementation-accurate `sample_hex` values from real pixels
269
+ - **ICC profile conversion** — Automatically converts Display P3, Adobe RGB, and other profiled images to sRGB before measurement via `PIL.ImageCms`, ensuring colors match what color pickers show
270
+ - **Color consolidation** — Collapses near-identical colors using perceptual ΔE thresholds in OKLab space, merges black/white families into single representatives, and limits output to a design-friendly palette size
271
+ - **Chroma-aware supplementation** — Two safety-net passes catch perceptually significant colors missed by area-dominant extraction:
272
+ - *Chroma outliers:* High-saturation pixels via z-score (>2.0 std deviations above mean chroma). Catches a yellow CTA button on a low-chroma blue page.
273
+ - *Uncovered colors:* Clusters pixels with ΔE > 0.15 from the nearest palette entry. Catches distinct color groups below the area threshold.
274
+ - **Spatial color distribution** — Fixed grid partitioning (2×2, 3×3, or 4×4) with per-region palettes. Preserves *where* colors appear — not just *which* colors exist
275
+ - **Closed-world measurement metadata** — States the palette's measurement criteria (coverage floor, collapse distance, palette cap). If a color isn't listed, it's below the threshold — omission is signal, not oversight
276
+
277
+ ### Optional passes (independently toggleable, off by default)
278
+
279
+ - **Text foreground colors** — OCR-assisted glyph region detection via Tesseract, extracting foreground colors with background exclusion. Uses original (unsmoothed) pixels for accuracy
280
+ - **Solid accent regions** — Connected-component detection for small but significant solid-color UI elements (CTAs, icons, badges) that fall below area-dominant thresholds. Filters by absolute pixel count, not percentage
281
+ - **CNN-guided pixel stabilization** — Shallow ConvNeXt stem (stem + stage 1 only) for reducing compression artifacts, gradient banding, and anti-aliasing. A stabilizer, not an interpreter — measurement logic remains the authority
282
+
283
+ ---
284
+
285
+ ## How it works
286
+
287
+ Calling `measure()` runs a seven-stage pipeline:
288
+
289
+ 1. **Load & convert** — Reads the image, converts ICC-profiled pixels (Display P3, Adobe RGB) to sRGB via `PIL.ImageCms`. NumPy arrays are accepted directly but must already be sRGB.
290
+
291
+ 2. **Smooth** *(optional)* — CNN pixel stabilization (`smooth=True`). Runs before extraction to reduce compression artifacts, gradient banding, and anti-aliasing before any color analysis.
292
+
293
+ 3. **Color extraction** — Extracts a raw palette using mode-based counting (exact pixel values, default) or k-means clustering (better for photos and gradients). Colors are represented in OKLCH.
294
+
295
+ 4. **Consolidation** — Collapses near-identical colors, merges black/white families, and filters noise. Produces a design-friendly palette at the requested size.
296
+
297
+ 5. **Chroma supplementation** — Two passes recover perceptually significant colors missed by area-dominant extraction (high-saturation outliers, uncovered color clusters). Both filter by novelty against the existing palette to avoid duplicates.
298
+
299
+ 6. **Spatial binning** — Partitions the image into a fixed grid and extracts per-region palettes. Region IDs follow reading order: `R1C1` (top-left) through `R2C2` (bottom-right) for a 2×2 grid.
300
+
301
+ 7. **Optional passes** — Text colors (OCR) and accent regions (connected components). Each is independently toggleable and does not affect the core palette.
302
+
303
+ ---
304
+
305
+ ## Serialization
306
+
307
+ Coriro separates measurement from delivery. The same `ColorMeasurement` can be serialized for different pipeline targets:
308
+
309
+ | Format | Function | Use case |
310
+ |--------|----------|----------|
311
+ | Consolidated JSON | `to_tool_output(m, consolidated=True)` | **Recommended.** Hex + OKLCH + weights in one object |
312
+ | Compact JSON | `to_tool_output(m, compact=True)` | Token-constrained pipelines |
313
+ | Hex-only | `to_tool_output(m, hex_only=True)` | Minimal output. Implementation tasks only |
314
+ | Full JSON | `to_tool_output(m)` | Complete OKLCH data with all metadata |
315
+ | XML block | `to_context_block(m, format=BlockFormat.XML)` | Inline context injection (Claude, Qwen) |
316
+ | Natural language | `to_system_prompt(m)` | Human-readable for system prompts |
317
+ | Markdown | `to_context_block(m, format=BlockFormat.MARKDOWN)` | Markdown-fenced JSON |
318
+
319
+ See the [documentation](https://coriro.org/docs) for more details.
320
+
321
+ ---
322
+
323
+ ## Contributing
324
+
325
+ Coriro is open source under the MIT license. Contributions are welcome.
326
+
327
+ ---
328
+
329
+ ## License
330
+
331
+ MIT License. Copyright © 2026 Saad Irfan. See [LICENSE](LICENSE).
332
+
333
+ ---
334
+
335
+ [coriro.org](https://coriro.org) · [Documentation](https://coriro.org/docs) · [GitHub](https://github.com/coriro-org/coriro)
coriro-1.0.0/README.md ADDED
@@ -0,0 +1,287 @@
1
+ # Coriro
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
4
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/downloads/)
5
+
6
+ Color measurement runtime for vision-language and multimodal inference pipelines.
7
+
8
+ Coriro extracts color data from image pixels — dominant colors, weighted palettes, spatial distribution — and passes them alongside the image into any vision-language or multimodal model pipeline.
9
+
10
+ Core runtime is pure Python. No GPU required. Zero lock-in.
11
+
12
+ ```python
13
+ from coriro import measure
14
+
15
+ m = measure("image.png")
16
+
17
+ m.to_json() # Raw measurement JSON (schema)
18
+ m.to_xml() # XML block for context injection
19
+ m.to_prompt() # Natural language for system prompts
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Why Coriro exists
25
+
26
+ It reads actual image pixels, extracts palettes in perceptually uniform OKLCH, consolidates near-identical colors, catches high-saturation outliers, and maps spatial distribution. The results are output as structured data (JSON, XML, natural language) that any pipeline can consume.
27
+
28
+ The output includes measurement criteria, coverage thresholds, collapse distances, and palette caps, so the receiving model knows exactly what was measured and what was excluded.
29
+
30
+ The most immediate reason to use it: multimodal and vision-language models lose color precision during vision encoding. Patch-based encoders downsample and average pixels, so the language model generates color values that are sometimes close, often wrong. Coriro runs pixel-level colorimetry outside the model and passes the results as plain text, the channel where language models are already precise.
31
+
32
+ But the value extends beyond compensating for encoder limitations. Even a model with perfect color perception does not produce a consolidated, weighted palette in a perceptually uniform color space with spatial distribution and measurement metadata. That is a measurement task. Coriro's output is useful as pipeline data, whether the model's vision is approximate or exact.
33
+
34
+ ```
35
+ ┌──────────────────────────────────────────────────────┐
36
+ │ VLM Pipeline │
37
+ │ │
38
+ │ ┌──────────┐ ┌────────────────────────────────┐ │
39
+ │ │ Image │───▶│ Vision Encoder │ │
40
+ │ └──────────┘ │ Sees layout, structure, UX │ │
41
+ │ │ │ Approximate color │ │
42
+ │ │ └────────────────┬───────────────┘ │
43
+ │ │ │ │
44
+ │ ▼ ▼ │
45
+ │ ┌──────────┐ ┌────────────────────────────────┐ │
46
+ │ │ Coriro │───▶│ Language Model Context │ │
47
+ │ │ measure()│ │ Image + Color sidecar data │ │
48
+ │ └──────────┘ │ = Complete information │ │
49
+ │ Pixel-level └────────────────────────────────┘ │
50
+ │ colorimetry │
51
+ │ (sidecar) │
52
+ └──────────────────────────────────────────────────────┘
53
+ ```
54
+
55
+ **The sidecar principle:** Coriro output runs *alongside* the image, not as a replacement. The model gets the image for layout, hierarchy, and semantics, plus measured color data for precise implementation. Remove Coriro from the pipeline and the model works exactly as before. No weights are modified. No configuration is changed.
56
+
57
+ ---
58
+
59
+ ## Use cases
60
+
61
+ - **VLM color grounding** — Provide measured palettes as structured data alongside the image, compensating for vision-encoder color loss documented in published benchmarks across many models.
62
+ - **Screenshot-to-code** — Provide exact hex values and spatial color distribution so code generation uses measured colors instead of vision-inferred estimates.
63
+ - **Product color metadata** — Attach verifiable weighted palettes to product listings for perceptual color search, cross-SKU consistency checks, and return-reduction workflows.
64
+ - **Design system compliance** — Measure rendered screenshots against design token definitions using perceptual ΔE distance to catch cross-platform drift.
65
+ - **Accessibility contrast auditing** — Compute contrast from rendered pixels in perceptually uniform OKLCH, including text over gradients and background images that DOM-only scanners can miss.
66
+ - **Color-aware asset search** — Index images by weighted palette and spatial distribution for retrieval by color proportion, placement, and perceptual similarity.
67
+
68
+ ---
69
+
70
+ ## Install
71
+
72
+ ```bash
73
+ pip install coriro
74
+ ```
75
+
76
+ Core installation requires only `numpy` and `Pillow`. Optional passes have separate dependencies:
77
+
78
+ | Feature | Install | Requires |
79
+ |---------|---------|----------|
80
+ | Text colors | `pip install coriro[text]` | `pytesseract` + system Tesseract OCR |
81
+ | Accent regions | `pip install coriro[accents]` | `scipy` |
82
+ | CNN smoothing | `pip install coriro[cnn]` | `torch`, `timm` |
83
+ | All features | `pip install coriro[all]` | All of the above |
84
+
85
+ ---
86
+
87
+ ## Quick start
88
+
89
+ ### Measure an image
90
+
91
+ ```python
92
+ from coriro import measure
93
+
94
+ m = measure("image.png")
95
+
96
+ # Dominant color (OKLCH)
97
+ print(m.dominant.hex) # hex from a real pixel in the cluster
98
+ print(m.dominant.L) # Lightness (0.0–1.0)
99
+ print(m.dominant.C) # Chroma (0.0–~0.32)
100
+ print(m.dominant.is_achromatic) # True when C < 0.02
101
+
102
+ # Palette (weighted, most dominant first)
103
+ for wc in m.palette:
104
+ print(f"{wc.color.hex} — {wc.weight:.0%}")
105
+
106
+ # Spatial distribution
107
+ region = m.spatial.get_region("R1C1") # Top-left
108
+ print(region.dominant.hex)
109
+ ```
110
+
111
+ ### Optional passes
112
+
113
+ Three additional passes are available. Each is off by default and independently toggleable:
114
+
115
+ ```python
116
+ m = measure(
117
+ "image.png",
118
+ smooth=True, # CNN pixel stabilization (requires torch + timm)
119
+ include_text=True, # Text foreground colors via OCR (requires pytesseract)
120
+ include_accents=True, # Solid accent region detection (requires scipy)
121
+ )
122
+ ```
123
+
124
+ > **CNN smoothing** stabilizes color surfaces before extraction — reducing noise from compression artifacts, gradient banding, and anti-aliasing. It is off by default because it requires `torch` + `timm` (`pip install coriro[cnn]`). If your environment already includes `torch` + `timm`, consider enabling `smooth=True` for improved measurement quality.
125
+
126
+ > **Latency control:** Coriro processes images at full resolution by default (`max_pixels=0`). For latency-sensitive pipelines, set `max_pixels` to cap the pixel count before processing. Because color measurement is statistical, equivalent palettes are usually preserved at reduced resolution.
127
+
128
+ ### Pipeline integration
129
+
130
+ ```python
131
+ from coriro import measure
132
+ from coriro.runtime import to_tool_output
133
+
134
+ m = measure("image.png", include_text=True)
135
+
136
+ # Structured measurement — pass to your pipeline
137
+ coriro_data = to_tool_output(m, consolidated=True)
138
+ ```
139
+
140
+ The consolidated format produces:
141
+
142
+ ```json
143
+ {
144
+ "tool": "coriro_color_measurement",
145
+ "measurement": {
146
+ "version": "1.0",
147
+ "scope": "area_dominant_surfaces",
148
+ "coverage": "complete",
149
+ "thresholds": { "min_area_pct": 1.0, "delta_e_collapse": 0.03 },
150
+ "palette_cap": 5,
151
+ "spatial_role": "diagnostic"
152
+ },
153
+ "dominant": { "hex": "#1A1A2E", "oklch": "L0.23/C0.04/H283" },
154
+ "palette": [
155
+ { "hex": "#1A1A2E", "oklch": "L0.23/C0.04/H283", "weight": 0.62 },
156
+ { "hex": "#E8453C", "oklch": "L0.63/C0.20/H28", "weight": 0.23 },
157
+ { "hex": "#F5F5F5", "oklch": "L0.97/C0.00", "weight": 0.15 }
158
+ ]
159
+ }
160
+ ```
161
+
162
+ The `measurement` block tells the receiving system: *this palette is complete above 1% area with ΔE > 0.03 between entries.* Any color not listed is below those thresholds. Omission becomes signal.
163
+
164
+ ### Advanced integration
165
+
166
+ The simplest use is appending Coriro output as text alongside the image in a VLM prompt when building your system prompts. But the schema supports integration at any level of your architecture.
167
+
168
+ Measurements are normalized numerical values (L, C, H in perceptually uniform ranges, weights summing to 1.0) with explicit thresholds. Depending on your pipeline, you can:
169
+
170
+ - Convert palette values into auxiliary feature vectors for multimodal fusion
171
+ - Map measurements through a projection layer into your model's embedding space
172
+ - Inject color features as auxiliary tokens alongside vision embeddings
173
+ - Concatenate structured color vectors with image embeddings before downstream processing
174
+ - Use measurements as conditioning signals for adapters, LoRA modules, or FiLM-style modulation layers
175
+ - Feed data into custom embedding or feature store pipelines
176
+
177
+ Because Coriro exposes explicit perceptual thresholds (ΔE collapse distance, coverage floor), these measurements function as deterministic, reproducible conditioning signals. Not heuristic approximations that vary with prompt phrasing.
178
+
179
+ Coriro does not prescribe how the data is consumed. It provides structured, perceptually grounded color measurements. Whether you integrate them as prompt metadata, auxiliary embeddings, model conditioning, or pipeline features is implementation-specific.
180
+
181
+ For serialization formats and code examples, see the [documentation](https://coriro.org/docs).
182
+
183
+ ---
184
+
185
+ ## Deployment
186
+
187
+ Coriro is a Python library, not an HTTP server. It integrates in two common ways:
188
+
189
+ - **Local Python pipeline (notebooks, scripts, backend workers)** — Import `measure()` in the same Python process that builds model requests, tool payloads, or feature pipelines. Coriro output is available as JSON, XML, or natural language. Pass it however your architecture consumes structured data.
190
+ - **Hosted sidecar API (for JS/Next.js/Vercel frontends)** — Run Coriro behind a small Python service that you host separately. Your frontend sends the image, receives the color measurement payload, and integrates it into your pipeline.
191
+
192
+ This package ships the measurement and serialization library only. If you need an HTTP endpoint, wrap it in any Python web framework. A minimal FastAPI example:
193
+
194
+ ```python
195
+ from fastapi import FastAPI, UploadFile
196
+ from coriro import measure
197
+ import tempfile, os
198
+
199
+ app = FastAPI()
200
+
201
+ @app.post("/measure")
202
+ async def measure_image(file: UploadFile):
203
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
204
+ tmp.write(await file.read())
205
+ tmp.close()
206
+ m = measure(tmp.name)
207
+ os.unlink(tmp.name)
208
+ return m.to_dict()
209
+ ```
210
+
211
+ That's the entire service — `pip install coriro fastapi uvicorn python-multipart`, run with `uvicorn app:app`, and your frontend has a color measurement endpoint.
212
+
213
+ ---
214
+
215
+ ## What it measures
216
+
217
+ ### Core (always on)
218
+
219
+ - **Dominant color** — The single most prominent color, extracted from the weighted palette
220
+ - **Area-ranked global palette** — Colors ordered by pixel area, represented in perceptually uniform OKLCH with implementation-accurate `sample_hex` values from real pixels
221
+ - **ICC profile conversion** — Automatically converts Display P3, Adobe RGB, and other profiled images to sRGB before measurement via `PIL.ImageCms`, ensuring colors match what color pickers show
222
+ - **Color consolidation** — Collapses near-identical colors using perceptual ΔE thresholds in OKLab space, merges black/white families into single representatives, and limits output to a design-friendly palette size
223
+ - **Chroma-aware supplementation** — Two safety-net passes catch perceptually significant colors missed by area-dominant extraction:
224
+ - *Chroma outliers:* High-saturation pixels via z-score (>2.0 std deviations above mean chroma). Catches a yellow CTA button on a low-chroma blue page.
225
+ - *Uncovered colors:* Clusters pixels with ΔE > 0.15 from the nearest palette entry. Catches distinct color groups below the area threshold.
226
+ - **Spatial color distribution** — Fixed grid partitioning (2×2, 3×3, or 4×4) with per-region palettes. Preserves *where* colors appear — not just *which* colors exist
227
+ - **Closed-world measurement metadata** — States the palette's measurement criteria (coverage floor, collapse distance, palette cap). If a color isn't listed, it's below the threshold — omission is signal, not oversight
228
+
229
+ ### Optional passes (independently toggleable, off by default)
230
+
231
+ - **Text foreground colors** — OCR-assisted glyph region detection via Tesseract, extracting foreground colors with background exclusion. Uses original (unsmoothed) pixels for accuracy
232
+ - **Solid accent regions** — Connected-component detection for small but significant solid-color UI elements (CTAs, icons, badges) that fall below area-dominant thresholds. Filters by absolute pixel count, not percentage
233
+ - **CNN-guided pixel stabilization** — Shallow ConvNeXt stem (stem + stage 1 only) for reducing compression artifacts, gradient banding, and anti-aliasing. A stabilizer, not an interpreter — measurement logic remains the authority
234
+
235
+ ---
236
+
237
+ ## How it works
238
+
239
+ Calling `measure()` runs a seven-stage pipeline:
240
+
241
+ 1. **Load & convert** — Reads the image, converts ICC-profiled pixels (Display P3, Adobe RGB) to sRGB via `PIL.ImageCms`. NumPy arrays are accepted directly but must already be sRGB.
242
+
243
+ 2. **Smooth** *(optional)* — CNN pixel stabilization (`smooth=True`). Runs before extraction to reduce compression artifacts, gradient banding, and anti-aliasing before any color analysis.
244
+
245
+ 3. **Color extraction** — Extracts a raw palette using mode-based counting (exact pixel values, default) or k-means clustering (better for photos and gradients). Colors are represented in OKLCH.
246
+
247
+ 4. **Consolidation** — Collapses near-identical colors, merges black/white families, and filters noise. Produces a design-friendly palette at the requested size.
248
+
249
+ 5. **Chroma supplementation** — Two passes recover perceptually significant colors missed by area-dominant extraction (high-saturation outliers, uncovered color clusters). Both filter by novelty against the existing palette to avoid duplicates.
250
+
251
+ 6. **Spatial binning** — Partitions the image into a fixed grid and extracts per-region palettes. Region IDs follow reading order: `R1C1` (top-left) through `R2C2` (bottom-right) for a 2×2 grid.
252
+
253
+ 7. **Optional passes** — Text colors (OCR) and accent regions (connected components). Each is independently toggleable and does not affect the core palette.
254
+
255
+ ---
256
+
257
+ ## Serialization
258
+
259
+ Coriro separates measurement from delivery. The same `ColorMeasurement` can be serialized for different pipeline targets:
260
+
261
+ | Format | Function | Use case |
262
+ |--------|----------|----------|
263
+ | Consolidated JSON | `to_tool_output(m, consolidated=True)` | **Recommended.** Hex + OKLCH + weights in one object |
264
+ | Compact JSON | `to_tool_output(m, compact=True)` | Token-constrained pipelines |
265
+ | Hex-only | `to_tool_output(m, hex_only=True)` | Minimal output. Implementation tasks only |
266
+ | Full JSON | `to_tool_output(m)` | Complete OKLCH data with all metadata |
267
+ | XML block | `to_context_block(m, format=BlockFormat.XML)` | Inline context injection (Claude, Qwen) |
268
+ | Natural language | `to_system_prompt(m)` | Human-readable for system prompts |
269
+ | Markdown | `to_context_block(m, format=BlockFormat.MARKDOWN)` | Markdown-fenced JSON |
270
+
271
+ See the [documentation](https://coriro.org/docs) for more details.
272
+
273
+ ---
274
+
275
+ ## Contributing
276
+
277
+ Coriro is open source under the MIT license. Contributions are welcome.
278
+
279
+ ---
280
+
281
+ ## License
282
+
283
+ MIT License. Copyright © 2026 Saad Irfan. See [LICENSE](LICENSE).
284
+
285
+ ---
286
+
287
+ [coriro.org](https://coriro.org) · [Documentation](https://coriro.org/docs) · [GitHub](https://github.com/coriro-org/coriro)