Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ venv.bak/

# macOS
.DS_Store
**/.DS_Store

# Windows
Thumbs.db
Expand All @@ -92,6 +93,9 @@ softwareX/
# Local roadmap
/ROADMAP.md

# Commit plan (local development only)
/COMMIT_PLAN.md

# Kilo local configuration
/kilo.json
# Image files (test output, not committed)
Expand All @@ -107,3 +111,7 @@ softwareX/
# README reference images are tracked
!/imgs/
!/imgs/**

# GUI assets are tracked
!/colorcast/assets/
!/colorcast/assets/**
2 changes: 1 addition & 1 deletion .zenodo.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@
],
"references": [],
"communities": [],
"version": "2.5.0",
"version": "2.6.0",
"language": "eng"
}
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Changelog

## [2.6.0] – 2026-08-05

### Added

- Perceptually uniform appearance-space abstraction in `colorcast/analysis/appearance.py` with two backends: **CIELAB** (legacy, D65) and **ICtCp** (ITU-R BT.2100, HDR-aware, ΔE scaled ×720). The `AppearanceSpace` protocol exposes `from_rgb(rgb)` and `delta_E(a, b)` methods.
- `ErrorMap` carries two new optional fields — `appearance_delta` (per-pixel ΔE) and `appearance_delta_name` (space identifier) — populated when an `AppearanceSpace` is passed to `get_error_map()`.
- `get_error_map()` accepts an optional keyword `appearance=` parameter for computing appearance-based ΔE alongside the existing Euclidean and CIEDE2000 metrics.
- `compute_dashboard()` accepts an `appearance=` keyword and ranks deficiencies by the appearance metric when available; `_summarize` prefers `appearance_delta` over `chroma_error_dE00` over `chroma_error`.
- New CLI subcommand: `colorcast dashboard IMAGE [-o OUTPUT] [--appearance {cielab,ictcp}]` generates a full-resolution CVD accessibility report.
- GUI Dashboard dialog gained a "ΔE metric" dropdown (CIELAB / ICtCp) in the bottom button bar; changing it re-triggers computation.
- `make_appearance_space(name)` factory function centralizes appearance-space creation by name; CLI and GUI now route through it, eliminating duplicated selection logic and brittle combobox-index coupling.
- `ErrorMap.preferred_metric()` method encapsulates the metric-priority rule (appearance_delta > chroma_error_dE00 > chroma_error), so dashboard summaries no longer encode this domain judgment directly.
- Consolidated `srgb_to_linear` gamma-decoding utility into `colorcast/utils/color_utils.py`, removing duplicate implementations from `appearance.py` and `simulation.py`.
- `AppearanceSpace` ABC docstring now documents thread-safety requirements for concurrent `delta_E` calls on shared instances; JND threshold comment restored in `dashboard.py:136` explaining why 1.0 ΔE ≈ one just-noticeable-difference for both CIEDE2000 and ΔE_ITP.
- Report filename and title now include the ΔE metric label (e.g. ``dashboard_report_ictcp.png``, ``"CVD Accessibility Dashboard – ICtCp"``), derived from ``DashboardResult.metric_label``.

### Changed

- `CIELABSpace` default metric changed from `cie76` to `ciede2000` for better perceptual uniformity.
- Replaced the QDarkStyle dependency with a custom QSS stylesheet and redesigned the GUI with a dark canvas (#171717), white pill buttons, hairline borders (#3d3d3d), and no drop shadows.
- README test metrics updated: 378 passing, 1 skipped (up from 361 in v2.5.0).

## [2.5.0] – 2026-08-02

### Added
Expand Down
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ cff-version: 1.2.0
message: "If you use this software, please cite it as below."
type: software
title: "ColorCast: Color Transfer Toolkit for Python"
version: 2.5.0
date-released: 2026-08-02
version: 2.6.0
date-released: 2026-08-05
abstract: "ColorCast is a Python toolkit for color and style transfer between images. Its accessibility pipeline simulates protanopia, deuteranopia, and tritanopia using Vienot (1999) and Brettel (1997) linear-RGB matrices; the Daltonization module re-encodes lost chromatic information into surviving perceptual channels, making color differences visible to dichromatic observers. It also provides histogram matching, mean/std transfer, LUT-based curves, and selective regional transfer via CLI, GUI, and library API."
authors:
- family-names: Semoglou
Expand Down
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ recursive-include tests *.py
# Include reference images (for README screenshots)
graft imgs

# Include GUI assets (combo box chevron)
recursive-include colorcast/assets *.png

# Exclude unnecessary files
global-exclude __pycache__
global-exclude *.py[cod]
Expand Down
44 changes: 38 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
- **Selective Regional Transfer** - Target shadows, midtones, or highlights specifically
- **Modular Package** - Use as Python API or run GUI/CLI
- **Performance Optimized** - LRU caching and batch processing support
- **Tested** - 75% test coverage with 361 passing tests
- **Documented** - API documentation and examples
- **Plugin Architecture** - Easy to add custom transfer methods
- **Tested** – 75% test coverage with 378 passing tests
- **Documented** – API documentation and examples
- **Perceptually Uniform ΔE Metrics** – CIELAB (defaults to CIEDE2000) and ICtCp (HDR, BT.2100) appearance spaces for ranking CVD deficiency severity
- **Plugin Architecture** – Add custom transfer methods through plugins
- **Minimal Dark Theme** – Dark interface with a neutral canvas, bordered cards, and white action buttons

### Transfer Methods

Expand Down Expand Up @@ -51,7 +53,7 @@ _CVD Accessibility Dashboard: side-by-side simulations and error maps for all th

_Method comparison: ranked results across multiple metrics and transfer algorithms_

![Dashboard Report](imgs/dashboard_report.png)
![Dashboard Report](imgs/dashboard_report_ICtCp.png)

_Full dashboard report summarizing simulation results and Daltonization efficacy_

Expand Down Expand Up @@ -166,6 +168,12 @@ Get package information:
colorcast info --version
```

Generate a CVD accessibility dashboard report:

```bash
colorcast dashboard image.jpg -o report.png --appearance ictcp
```

**CLI Options:**

- `-m, --method`: Transfer method (histogram, meanstd, lab_reinhard, lut_linear, lut_scurve, lut_contrast, selective_shadows, selective_midtones, selective_highlights, simulate_protanopia, simulate_deuteranopia, simulate_tritanopia, daltonize_protanopia, daltonize_deuteranopia, daltonize_tritanopia)
Expand Down Expand Up @@ -239,6 +247,30 @@ method = registry.get_method("histogram")
result = method.transfer(content, style)
```

#### Using Appearance Spaces for Perceptually Uniform Metrics

```python
from colorcast.analysis import make_appearance_space, get_error_map
from colorcast.processing.simulation import ColorBlindSimulator
from colorcast import load_image

# Load image and simulate color-vision deficiency
image = load_image("content.jpg")
simulator = ColorBlindSimulator()
simulated = simulator.transform_color_space(image, "deuteranopia")

# Create appearance space by name (CIELAB defaults to CIEDE2000)
cielab = make_appearance_space("cielab")
ictcp = make_appearance_space("ictcp") # HDR-aware BT.2100

# Compute error map with appearance-based ΔE
error_map = get_error_map(image, simulated, appearance=ictcp)

# Access the appearance delta
print(f"Space: {error_map.appearance_delta_name}")
print(f"Mean ΔE: {error_map.appearance_delta.mean():.2f}")
```

#### Batch Processing

```python
Expand Down Expand Up @@ -330,7 +362,7 @@ pytest tests/test_transfer_methods.py
pytest -v
```

**Test Coverage:** 75% (361 passing, 1 skipped)
**Test Coverage:** 75% (378 passing, 1 skipped)

- Core transfer methods: 100% coverage
- Full test suite including integration, performance, and property-based tests
Expand Down Expand Up @@ -440,7 +472,7 @@ If you use ColorCast in academic work, please cite it as:
title = {ColorCast: Color Transfer Toolkit for Python},
doi = {10.5281/zenodo.18550038},
url = {https://github.com/MichailSemoglou/ColorCast},
version = {2.5.0},
version = {2.6.0},
year = {2026},
}
```
Expand Down
60 changes: 60 additions & 0 deletions colorcast/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,36 @@ def parse_args() -> argparse.Namespace:
help="Show version information",
)

# Dashboard command
dashboard_parser = subparsers.add_parser(
"dashboard",
help="Generate a CVD accessibility dashboard report",
)
dashboard_parser.add_argument(
"--verbose",
action="store_true",
default=argparse.SUPPRESS,
help="Show traceback on errors",
)
dashboard_parser.add_argument(
"image",
type=Path,
help="Path to the image to analyze",
)
dashboard_parser.add_argument(
"-o",
"--output",
type=Path,
default=Path("dashboard_report.png"),
help="Output path for the dashboard report (default: dashboard_report.png)",
)
dashboard_parser.add_argument(
"--appearance",
choices=["cielab", "ictcp"],
default="cielab",
help="Appearance space for ΔE ranking (default: cielab)",
)

return parser.parse_args()


Expand Down Expand Up @@ -332,6 +362,34 @@ def cmd_info(args: argparse.Namespace) -> None:
print("Visit https://github.com/MichailSemoglou/ColorCast for more info.")


def cmd_dashboard(args: argparse.Namespace) -> None:
"""Handle dashboard command."""
from colorcast.analysis.appearance import make_appearance_space
from colorcast.analysis.dashboard import compute_dashboard, generate_dashboard_report

image = load_image(str(args.image))
appearance = make_appearance_space(args.appearance)

print(f"Computing CVD dashboard (appearance space: {appearance.name})…")
result = compute_dashboard(image, appearance=appearance)

print("Summary:")
for deficiency in ("protanopia", "deuteranopia", "tritanopia"):
stats = result.summary[deficiency]
print(
f" {deficiency:14} mean={stats['mean_error']:7.2f} "
f"median={stats['median_error']:7.2f} "
f"p95={stats['p95_error']:7.2f} "
f"affected={stats['percent_affected']:5.1f}%"
)

metric_label = result.metric_label
title = f"CVD Accessibility Dashboard – {metric_label}"
print(f"\nGenerating report: {args.output}")
generate_dashboard_report(result, str(args.output), title=title)
print(f"Dashboard saved to {args.output}")


def main() -> None:
"""Main entry point for CLI."""
args = parse_args()
Expand All @@ -350,6 +408,8 @@ def main() -> None:
cmd_list_methods()
elif args.command == "info":
cmd_info(args)
elif args.command == "dashboard":
cmd_dashboard(args)
else:
print(f"Error: Unknown command '{args.command}'", file=sys.stderr)
sys.exit(1)
Expand Down
2 changes: 1 addition & 1 deletion colorcast/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
copy; update all of them in sync when cutting a release.
"""

__version__ = "2.5.0"
__version__ = "2.6.0"
13 changes: 13 additions & 0 deletions colorcast/analysis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@
- Error map / difference analysis for colour-blindness simulation
- Daltonization: re-encoding lost chromatic information
- CVD accessibility dashboard: compare deficiencies at once
- Perceptually uniform appearance spaces (ICtCp, CIELAB)
"""

from colorcast.analysis.appearance import (
AppearanceDelta,
AppearanceSpace,
CIELABSpace,
ICtCpSpace,
make_appearance_space,
)
from colorcast.analysis.comparison import MethodComparison
from colorcast.analysis.daltonization import (
apply_daltonization,
Expand All @@ -33,6 +41,11 @@
)

__all__ = [
"AppearanceSpace",
"AppearanceDelta",
"CIELABSpace",
"ICtCpSpace",
"make_appearance_space",
"MethodComparison",
"visualize_transfer_result",
"visualize_method_comparison",
Expand Down
Loading