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
6 changes: 3 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Tests

on:
push:
branches: [ main, dev_1.4, dev_1.7, dev_v0.2.0, dev_v0.2.1]
branches: [ main, dev_1.4, dev_1.7, dev_v0.2.0, dev_v0.2.1, dev_v0.2.2 ]
pull_request:
branches: [ main, dev_1.4, dev_1.7, dev_v0.2.0, dev_v0.2.1 ]
branches: [ main, dev_1.4, dev_1.7, dev_v0.2.0, dev_v0.2.1, dev_v0.2.2 ]

jobs:
tests:
Expand All @@ -15,7 +15,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.9", "3.10", "3.11", "3.12"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
# 1) Récupérer le code
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ __pycache__/
*.pyd
*.dll
.pytest_tmp/
tmp/
*venv*/
*.db
*.db*
Expand All @@ -27,6 +28,9 @@ documentation/readthedocs/_build/
# PACKAGE DATA
# -------------------------------

# Dev TODOs
future_todos.md

# Ignore everything inside OUTPUT, but keep the folder itself
src/shinier/data/OUTPUT/*
!src/shinier/data/OUTPUT/.gitkeep
Expand Down
43 changes: 22 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 🌟 SHINIER
# SHINIER
```text
███████╗██╗ ██╗██╗███╗ ██╗██╗███████╗██████╗
██╔════╝██║ ██║██║████╗ ██║██║██╔════╝██╔══██╗
Expand All @@ -15,26 +15,26 @@
[![Tests](https://github.com/Charestlab/shinier/actions/workflows/tests.yml/badge.svg)](https://github.com/Charestlab/shinier/actions/workflows/tests.yml)
---

## 🎯 Overview
## Overview

SHINIER is a modern Python implementation of SHINE (Spectrum, Histogram, and Intensity Normalization and Equalization), originally developed in MATLAB by Willenbockel et al., 2010. It provides precise control over luminance, contrast, histograms, and spectral content across large image sets for well-calibrated visual experiments.

### Key Features and Improvements

- 🎨 **Color Processing** — New modes for color image control with modern color-space standards (Rec.601 / Rec.709 / Rec.2020).
- 🖼️ **Dithering Support** — Reduces quantization artifacts and enhances output image quality.
- **Optimized Performance** — Efficient memory management and faster processing for large image sets (optional Cython/C++ convolution core).
- 🕰 **Legacy Mode** — Ensures full backward compatibility with MATLAB’s original SHINE toolbox.
- 🔢 **High-Precision Arithmetic** — Computations in floating-point precision rather than 8-bit integer space, minimizing rounding errors in multi-stage processing.
- 📦 **Object-Oriented Design** — Modular, extensible architecture with a clean Python API.
- 😀 **User-Friendly CLI** — Guided, prompt-based interface for users who prefer not to write code.
- **Color Processing** — New modes for color image control with modern color-space standards (Rec.601 / Rec.709 / Rec.2020).
- **Dithering Support** — Reduces quantization artifacts and enhances output image quality.
- **Optimized Performance** — Efficient memory management and faster processing for large image sets (optional Cython/C++ convolution core).
- **Legacy Mode** — Ensures full backward compatibility with MATLAB’s original SHINE toolbox.
- **High-Precision Arithmetic** — Computations in floating-point precision rather than 8-bit integer space, minimizing rounding errors in multi-stage processing.
- **Object-Oriented Design** — Modular, extensible architecture with a clean Python API.
- **User-Friendly CLI** — Guided, prompt-based interface for users who prefer not to write code.

For detailed technical documentation (algorithms, numerical choices, and MATLAB vs Python behavior), see
[`documentation/documentation.md`](documentation/documentation.md).

---

## 🚀 Quick Start
## Quick Start

### Installation

Expand Down Expand Up @@ -68,7 +68,7 @@ print("shinier version:", getattr(shinier, "__version__", "unknown"))
```


### 😀 **User-friendly Interface**
### User-friendly Interface
Call the following bash command to quickly start using the interactive CLI.
```bash
shinier --show_results --image_index=1
Expand All @@ -77,7 +77,7 @@ shinier --show_results --image_index=1
<img src="https://raw.githubusercontent.com/Charestlab/shinier/main/assets/DEMO_INTERACTIVE_CLI.gif" width="1000" alt="CLI demo">
</p>

### 🧩 Example in Python
### Example in Python
Run the following python code to make sure the package is running properly.
```python
from shinier import Options, ImageDataset, ImageProcessor, utils
Expand All @@ -102,15 +102,15 @@ Change the mode number (e.g. `opt = Options(mode=3)`) to change image processing
| 6 | `hist_match → spec_match` | Histogram, then spectrum |
| 7 | `sf_match → hist_match` | Spatial frequency, then histogram |
| 8 | `spec_match → hist_match` (default) | Spectrum, then histogram (recommended) |
| 9 | `dithering` | Dithering only |
| 9 | `ie_methods` or `dithering` | Standalone per-image transform (histogram-derived enhancement or dithering) |

Below is an example of results obtained using mode 5 with joint histogram equalization and spatial frequency normalization.
<p>
<img src="https://raw.githubusercontent.com/Charestlab/shinier/main/assets/demo_cli_mode5.png" width="600" alt="CLI demo">
</p>

---
## 🏛️ **Technical information**
## **Technical information**

Documentation:
1. [Package Overview](documentation/documentation.md#overview)
Expand All @@ -128,29 +128,30 @@ Documentation:
13. [Additional Resources](documentation/documentation.md#additional-resources)

---
## 📚 Citing
## Citing
If you use **SHINIER**, please cite both of these articles:

### References
- Salvas-Hébert, M., Dupuis-Roy, N., Landry, C., Charest, I., & Gosselin, F. (2026). *SHINIER: An Open-Source Python Package for Controlling Low-Level Image Properties*
- Willenbockel, V., Sadr, J., Fiset, D., Horne, G. O., Gosselin, F., & Tanaka, J. W. (2010). Controlling low-level image properties: The SHINE toolbox. *Behavior Research Methods, 42*(3), 671–684. https://doi.org/10.3758/BRM.42.3.671
- Salvas-Hébert, M., Dupuis-Roy, N., Landry, C., Charest, I., & Gosselin, F. (2026). SHINIER: An open-source Python package for controlling low-level image properties. *SoftwareX*, *35*, Article 102884. https://doi.org/10.1016/j.softx.2026.102884
- Willenbockel, V., Sadr, J., Fiset, D., Horne, G. O., Gosselin, F., & Tanaka, J. W. (2010). Controlling low-level image properties: The SHINE toolbox. *Behavior Research Methods*, *42*(3), 671–684. https://doi.org/10.3758/BRM.42.3.671

---
## 🤝 Contributing
## Contributing
See [CONTRIBUTING.md](documentation/contributing.md) for guidelines (coding standards, tests, docs, and PR flow).

---
## 📄 License
## License
See [LICENSE](LICENSE) for more information.

---
## 🛠️ Troubleshooting
## Troubleshooting
- No compiler available: install a C/C++ toolchain or proceed with the NumPy fallback (slower).
- Compiled extension fails to load (e.g., NumPy too old / ABI mismatch): SHINIER warns and uses the NumPy fallback; upgrade NumPy (and pip/setuptools/wheel) and reinstall to restore the compiled core.
- Import errors after upgrade: try pip install --upgrade pip setuptools wheel and reinstall.
- Windows build issues: ensure MSVC Build Tools are installed and on PATH.

---
<p align="center">
<strong>Code developed by Nicolas Dupuis-Roy and Mathias Salvas-Hébert </strong><br>
<em>Version 0.2.0 - Complete technical documentation</em>
<em>Version 0.2.2 - Complete technical documentation</em>
</p>
4 changes: 2 additions & 2 deletions documentation/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ By participating, you agree to uphold a standard of professional, inclusive, and

## Development Setup

> **Python:** >=3.9, <3.13
> **Python:** >=3.9, <3.15
> **OS:** macOS / Linux / Windows
> **Optional:** C/C++ toolchain for the Cython-compiled `_cconvolve` extension (speeds up convolution)

Expand Down Expand Up @@ -238,6 +238,6 @@ pytest -m unit_tests
# Validation Tests (VERY LONG AND SLOW) and markers
pytest -q -m validation_tests
```
#### 🏛️ Test README
#### Test README
For complete testing procedures (markers, sharding, replay/debug workflow), see the dedicated
Testing Guide in `tests/README.md`.
30 changes: 15 additions & 15 deletions documentation/demos.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ MASKS_FOLDER = "path"

---

### Mode 1 – `lum_match`
#### Mode 1 – `lum_match`

```python
"""
Expand Down Expand Up @@ -168,7 +168,7 @@ opts = Options(

---

### Mode 2 – `hist_match`
#### Mode 2 – `hist_match`

```python
"""
Expand All @@ -193,7 +193,7 @@ opts = Options(

---

### Mode 3 – `sf_match`
#### Mode 3 – `sf_match`

```python
"""
Expand All @@ -216,7 +216,7 @@ opts = Options(

---

### Mode 4 – `spec_match`
#### Mode 4 – `spec_match`

```python
"""
Expand All @@ -240,7 +240,7 @@ opts = Options(

---

### Mode 5 – `hist_match` → `sf_match`
#### Mode 5 – `hist_match` → `sf_match`

```python
"""
Expand All @@ -249,7 +249,7 @@ Mode 5 (hist_match & sf_match): histogram matching followed by rotational

Example use case: Histogram specification with noise is applied (legacy method),
then rotational Fourier spectra are aligned. No rescaling is performed
afterwards,to preserve the luminance distribution imposed by histogram
afterwards, to preserve the luminance distribution imposed by histogram
matching.
"""
opts = Options(
Expand All @@ -264,7 +264,7 @@ opts = Options(

---

### Mode 6 – `hist_match` → `spec_match`
#### Mode 6 – `hist_match` → `spec_match`

```python
"""
Expand All @@ -285,7 +285,7 @@ opts = Options(

---

### Mode 7 – `sf_match` → `hist_match`
#### Mode 7 – `sf_match` → `hist_match`

```python
"""
Expand All @@ -308,7 +308,7 @@ opts = Options(

---

### Mode 8 – `spec_match` → `hist_match`
#### Mode 8 – `spec_match` → `hist_match`

```python
"""
Expand All @@ -332,11 +332,11 @@ opts = Options(

---

### Mode 9 – Standalone Per-Image Transform
#### Mode 9 – Standalone Per-Image Transform

Mode 9 applies a standalone transform to each image independently — no inter-image target is computed. The operation is selected via `standalone_op`.

#### 9a Image Enhancement (`standalone_op="ie_methods"`)
**9a Image enhancement** (`standalone_op="ie_methods"`)

```python
"""
Expand All @@ -355,7 +355,7 @@ opts = Options(
)
```

#### 9b Dithering only (`standalone_op="dithering"`)
**9b Dithering only** (`standalone_op="dithering"`)

```python
"""
Expand All @@ -373,15 +373,15 @@ opts = Options(

---

### Example 10 – Mode 2 + extra parameters
#### Example 10 – Mode 2 + extra parameters

```python
"""
Example 10 (mode 2 + non-mode-specific parameters): to show the other parameters.

Example use case: hist_matching using Coltuc, Bolon & Chassery (2006) exact
histogram specification. Target histogram will be the average from all the
images (default), no SSIM optimization (Avanki, 2009).
images (default), no SSIM optimization (Avanaki, 2009).

The masks loaded from masks_folder are used for figure-ground separation
(whole_image = 3). The images will be transformed to grayscale (1 channel),
Expand All @@ -405,7 +405,7 @@ opts = Options(

---

### Example 11 – Preserve colors with `constrain_image_chrominance`
#### Example 11 – Preserve colors with `constrain_image_chrominance`

```python
"""
Expand Down
Loading