API Reference๏
This page is generated from the Python docstrings. The public narrative
documentation remains in the Markdown files under documentation/.
Core Classes๏
- class shinier.Options๏
Bases:
InformativeBaseModelClass to hold SHINIER processing options.
Subsections๏
- INPUT/OUTPUT images folders
input_folder,output_folder
- MASKS and FIGURE-GROUND separation
masks_folder,whole_image,background
- SHINIER MODE
mode,legacy_mode,seed,iterations
- Grayscale / color
as_gray,linear_luminance,rec_standard,gamut_strategy
- Dithering / Memory
dithering,conserve_memory
- LUMINANCE matching
safe_lum_match,target_lum
- HISTOGRAM matching
hist_optim,hist_specification,hist_iterations,target_hist
- FOURIER matching
rescaling,target_spectrum,fft_padding_mode,fft_padding_value
- Misc
verbose
Options๏
- input_folderUnion[str, Path]
[1] INPUT/OUTPUT images folders.
Relative or absolute path of the image folder. Default is the package sample INPUT folder.
- output_folderUnion[str, Path]
[1] INPUT/OUTPUT images folders.
Relative or absolute path where processed images will be saved. Default is the package sample OUTPUT folder.
- masks_folderOptional[Union[str, Path]]
[2] MASKS and FIGURE-GROUND separation.
Relative or absolute path of mask folder. Default is None.
- whole_imageLiteral[1, 2, 3]
[2] MASKS and FIGURE-GROUND separation.
Binary region-of-interest (ROI) masks: analysis runs on selected pixels. Default is 1.
1 = No ROI mask: whole images will be analyzed.
2 = ROI masks: analysis run on pixels !=
backgroundpixel value.3 = ROI masks: masks loaded from the MASK folder and analysis run on pixels >= 127.
- backgroundUnion[int, float]
[2] MASKS and FIGURE-GROUND separation.
Background grayscale intensity of mask, or 300 = automatic. Default is 300.
By default (300), the most frequent luminance intensity in the image is used as the background value; i.e., all regions of that luminance intensity are treated as background.
- modeLiteral[1, 2, 3, 4, 5, 6, 7, 8, 9]
[3] SHINIER MODE.
Image processing treatment. Default is 2.
1 = lum_match only.
2 = hist_match only (default).
3 = sf_match only.
4 = spec_match only.
5 = hist_match and sf_match.
6 = hist_match and spec_match.
7 = sf_match and hist_match.
8 = spec_match and hist_match.
9 = only dithering.
Related methods in
shinier.ImageProcessor:Luminance matching:
shinier.ImageProcessor.lum_match()Histogram matching:
shinier.ImageProcessor.hist_match()Spatial-frequency matching:
shinier.ImageProcessor.sf_match()Spectrum matching:
shinier.ImageProcessor.spec_match()Full pipeline orchestration:
shinier.ImageProcessor.process()
- legacy_modeOptional[bool]
[3] SHINIER MODE.
Enables backward compatibility with older versions while retaining recent optimizations. Default is False.
Important: legacy_mode affects more than the explicit option overrides listed below. It also enables MATLAB-compatibility behavior in several processing steps (for example MATLAB-style rounding and grayscale conversion paths), so outputs may differ even when two runs appear to share the same visible option values.
True reproduces the behavior of previous releases by setting:
conserve_memory=Falseas_gray=1dithering=0hist_specification=1safe_lum_match=False
False means no legacy settings are forced and all options follow their current defaults.
- seedOptional[int]
[3] SHINIER MODE.
Seed to initialize the PRNG. Default is None.
Used for the Noisy bit dithering and
hist_specification(withhybridornoisetie-breaking strategies). IfNone,int(time.time())will be used.- iterationsint
[3] SHINIER MODE.
Number of iterations for composite modes. Default is 5.
For these modes, histogram specification and Fourier amplitude specification affect each other. Multiple iterations allow a high degree of joint matching.
This method of iterating was developed so that it recalculates the respective target at each iteration (i.e., no target hist/spectrum).
- as_graybool
[4] Grayscale / color.
Conversion into grayscale images. Default is 0 (False).
True = Convert into grayscale images.
- When
linear_luminanceisFalse: computes non-linear grayscale images by applying the perceptual luma weights from the specified
rec_standard.
- When
- When
linear_luminanceisTrue: computes linear grayscale images by averaging the RGB channels (simple mean(RGB)).
- When
False = No conversion applied.
- linear_luminancebool
[4] Grayscale / color.
Are pixel values linearly related to luminance? Default is False.
True: no conversion mode.
Assumes input images are linear RGB or grayscale.
All transformations are applied independently to each channel.
No color-space conversion is performed.
False: conversion to CIE xyY (recommended and default).
Assumes input images are gamma-encoded (e.g., sRGB).
- Images are converted to the CIE xyY color space:
sRGB -> linRGB -> XYZ -> xyY
- Transformations are applied only to the luminance channel (Y),
while chromatic channels (x, y) remain unchanged.
- The modified image is then reconstructed via:
xyY -> XYZ -> linRGB -> sRGB
- This mode preserves color gamuts and is highly recommended for operations
on linear-to-luminance values like fourier matching and luminance matching.
- rec_standardLiteral[1, 2, 3]
[4] Grayscale / color.
Specifies the Rec. color standard used for RGB <-> XYZ conversion. Default is 2.
1 = Rec.601 (SDTV, legacy systems).
2 = Rec.709 (HDTV, sRGB default). SHINIER assumes display-referred Rec. 709 with sRGB-like transfer.
3 = Rec.2020 (UHDTV, wide-gamut HDR).
- gamut_strategyLiteral[โconstrain_dataset_luminanceโ, โconstrain_dataset_chrominanceโ, โconstrain_image_chrominanceโ, โconstrain_image_luminanceโ, โclipโ]
[4] Grayscale / color.
Strategy to deal with out-of-gamut problem. Requires
linear_luminance=False. Default isconstrain_image_chrominance.Global constraints (applies the same transform to the whole dataset): best for dataset consistency.
constrain_dataset_luminance: Scales the luminance of ALL images down so the most saturated pixel fits.Preserves hue and saturation; compresses contrast/luminance.
constrain_dataset_chrominance: Scales the saturation of ALL images down so the brightest pixel fits.Preserves contrast/luminance; compresses saturation.
Local repairs (applies a single transform to all pixels of a given image): best to maximize image contrast.
constrain_image_luminance: Scales down the luminance of all pixels of an image so the most saturated pixel fits.Preserves hue and saturation; compresses contrast/luminance.
constrain_image_chrominance: Scales down the saturation of all pixels of an image so the brightest pixel fits.Preserves contrast/luminance; compresses saturation.
clip: Default color conversion behavior, numpysafe_mode(not recommended unless you know what are you doing).
- ditheringLiteral[0, 1, 2]
[5] Dithering / Memory.
Dithering applied before final conversion to uint8 to mitigate precision loss from float64 processing and perceptually extend effective bit depth.
Default is 0.
0 = No dithering.
1 = Noisy bit dithering (Allard R. and Faubert J., 2008).
2 = Floyd-Steinberg dithering (Floyd R.W. and Steinberg L., 1976).
- conserve_memoryOptional[bool]
[5] Dithering / Memory.
Controls how images are loaded and stored in memory during processing. Default is True.
True minimizes memory usage by keeping only one image in memory at a time and using a temporary directory to save the images. If the
input_datais a list of NumPy arrays, images are first saved as.npyin a temporary directory, and they are loaded in memory one at a time upon request.False increases memory usage substantially by loading all images into memory at once, but may improve processing speed.
- safe_lum_matchbool
[6] LUMINANCE matching.
Adjusting the mean and standard deviation to keep all luminance values [0, 255]. Default is True.
True = No values will be clipped, but the resulting targets may differ from the requested values. False = Values will be clipped, but the resulting targets will stay the same.
- target_lumTuple[Optional[float], Optional[float]]
[6] LUMINANCE matching.
Target luminance statistics as
(mean, std)for luminance matching. Default is(0, 0).Constraints:
meanmust be in[0, 255]orNone.stdmust be>= 0orNone.
Semantics:
0uses a dataset-level automatic target for that statistic.Nonepreserves the original per-image statistic when possible.
With
safe_lum_match=True, the resolved targets may be adjusted to avoid out-of-range luminance values. Explicit targets are preserved when possible, while automatic or preserved targets (i.e.0orNone) are relaxed first.- hist_optimbool
[7] HISTOGRAM matching.
Optimization of the histogram-matched images with structural similarity index measure (Avanaki, 2009). Default is False.
True = SSIM optimization (Avanaki, 2009).
- Following Avanakiโs experimental results, no tie-breaking strategy is applied when
optimizing SSIM except for the very last iteration where the hybrid strategy is used (see
hist_specification).
- To change the number of iterations (default = 5) and adjust step size
(default = 35), see below.
False = No SSIM optimization.
- hist_specificationLiteral[1, 2, 3, 4]
[7] HISTOGRAM matching.
Determines the algorithm used to break ties (isoluminance) when matching the histogram. Default is 4.
- 1 =
Noise: Exact specification with noise (legacy code). Add small uniform noise to break ties (fast; non-deterministic unless seed set).
- 1 =
- 2 =
Moving-average: Coltuc Bolon and Chassery (2006) tie-breaking strategy with moving-average filters. Kernels defined in the paper sorted lexicographically for deterministic local ordering.
- 2 =
- 3 =
Gaussian: Coltuc tie-breaking strategy with gaussian filters. Adaptive amount of gaussian filters used (min 5, max 7; deterministic local ordering).
- 3 =
- 4 =
Hybrid: Coltuc tie-breaking strategy with gaussian filters and noise fallback if isoluminant pixels persist. Gaussian(deterministic) +Noise fallback(stochastic, if needed), best compromise.
- 4 =
Set to
Noneifhist_optimisTrue. Seehist_optimfor more info.- hist_iterationsint
[7] HISTOGRAM matching.
Number of iterations for SSIM optimization in hist_optim. Default is 10.
- target_histOptional[Union[np.ndarray, Path, Literal[โequalโ]]]
[7] HISTOGRAM matching.
Target histogram, image path,
'equal', orNone. Default isNone.Accepted inputs:
np.ndarray(histogram array):- Can contain histogram counts (int) or weights (float).
Histograms are normalized internally before use.
- Shape must be
(256,)or(256, 1)for single-channel processing, or
(256, C)for multi-channel processing (e.g., RGB withlinear_luminance=True,as_gray=False).
- Shape must be
See
imhistinutils.pyto compute a target histogram from an image.
Path(input image file):Image is processed using the same pipeline as the dataset to compute the target histogram.
Spatial dimensions must match the processed images.
'equal':Uses a flat histogram, i.e., histogram equalization.
None:Uses the average histogram of all input images.
Used in all modes involving histogram matching (modes 2, 5, 6, 7, and 8).
- rescalingLiteral[0, 1, 2, 3]
[8] FOURIER matching.
Post-processing applied after sf_match or spec_match only. Default is 2.
0 = no rescaling.
1 = Rescaling each image so that it stretches to [0, 1] (its own min -> 0, max -> 1).
2 = Rescaling absolute max/min (shared 0-1 range).
3 = Rescaling average max/min.
Not allowed for modes 1 and 2.
- target_spectrumOptional[Union[np.ndarray, Path]]
[8] FOURIER matching.
Target Fourier magnitude spectrum, image path, or
None. Default isNone.Accepted inputs:
np.ndarray(magnitude spectrum array, dtype must be float):- Spatial shape must match the processed images:
(H, W), or(H, W, C) for multi-channel processing (e.g., RGB with
linear_luminance=True,as_gray=False).
- Spatial shape must match the processed images:
See
image_spectruminutils.pyto compute a target spectrum from an image.
Path(input image file):Image is processed using the same pipeline as the dataset to compute the target spectrum.
Spatial dimensions must match the processed images.
None:Uses the average spectrum of all input images.
Used in all modes involving Fourier matching (modes 3, 4, 5, 6, 7, and 8).
- fft_padding_modeLiteral[0, 1, 2, 3]
[8] FOURIER matching.
Optional spatial padding before FFT computation. Default is 0.
0 = No padding (disabled).
1 =
Reflect: mirror image values without repeating the edge pixel.2 =
Symmetric: mirror image values including the edge pixel.3 =
Constant: pad with a constant spatial-domain intensity.
Padding is applied before FFT and cropped after inverse FFT reconstruction.
- fft_padding_valueUnion[int, Literal[300]]
[8] FOURIER matching.
Constant padding intensity in [0, 255] when
fft_padding_mode=3.300means: use the mean intensity of the current normalized image. Default is300.If
300, the mean intensity of the current normalized image is used. Used only whenfft_padding_mode=3.- verboseLiteral[-1, 0, 1, 2, 3], optional
[9] Misc.
Controls verbosity levels. Default is 0.
-1 = Quiet mode.
0 = Progress bar with ETA.
1 = Basic progress steps (no progress bar).
2 = Additional info about image and channels being processed are printed (no progress bar).
3 = Debug mode for developers (no progress bar).
- class shinier.ImageDataset๏
Bases:
InformativeBaseModelClass to load and manage a collection of images and masks, keeping track of their state throughout image processing.
Only
images,masks, andoptionsare expected as participant/user inputs. For normal usage, onlyoptionsshould be set by the user.- Parameters:
images (ImageListType) โ List of images. If not provided, images are loaded from
input_folderas defined inOptions.masks (ImageListType) โ List of masks, each specifying the image regions to include in processing. If not provided, masks are loaded from
masks_folderas defined inOptions.options (Optional[Options]) โ Instance of the
Optionsclass. If not provided,Optionsis instantiated with default values.
Runtime Attributes
images(ImageListType): Collection of images.masks(ImageListType): Collection of masks.n_images(int): Number of images.n_masks(int): Number of masks.images_name(List[str]): List of image file names.masks_name(List[str]): List of mask file names.processing_logs(List[str]): Processing steps applied to the dataset with related metrics and info.options(Options): Configuration options for the dataset.magnitudes(Optional[ImageListIO]): Buffer of Fourier magnitudes used in Fourier-based modes.phases(Optional[ImageListIO]): Buffer of Fourier phases used in Fourier-based modes.buffer(Optional[ImageListIO]): Main working buffer used during processing.buffer_other(Optional[ImageListIO]): Secondary working buffer (e.g., chromatic channels when applicable).
- close()๏
Release all loaded image and buffer resources.
- initialize_dataset()๏
Perform dataset construction and validation after field initialization.
- post_init(_ImageDataset__context)๏
Called automatically after Pydantic validation.
- print_log()๏
Record processing steps to a timestamped log file.
- save_images()๏
Save processed images to disk.
- class shinier.ImageProcessor๏
Bases:
InformativeBaseModelProcess an image dataset with configurable SHINIER operations.
This class is designed to process a dataset of images by applying the following configurable transformations: luminance matching, histogram matching, spatial frequency matching, and Fourier spectrum matching. It is highly customizable and includes options like verbosity levels, random seed initialization, and mask generation for more fine-grained processing. Validation tests are applied on each transformation and all processing steps and important information (e.g. seed) are logged in the output_folder.
Only
datasetandoptionsare expected as participant/user inputs. The functions are not meant to be called directly by users. See thedemos.mdfor example usage,documentation.mdfor more details, and theREADME.mdfor a quick-start overview.- Parameters:
dataset (ImageDataset) โ Dataset containing images, masks, buffers, and processing metadata.
options (Optional[Options]) โ Processing options, including mode selection, verbosity, random seed, and matching parameters.
Runtime Attributes
bool_masks(List): Boolean masks used for masked processing of each image.verbose(Literal[-1, 0, 1, 2, 3]): Verbosity level controlling console output.log(List[str]): Processing log messages accumulated during execution.validation(List[dict]): Internal validation results collected after processing steps.ssim_results(List[dict]): Structural Similarity Index (SSIM) test results.ssim_data(List[dict]): SSIM-related intermediate data retained for analysis.seed(Optional[int]): Random seed used for reproducible stochastic operations.desaturate_chroma_on_low_luminance(bool): Whether low-luminance chroma desaturation is applied in color treatment.from_cli(bool): Flag indicating that the processor was launched via command-line entry point.from_unit_test(bool): Internal flag used by unit-test execution paths.from_validation_test(bool): Internal flag used by validation-test execution paths.
Notes
Uses exact histogram specification without ties whenever possible for faster and deterministic results.
Input images are transformed into floats [0, 255] and then converted into the relevant color space at the beginning of processing. A buffer image dataset stores intermediate results. Output images are reconverted back into sRGB and uint8 at the end of all image processing steps.
All important processing information (e.g. seed) is logged and stored in output_folder.
- get_results()๏
Return the processed output in the same container style as the input.
This method is the public accessor for completed runs. It should be called after
process()has finished successfully.- Returns:
Union[list[np.ndarray], ImageListIO] โ If the original input images were provided as NumPy arrays, returns the processed array data.
If the input images were loaded from files, returns the same
ImageListIOcontainer used by the processor, with the processed images stored inside it.- Raises:
RuntimeError โ If processing has not been completed yet.
- hist_match()๏
Match each image histogram to the target histogram.
The target histogram is taken from
options.target_histand is matched with an exact histogram specification routine on 8-bit bins (n_bins = 256).Tie handling is selected by
options.hist_specification:1 =
noise2 =
moving-average3 =
gaussian4 =
hybrid
In practice, this implementation first checks whether ties are present in the current image. If no ties are detected, it forces the fast deterministic mapping path (
tie_strategy='none').If
options.hist_optimis enabled, the method performs additional SSIM guided sub-iterations using sensitivity maps and adaptive step-size control. The number of optimization iterations comes fromoptions.hist_iterations(implemented ashist_iterations + 1so the last pass remains an exact histogram projection). The random seed set inprocessis reused for reproducibility when tie-breaking requires stochastic behavior.Notes
Matching is performed inside the current masks when masks are active.
Behavior depends on color-treatment mode. In the default color path (
linear_luminance=Falseandas_gray=False), histogram matching is applied to the luminance channel only and chromaticity is restored later. Withlinear_luminance=Trueandas_gray=False, matching is done independently per RGB channel. Withas_gray=True, matching is done on grayscale intensities.Output is kept as float
[0, 255]in the working buffer.In optimization mode, SSIM trends are monitored and can trigger rollback/stall stopping through
StepSizeController.
- Raises:
ValueError โ If
target_histis incompatible with the processed images. Typical cases are: - wrong number of bins (must be 256), - wrong number of channels for the active processing mode, - target histogram image path with a size different from the processed dataset reference size.
- lum_match()๏
Match the luminance statistics of each image to a target.
This method adjusts the mean and/or standard deviation of each masked image region. The target values come from
options.target_lum:target_lum[0]controls the mean.target_lum[1]controls the standard deviation.
Target values can be explicit numbers (for example,
(127, 20)or(100, 15)), or special reserved values:0andNone.0requests a dataset-level automatic target for that statistic (for example,(0, 20)uses one automatic mean for the dataset and requests a standard deviation of20);Nonerequests preservation of each imageโs original statistic (for example,(None, 20)keeps each imageโs original mean and requests a standard deviation of20).
When
safe_lum_matchis enabled, SHINIER keeps explicit targets as long as possible while avoiding luminance values outside[0, 255]. It first relaxes the automatic or preserved statistic, trying a global correction before per-image corrections. If no safe solution exists, the explicit target may also be relaxed. For example,(50, 0)keeps mean50and adjusts the standard deviation first, whereas(0, 50)keeps standard deviation50and adjusts the mean first.Notes
The operation works on the processed buffer after the color treatment selected by
options.as_grayandoptions.linear_luminance.Clipping should happen before
uint8conversion because NumPy wraps out-of-range values when casting touint8.In grayscale mode, the matching is applied directly to the intensity values.
- process()๏
Run the full SHINIER pipeline on the current dataset.
This method performs the complete processing workflow selected by
options.mode. Input images are first copied into the working buffer and converted to float values in[0, 255]. Depending on the selected mode, the processor may then apply luminance matching, histogram matching, spatial-frequency matching, Fourier-spectrum matching, or dithering only.The processing order and number of iterations are controlled by
options.modeandoptions.iterations. When histogram-based methods are used, a random seed is created if needed and recorded in the log so that the run can be reproduced. Color conversion behavior is driven byoptions.as_gray,options.linear_luminance,options.rec_standard, andoptions.gamut_strategy. Final conversion touint8is handled byoptions.ditheringand can use noisy-bit or Floyd-Steinberg dithering.Notes
legacy_modeswitches several sub-steps to MATLAB-compatible behavior.conserve_memoryand the input storage mode affect how much data is kept in memory during processing.Fourier-based modes reuse the padding configuration from
options.fft_padding_modeandoptions.fft_padding_value.
- sf_match()๏
Match the rotationally averaged spatial-frequency content of each image (i.e., mean amplitude per spatial frequency).
The target frequency profile is derived from
options.target_spectrumand the initial padded spectra computed through the Fourier pipeline. The method scales each channelโs magnitude spectrum to match the target radial average while preserving the original phase.Notes
Spectra are computed upstream from float
[0, 1]images. If FFT padding is enabled, it is controlled byoptions.fft_padding_modeandoptions.fft_padding_value.The target profile is recomputed when the pipeline uses moving targets (composite iterative modes).
Frequencies above the Nyquist limit are explicitly zeroed to avoid aliasing artifacts.
The implementation switches between Cartesian and polar Fourier representations (
cart2pol/pol2cart) so magnitude can be reweighted while phase is preserved.Channel phases are preserved; only magnitude is reweighted to match the target rotational average.
The adjustment is done independently per channel.
Intermediate outputs can go outside
[0, 1]. This is expected in iterative modes5and7and is readjusted whenself._is_last_operationthrough soft clipping and optional rescaling.
- spec_match()๏
Match the full Fourier magnitude spectrum to a target spectrum.
This method replaces the magnitude of each imageโs Fourier transform with
options.target_spectrumwhile keeping the original phase information. It then reconstructs the image through the inverse FFT.Notes
This method is used by
options.mode = 4(spectrum-only) and by composite histogram+spectrum modes6and8.The target spectrum must be compatible with the processed image shape and channel count.
Fourier spectra are computed using the padding parameters from
options.fft_padding_modeandoptions.fft_padding_value.Output values are often outside
[0, 1]in intermediate steps and are readjusted whenself._is_last_operationthrough clipping and optional rescaling.
Color Processing๏
- class shinier.color.ColorConverter๏
Bases:
InformativeBaseModelEncapsulates color-space conversions for Rec.601/709/2020 systems.
This class implements reversible conversion pipelines:
sRGB -> linRGB -> XYZ -> CIELABCIELAB -> XYZ -> linRGB -> sRGB
Validation reference:
tests/validation_tests/Converter_validation_tests.py. Results replicate the colour-science package (https://pypi.org/project/colour-science/) across all tested colour standards (Rec.601, Rec.709, Rec.2020).Slight deviation observed for Rec.601 arises from our use of a piecewise sRGB/Rec.709-style transfer function (ฮณ โ 2.2) instead of the historical pure power-law (ฮณ = 2.8) applied in older analog-era Rec.601 specifications. This choice ensures consistency with MATLABโs rgb2gray and modern digital workflows, where Rec.601 luminance weights are paired with an sRGB-like TRC.
Core color-space conversion class for SHINIER.
- Parameters:
rec_standard (Literal[โrec601โ, โrec709โ, โrec2020โ]) โ Rec. standard used for transfer functions and RGB/XYZ matrices.
safe_mode (bool) โ If True, clip intermediate RGB values to valid ranges during conversions where clipping is appropriate.
Runtime Attributes
gamma(float): Transfer-function exponent.white_point(np.ndarray): Reference white point, D65 by default.M_RGB2XYZ(np.ndarray): Forward RGB-to-XYZ conversion matrix.M_XYZ2RGB(np.ndarray): Inverse XYZ-to-RGB conversion matrix.
- lab_to_sRGB(lab)๏
Convert CIE Lab values directly to gamma-encoded sRGB.
- lab_to_xyz(lab)๏
Convert CIE Lab values to CIE XYZ coordinates.
- linRGB_to_sRGB(linRGB)๏
Convert linear RGB values to non-linear RโGโBโ.
- Parameters:
linRGB (np.ndarray) โ Linear RGB array in [0, 1].
- Returns:
np.ndarray โ Gamma-encoded RGB array.
- linRGB_to_xyz(linRGB)๏
Convert linear RGB into CIE XYZ.
- Parameters:
linRGB (np.ndarray) โ Linear RGB array.
- Returns:
np.ndarray โ CIE XYZ tristimulus values.
- sRGB_to_lab(rgb)๏
Convert gamma-encoded sRGB values directly to CIE Lab.
- sRGB_to_linRGB(rgb)๏
Convert non-linear RโGโBโ values to linear RGB.
- Parameters:
rgb (np.ndarray) โ Gamma-encoded RGB array in [0, 1].
- Returns:
np.ndarray โ Linear RGB array.
Notes
rec2020uses the ITU-R BT.2020 inverse OETF.rec709uses the IEC 61966-2-1 sRGB transfer function.rec601uses an sRGB-style piecewise transfer function for consistency with MATLAB-like SDR workflows.
- sRGB_to_xyY(rgb)๏
Convert gamma-encoded sRGB values directly to CIE xyY.
- sRGB_to_xyz(rgb)๏
Convert gamma-encoded sRGB values directly to CIE XYZ.
- xyY_to_sRGB(xyY)๏
Convert CIE xyY values directly to gamma-encoded sRGB.
- static xyY_to_xyz(xyY, safe_mode=True)๏
Convert CIE xyY into CIE XYZ.
- Parameters:
xyY (np.ndarray) โ xyY array with channels
x,y, andY.safe_mode (bool) โ If True, replaces near-zero
yvalues with 1.0 to avoid numerical explosions. If False, preserves tinyyvalues so gamut-control code can detect out-of-gamut reconstructions.
- Returns:
np.ndarray โ CIE XYZ tristimulus values.
- xyz_to_lab(xyz)๏
Convert CIE XYZ values to CIE Lab coordinates.
- xyz_to_linRGB(xyz)๏
Convert CIE XYZ into linear RGB.
- xyz_to_sRGB(xyz)๏
Convert CIE XYZ values directly to gamma-encoded sRGB.
- static xyz_to_xyY(xyz)๏
Convert CIE XYZ into CIE xyY.
The x and y channels encode chromaticity; Y stores luminance.
- class shinier.color.ColorTreatment๏
Bases:
ColorConverterApply forward and backward color treatment around SHINIER processing.
The forward treatment converts input images into the space used by the processing pipeline. In the default color-preserving path, sRGB images are converted to CIE xyY, the luminance channel is stored in the main buffer, and chromaticity channels are stored in an auxiliary buffer. The backward treatment reconstructs display-ready sRGB images after processing.
Runtime Attributes
Y_desaturation_threshold(ClassVar[float]): Low-luminance threshold used when preventive chroma desaturation is enabled.
- static backward_color_treatment(rec_standard, input_images, output_images, linear_luminance, as_gray, input_other=None, conversion_type='xyY_to_sRGB', gamut_strategy='clip', verbose=False)๏
Apply the backward color-treatment step to an image collection.
The function processes the provided input images and optionally incorporates additional data (input_other) depending on the context, ensuring that images are returned to their original color or grayscale representations.
- Parameters:
rec_standard (REC_STANDARD) โ Color standard used for processing.
input_images (ImageListIO) โ Main processed buffer to convert back.
output_images (ImageListIO) โ Buffer receiving reconstructed output images.
linear_luminance (bool) โ If True, no perceptual color treatment needs to be undone.
as_gray (bool) โ If True, reconstructs grayscale output.
input_other (Optional[ImageListIO]) โ Auxiliary chromatic data required for color reconstruction.
conversion_type (Literal[โxyY_to_sRGBโ, โlab_to_sRGBโ]) โ Backward color conversion to apply.
gamut_strategy (str) โ Strategy for repairing out-of-gamut pixels during conversion.
verbose (bool) โ If True, prints processing messages.
- Raises:
ValueError โ If color reconstruction requires
input_otherbut it is missing.- Returns:
ImageListIO โ Output images converted back to display-ready representation.
- static forward_color_treatment(rec_standard, input_images, output_images, linear_luminance, as_gray, output_other=None, conversion_type='sRGB_to_xyY', desaturate_chroma_on_low_luminance=False, legacy_mode=False, verbose=False)๏
Apply the forward color-treatment step to an image collection.
This static method performs a forward pass based on the specified color treatment, grayscale conversion, and color space transformation. Depending on the input parameters, the images may undergo various treatments such as conversion to grayscale, transformation into a different color space (sRGB to xyY or sRGB to Lab), or extracting specific channels for processing.
- Parameters:
rec_standard (REC_STANDARD) โ Reference color standard for image processing.
input_images (ImageListIO) โ Input image collection to process.
output_images (ImageListIO) โ Buffer receiving the main processed channel or channels.
linear_luminance (bool) โ If True, skips perceptual color-space conversion and processes channels directly.
as_gray (bool) โ If True, output is grayscale. If False, color channels are preserved through an auxiliary buffer when needed.
output_other (Optional[ImageListIO]) โ Secondary buffer receiving auxiliary chromatic channels.
conversion_type (Literal[โsRGB_to_xyYโ, โsRGB_to_labโ]) โ Forward color conversion to apply.
desaturate_chroma_on_low_luminance (bool) โ If True, desaturates chroma for very low-luminance pixels to prevent chromatic noise from being inflated by luminance manipulation.
legacy_mode (bool) โ If True, uses MATLAB-compatible grayscale conversion behavior.
verbose (bool) โ If True, prints processing messages.
- Returns:
Tuple[ImageListIO, Optional[ImageListIO]] โ Main processed buffer and optional auxiliary chromatic buffer.
- Raises:
ValueError โ If color treatment requires
output_otherbut it is not provided.
- class shinier.color.GamutControl๏
Bases:
InformativeBaseModelManage gamut constraints and repairs for image datasets.
GamutControl implements robust gamut management for pipelines that manipulate luminance and chromaticity separately, for example processing
Ywhile preservingxyin CIE xyY. After such operations, some colors may become out of gamut for the target RGB encoding: when converting back to linear RGB, one or more channels can fall below 0 or exceed 1. Naive clipping fixes the bounds but introduces non-linear, content-dependent distortions such as hue shifts and contrast changes.- Parameters:
strategy (GAMUT_STRATEGY_TYPE) โ Strategy to apply when reconstructed RGB values fall outside gamut.
rec_standard (str) โ RGB standard used by the internal color converters.
color_space (Literal[โxyYโ]) โ Color space used for gamut repair.
Runtime Attributes
warning_threshold(float): Scaling threshold below which warnings are logged.prc_clipping(float): Percentage of extreme pixels allowed to clip when robust quantile reduction is used.low_Y_desaturate(bool): Whether to desaturate low-luminance chroma before processing.low_Y_threshold(float): Low-luminance threshold in [0, 1].low_Y_fade_width(float): Width of the optional low-luminance fade ramp.log_low_Y_chroma_loss(bool): Whether to log chroma-loss metrics for low-luminance desaturation.
Strategies๏
clipRely on the converterโs clipping/safe-mode behavior only, without global constraint estimation.
constrain_image_luminanceFor each image independently, compute a luminance scaling factor so reconstructed linear RGB stays mostly within bounds, then scale
Y. Chromaticity is preserved.constrain_dataset_luminanceCompute a single global luminance scaling factor from the whole dataset and apply it to every image, preserving relative luminance structure across images.
constrain_image_chrominanceFor each image independently, compute a per-image chroma scaling factor by desaturating
xytoward the adapting white-point while keepingYunchanged.constrain_dataset_chrominanceCompute one global chroma scaling factor for the dataset and apply it to all images by desaturating
xytoward the adapting white-point.
Safeguards๏
[S1]Low-luminance preventive desaturationIn xyY, chromaticity becomes ill-conditioned near
Y = 0becausexandyare normalized byX + Y + Z. Small numerical noise can produce unstable colors, so low-luminance pixels can be gently desaturated toward the white-point chromaticity while keepingYunchanged. Implemented inapply_low_Y_desaturation.[S2]Chroma validity maskExcludes ill-conditioned or physically implausible chromaticities, such as
ynear 0 orx + yslightly above 1, from driving constraint estimation. These pixels may still be repaired later via clipping or safeguards.[S3]Reliability weighting by luminancePixels near
Y = 0andY = 1are down-weighted when estimating global factors because tinyxynoise at luminance extremes can produce unrealistically large RGB excursions. Mid-tones therefore dominate the estimate.[S4]Allowed outlier clippingOptionally use a lower quantile instead of the strict minimum when reducing per-pixel headroom ratios. This prevents a tiny fraction of extreme pixels from dictating aggressive global compression.
Notes
The remaining open question is to quantify the trade-off between gamut safety and distortion of the original image statistics, including order accuracy metrics.
- apply_dataset(buffer, buffer_other, verbose=False)๏
Apply a dataset-level gamut strategy.
Dataset-level strategies compute a single global factor (scale or desaturation) and apply it to all images.
- Parameters:
buffer (ImageListIO) โ Main luminance buffer.
buffer_other (ImageListIO) โ Auxiliary chromaticity buffer.
verbose (bool) โ Whether to emit informative logs.
- Returns:
Tuple[ImageListIO, ImageListIO] โ Updated luminance and chromaticity buffers.
- apply_image(Y, other, idx=None, verbose=False)๏
Apply an image-level gamut strategy to a single image.
- Parameters:
Y (np.ndarray) โ Luminance image in [0, 255], with shape (H, W).
other (np.ndarray) โ Chromaticity channels, usually xy, with shape (H, W, 2).
idx (Optional[int]) โ Image index used for logging.
verbose (bool) โ Whether to emit informative logs.
- Returns:
Tuple[np.ndarray, np.ndarray] โ Updated luminance and chromaticity arrays with the same shapes as the inputs.
- apply_low_Y_desaturation(Y, other, idx=None, verbose=False)๏
[S1] Optionally desaturate chroma for low-luminance pixels.
Intended as a preventive step before luminance manipulation. Only modifies other (xy). Returns Y unchanged.
- Parameters:
Y (np.ndarray) โ Luminance image in [0, 1] or [0, 255], with shape (H, W) or (H, W, 1).
other (np.ndarray) โ Chromaticity array with xy channels, shape (H, W, 2).
idx (Optional[int]) โ Optional image index for logging.
verbose (bool) โ Whether to emit informative logs.
- Returns:
Tuple[np.ndarray, np.ndarray] โ Unchanged luminance image and possibly desaturated chromaticity.
- get_max_luminance_map(x, y)๏
Calculate the maximum in-gamut luminance for each chromaticity.
- Parameters:
x (np.ndarray) โ CIE x chromaticity coordinates.
y (np.ndarray) โ CIE y chromaticity coordinates.
- Returns:
np.ndarray โ Maximum luminance before RGB clipping for each chromaticity.
- shinier.color.rgb2gray(image, conversion_type='equal', matlab_601=False)๏
Convert an RโGโBโ image to grayscale luma.
- Parameters:
image (Union[np.ndarray, Image.Image]) โ RGB image array with a final channel dimension of 3. The image is assumed to be gamma-encoded, as with typical sRGB files.
conversion_type (RGB_STANDARD) โ Luma standard to use:
"equal","rec601","rec709", or"rec2020".matlab_601 (bool) โ If True and
conversion_type="rec601", uses MATLABโs BT.601 weights.
- Returns:
np.ndarray โ Grayscale image with the final color channel removed.
Notes
This computes luma (Yโ) from gamma-encoded components. For physical linear luminance, first linearize RGB, combine linear-light coefficients, then re-encode if needed.
- shinier.color.gray2rgb(image)๏
Convert a grayscale image to RGB.
- Parameters:
image (Union[np.ndarray, Image.Image]) โ Input grayscale image.
- Returns:
np.ndarray โ RGB image with three identical channels.
Utility Functions๏
Plotting๏
- shinier.utils.hist_plot(hist, bins=256, figsize=None, dpi=100, title=None, target_hist=None, descriptives=False, ax=None, show_normalized_rmse=False)๏
Display a histogram with optional target and descriptive statistics.
The histogram is displayed as a compact horizontal plot. A grayscale gradient bar (0โ255) is placed directly under the histogram. When
descriptives=True, the histogram includes a vertical line for the mean and a translucent band spanning mean +/- one standard deviation. For RGB histograms, ฮผ and ฯ are computed and displayed per channel.- Parameters:
hist (np.ndarray) โ Input histogram, either
(n_bins,)for grayscale or(n_bins, 3)for RGB. Histograms are normalized before display.bins (int) โ Number of histogram bins.
figsize (Optional[tuple]) โ Matplotlib figure size. Used only when
axis None.dpi (int) โ Matplotlib figure DPI. Used only when
axis None.title (Optional[str]) โ Optional histogram title.
target_hist (Optional[np.ndarray]) โ Optional target histogram to overlay.
descriptives (bool) โ If True, overlays mean and +/-1 standard deviation.
ax (Optional[plt.Axes]) โ Axes on which to draw the histogram.
show_normalized_rmse (bool) โ If True, shows normalized RMSE between two normalized histograms.
- Returns:
Tuple[plt.Figure, Tuple[Any, Any]] โ Figure and axes for the gradient bar and histogram.
- shinier.utils.imhist_plot(img, bins=256, figsize=(8, 6), dpi=100, title=None, target_hist=None, binary_mask=None, descriptives=False, ax=None, show_normalized_rmse=False)๏
Display an image with its histogram and optional descriptive statistics.
The image is shown on top, with a compact horizontal histogram below. A grayscale gradient bar (0โ255) is placed directly under the histogram. When
descriptives=True, the histogram includes a vertical line for the mean and a translucent band spanning mean +/- one standard deviation. For RGB images, ฮผ and ฯ are computed and displayed per channel.- Parameters:
img (np.ndarray) โ Input image, either grayscale
(H, W)or RGB(H, W, 3).bins (int) โ Number of histogram bins.
figsize (tuple) โ Matplotlib figure size.
dpi (int) โ Matplotlib figure DPI.
title (Optional[str]) โ Optional image title.
target_hist (Optional[np.ndarray]) โ Optional target histogram to overlay.
binary_mask (Optional[np.ndarray]) โ Optional mask used to compute the histogram.
descriptives (bool) โ If True, overlays mean and +/-1 standard deviation.
ax (Optional[plt.Axes]) โ Axes on which to draw the image.
show_normalized_rmse (bool) โ If True, shows normalized RMSE between two normalized histograms.
- Returns:
Tuple[plt.Figure, Tuple[Any, Any, Any]] โ Figure and axes for the image, gradient bar, and histogram.
- shinier.utils.imshow(image, ax=None)๏
Show an image on matplotlib axes.
- Parameters:
image (np.ndarray) โ Image to display.
ax (Optional[plt.Axes]) โ Axes on which to draw the image. If None, a new figure and axes are created.
- Returns:
Tuple[plt.Figure, plt.Axes] โ Figure and axes containing the image.
- shinier.utils.sf_plot(image, sf_p=None, target_sf=None, ax=None, show_normalized_rmse=False)๏
Plot the rotational average of Fourier energy.
- Parameters:
image (np.ndarray) โ Image array of shape (H, W) or (H, W, 3).
sf_p (Optional[np.ndarray]) โ Precomputed spatial-frequency profile. If None, it is computed from
image.target_sf (Optional[np.ndarray]) โ Optional target spatial-frequency profile to overlay.
ax (Optional[plt.Axes]) โ Axes on which to draw the plot. If None, a new figure is created.
show_normalized_rmse (bool) โ If True, show normalized RMSE on the graph.
- Returns:
Union[plt.Figure, plt.Axes] โ Figure or axes containing the plot.
- shinier.utils.spectrum_plot(spectrum, cmap='gray', log=True, gamma=1.0, ax=None, with_colorbar=True, colorbar_label='log(1 + |F|) (stretched)', target_spectrum=None, show_normalized_rmse=False)๏
Display a Fourier magnitude spectrum.
- Parameters:
spectrum (np.ndarray) โ Fourier magnitude or power spectrum.
cmap (str) โ Matplotlib colormap.
log (bool) โ If True, display
log(1 + |F|).gamma (float) โ Optional gamma correction applied after stretching.
ax (Optional[plt.Axes]) โ Axes on which to draw the spectrum. If None, a new figure is created.
with_colorbar (bool) โ If True, add a colorbar.
colorbar_label (str) โ Label for the colorbar.
target_spectrum (Optional[np.ndarray]) โ Optional target spectrum used for normalized RMSE display.
show_normalized_rmse (bool) โ If True, show normalized RMSE against
target_spectrum.
- Returns:
Union[plt.Figure, plt.Axes] โ Figure or axes containing the spectrum plot.
- shinier.utils.im_power_spectrum_plot(im, with_colorbar=True)๏
2D log-scaled Fourier power spectrum (centered).
Visualizes the distribution of image energy across spatial frequencies and orientations. The center of the plot corresponds to low spatial frequencies, while the edges represent high frequencies. Brightness indicates the power contribution of each spatial frequency and orientation.
- Parameters:
im (np.ndarray) โ Image array of shape
(H, W)or(H, W, 3). RGB images are converted to grayscale before computing the spectrum.with_colorbar (bool, optional) โ If True, display a colorbar.
- Returns:
matplotlib.figure.Figure โ Figure containing the centered power spectrum.
- shinier.utils.show_processing_overview(processor, img_idx=0, show_figure=True, show_initial_target=False)๏
Display before/after images and diagnostics for all processing steps in one figure.
The figure layout adapts to the active SHINIER mode:
Row 1: before/after images.
Subsequent rows: one row per processing step (for example luminance, histogram, or spectrum matching).
Each diagnostic row shows before and after panels side by side.
- Parameters:
processor (ImageProcessor) โ SHINIER image processor instance.
img_idx (int, optional) โ Index of the image to visualize.
show_figure (bool, optional) โ If False, return the figure without calling
plt.show().show_initial_target (bool, optional) โ If True, plot the initial target in composite modes.
- Returns:
matplotlib.figure.Figure โ Composite figure summarizing the image transformations.
Examples
>>> from shinier import ImageDataset, ImageProcessor, Options >>> from shinier.utils import show_processing_overview >>> processor = ImageProcessor(dataset=ImageDataset(options=Options(mode=2))) >>> fig = show_processing_overview(processor, img_idx=0, show_figure=False)
StimulusMasker๏
- class shinier.utils.StimulusMasker๏
Bases:
objectCreate and apply ellipse masks to image stimuli.
- Parameters:
image_size (int | tuple[int, int]) โ Mask/image size in pixels. If int, creates a square mask. If tuple, uses
(height, width).cutoff_a (float) โ Horizontal ellipse radius in normalized coordinates.
cutoff_b (float | None, optional) โ Vertical ellipse radius. If None, uses
cutoff_a(circular mask).offset_a (float, optional) โ Horizontal ellipse offset in normalized coordinates.
offset_b (float, optional) โ Vertical ellipse offset in normalized coordinates.
mask_type (MaskType, optional) โ Mask edge type:
"hard","gaussian", or"feathered_disk".sigma (float, optional) โ Gaussian standard deviation in pixels, used when
mask_type == "gaussian".edge_width (float, optional) โ Transition width in pixels, used when
mask_type == "feathered_disk".background (float, optional) โ Background value in
[0, 1]outside the mask.output_dtype (np.dtype | type, optional) โ Output dtype for masked images.
Notes
Input images can be grayscale
(H, W)or channel-based(H, W, C). Integer images are normalized by their dtype range. Float images with max value greater than 1 are assumed to be in[0, 255].Examples
>>> masker = StimulusMasker(128, 0.7, mask_type="feathered_disk", edge_width=3) >>> mask = masker.mask() >>> masked_images = masker.apply_all(stim_arr) >>> final_mask = masker.interactive_mask(image)
- apply(image)๏
Apply the mask to one image.
- Parameters:
image (np.ndarray) โ Input image. Accepts
(H, W)grayscale or(H, W, C)channel images. Integer images are normalized by dtype range; float images above 1 are assumed to be in[0, 255].- Returns:
np.ndarray โ Masked image cast to
output_dtype.
- apply_all(stimuli)๏
Apply the same mask to multiple images.
- interactive_mask(image)๏
Open a Matplotlib GUI for tuning the mask on top of an image.
The GUI updates the current object in place. Closing the window keeps the selected cutoff, offset, mask type, sigma, and edge_width values on self.
- Parameters:
image (np.ndarray) โ Image used for the masked preview. Accepts
(H, W)grayscale or(H, W, C)channel images. The mask size is automatically set from this image.- Returns:
np.ndarray โ Final mask as
float64in[0, 1].
- mask()๏
Generate mask as float64 in [0, 1].
MatlabOperators๏
- class shinier.utils.MatlabOperators๏
Bases:
objectReplicate selected MATLAB operators with NumPy-based implementations.
This utility class provides static methods used to preserve MATLAB-compatible numerical behavior when needed (for example in validation pipelines).
- static ceil(x)๏
Replicates MATLABโs ceil function.
- static double(x)๏
Ensures double precision, similar to MATLABโs double.
- static fix(x)๏
Replicates MATLABโs fix function (rounds toward zero).
- static floor(x)๏
Replicates MATLABโs floor function.
- static int16(x)๏
Replicates MATLABโs int16 behavior: rounds and clips to [-2**15, 2**15 - 1]
- static int32(x)๏
Replicates MATLABโs int32 behavior: rounds and clips to [-2**31, 2**31 - 1]
- static linspace(start, stop, num=50)๏
Replicates MATLABโs linspace function.
- static mean2(x)๏
Return the mean of all elements, mirroring MATLABโs mean2.
- static mod(x, y)๏
Replicates MATLABโs mod function (modulus operation with sign matching divisor).
- static rem(x, y)๏
Replicates MATLABโs rem function (remainder operation with sign matching dividend).
- static rgb2gray(image)๏
Replicates MATLABโs rgb2gray function (ITU-R rec601).
- static round(x)๏
Round values with MATLABโs tie-breaking rule.
MATLAB rounds half values away from zero (e.g.,
0.5 -> 1and-0.5 -> -1), while NumPy uses bankerโs rounding by default.- Parameters:
x (array-like) โ Input scalar or array values.
- Returns:
np.ndarray โ Rounded values using MATLAB-compatible tie behavior. The returned dtype follows NumPy arithmetic rules for the input type.
Notes
Original adaptation inspired by GLMsingleโs
alt_roundimplementation, with project-specific adjustments to better align with SHINIER behavior.Examples
>>> x = np.array([-1.0, -0.5, 0.0, 0.5, 1.5, 2.5]) >>> MatlabOperators.round(x) array([-1., -1., 0., 1., 2., 3.])
- static single(x)๏
Ensures single precision, similar to MATLABโs single.
- static std2(x)๏
Replicates MATLABโs std2 function, which uses ddof=1
- static uint8(x)๏
Replicates MATLABโs uint8 behavior: rounds and clips to [0, 255]
Functions๏
- shinier.utils.print_shinier_header(is_tty=True, version='0.2.0')๏
Prints a styled header for the SHINIER CLI.
- shinier.utils.get_field_values_from_pydantic_model(field)๏
Return all possible categorical values for a Pydantic field.
- shinier.utils.generate_pydantic_key_value_dict(model_cls)๏
Return dict of field โ possible values for a Pydantic model.
- shinier.utils.sf_profile(image, spectrum=None, is_power_spectrum=True, is_truncated=True, legacy_mode=False)๏
Compute the rotational average of a Fourier spectrum.
- Parameters:
image (np.ndarray) โ Input image.
spectrum (Optional[np.ndarray]) โ Precomputed spectrum. If provided, the function uses it instead of computing a new spectrum from
image.is_power_spectrum (bool) โ If True, averages power. If False, averages magnitude.
is_truncated (bool) โ If True, truncates the profile at the Nyquist frequency of the shortest image dimension.
legacy_mode (bool) โ If True, uses MATLAB-compatible rounding for the radius grid.
- Returns:
Tuple[np.ndarray, np.ndarray] โ Rotational average and corresponding radii.
- shinier.utils.freq_axis(n)๏
Compute spatial frequency axis for image spectrum
- shinier.utils.get_radius_grid(x_size, y_size, legacy_mode=False)๏
Compute the radius grid for rotational average
- shinier.utils.rotational_avg(spectrum, radius)๏
Mean spectrum per radius bin (annular average).
- shinier.utils.stretch(arr)๏
Stretch an array to the range [0, 1].
This rescales the input array so that its minimum maps to 0 and its maximum maps to 1. Works for any number of dimensions (grayscale, color images, or higher-dimensional data).
- Parameters:
arr (np.ndarray) โ Input array of any shape and numeric dtype.
- Returns:
np.ndarray โ Array of the same shape as input, with dtype
float64and values scaled to[0, 1].
Notes
If the array has constant values, returns an array of zeros to avoid division by zero. Output is
float64for numerical stability.
- shinier.utils.convolve_1d(arr, kernel, axis)๏
Apply 1D convolution with reflect padding along a chosen axis.
- Parameters:
arr (np.ndarray) โ Input 2D image array.
kernel (np.ndarray) โ 1D convolution kernel.
axis (int) โ Axis along which to convolve. Use
0for vertical and1for horizontal.
- Returns:
np.ndarray โ Convolved image.
- shinier.utils.convolve_2d(image, kernel)๏
Convolve a 2D image with a kernel.
A 1D kernel applies separable convolution. A 2D square kernel applies dense 2D convolution with sliding windows.
- Parameters:
image (np.ndarray) โ Input 2D image.
kernel (np.ndarray) โ Convolution kernel, either 1D or 2D square.
- Returns:
np.ndarray โ Convolved image.
- Raises:
TypeError โ If inputs have the wrong type or dimensionality.
ValueError โ If a 2D kernel is not square.
- shinier.utils.has_duplicates(image, binary_mask)๏
Check whether an image contains duplicate pixel values in any channel.
- Parameters:
image (np.ndarray) โ Image data.
binary_mask (np.ndarray) โ Boolean mask selecting the pixels to inspect.
- Returns:
bool โ True if duplicate pixel values are found in any channel.
- shinier.utils.n_unique(arr)๏
Compute the number of unique values in 2D and 3D images efficiently using hash.
Handles 1D vectors or 2D and 3D images. If the array is 3D, it is reshaped to (H*W, C). If it is 2D and square, it is treated as a single-channel image (converted via im3D). Otherwise, the shape is preserved.
The implementation uses the np.view(void) hashing trick to perform fast and deterministic uniqueness checks across all vector dimensions.
- Parameters:
arr (np.ndarray) โ Input array representing feature responses or image data. Accepts vectors, 2D images, 3D images, or generic feature matrices.
- Returns:
int โ Number of unique row vectors in the flattened representation.
- Raises:
ValueError โ If the input has an invalid shape.
Notes
The function avoids deep copies where possible and is deterministic.
- shinier.utils.strict_ordering(image, kernels, early_stop=False, min_kernels=None)๏
Assign strict pixel ordering using a customizable set of kernels.
For each channel, this function applies a series of convolution kernels and stacks the resulting responses into a multidimensional feature vector used for lexicographic sorting. The order accuracy (OA) is tracked after each kernel addition.
- Parameters:
image (np.ndarray) โ Input grayscale or color image.
kernels (list[np.ndarray]) โ Convolution kernels to apply.
early_stop (bool, optional) โ If True, stop early once all pixels have unique feature responses after applying at least
min_kernelskernels.min_kernels (Optional[int], optional) โ Minimum number of kernels to apply before early stopping is allowed.
- Returns:
tuple[np.ndarray, list[float]] โ Lexicographic rank indices and order-accuracy values per channel.
- shinier.utils.exact_histogram(image, target_hist, binary_mask=None, n_bins=None, tie_strategy='hybrid', verbose=True)๏
Unified exact histogram specification.
- Provides a single entry point for 3 families of exact histogram specification strategies:
Direct mapping assuming no isoluminant pixel (no ties): โnoneโ
Coltucโs ordering based on filter bank: โmoving-averageโ or โgaussianโ.
Noise-based tie-breaking: โnoiseโ.
A hybrid strategies first using โgaussianโ, then โnoise if ties persist: โhybridโ.
- Parameters:
image (np.ndarray) โ Input grayscale or RGB image.
target_hist (np.ndarray) โ Target histogram counts or weights, shape
(n_bins, C).binary_mask (Optional[np.ndarray], optional) โ Foreground mask.
n_bins (Optional[int], optional) โ Number of bins. Required for float images.
tie_strategy (str, optional) โ Tie-breaking strategy:
"none","moving-average","gaussian","noise", or"hybrid".verbose (bool, optional) โ If True, log key operations.
- Returns:
tuple[np.ndarray, list] โ Histogram-specified image and order-accuracy values per channel.
- shinier.utils.apply_histogram_mapping(image, target_hist, binary_mask=None)๏
Map pixel ranks to discrete intensity levels to match a target histogram.
- Parameters:
image (np.ndarray) โ Input image with ordered or unique-valued pixels.
target_hist (np.ndarray) โ Target histogram of shape
(n_bins, C).binary_mask (Optional[np.ndarray], optional) โ Optional boolean mask.
- Returns:
np.ndarray โ Histogram-specified image.
- shinier.utils.floyd_steinberg_dithering(image, depth=256, legacy_mode=False)๏
Apply Floyd-Steinberg error-diffusion dithering.
- Parameters:
image (np.ndarray) โ Floating-point image with values in
[0, 1].depth (int, optional) โ Number of quantization levels.
legacy_mode (bool, optional) โ If True, use MATLAB-compatible rounding.
- Returns:
np.ndarray โ Dithered integer image in
[0, depth - 1].
References
Floyd, R. W. and Steinberg, L. (1976). An adaptive algorithm for spatial grey scale. Proceedings of the Society for Information Display, 17, 75-77.
- shinier.utils.error_diffusion_dither(image, n_levels=256, *, diffusion_map, legacy_mode=False, scan_order='raster', normalize_map=False)๏
Apply generic channel-wise error-diffusion dithering.
- Parameters:
image (np.ndarray) โ Float image in
[0, 1]with shape(H, W)or(H, W, C).n_levels (int, optional) โ Number of quantization levels per channel. Must be at least 2.
diffusion_map (Sequence[DiffusionTap]) โ Sequence of diffusion taps
(dy, dx, weight).legacy_mode (bool, optional) โ If True, use MATLAB-compatible rounding.
scan_order (Literal[โrasterโ, โserpentineโ], optional) โ Pixel traversal order.
normalize_map (bool, optional) โ If True, normalize diffusion weights by their sum.
- Returns:
np.ndarray โ Quantized integer image in
[0, n_levels - 1].
- shinier.utils.soft_clip(arr, min_value=0.0, max_value=1.0, max_percent=0.05, tol=0.0001, verbose=True)๏
Softly clip an array to a target range.
Softly clip an array to
[min_value, max_value]while ensuring that the proportion of clipped values does not exceedmax_percent. If naive clipping would clip more thanmax_percentof values, the function rescales the array to reduce clipping until the clipped proportion is approximatelymax_percent.- Parameters:
arr (np.ndarray) โ Input array.
min_value (float, optional) โ Minimum allowed value after clipping.
max_value (float, optional) โ Maximum allowed value after clipping.
max_percent (float, optional) โ Maximum allowed proportion of clipped values.
tol (float, optional) โ Optimization tolerance.
verbose (bool, optional) โ If True, print diagnostic information.
- Returns:
np.ndarray โ Clipped and possibly rescaled array.
- shinier.utils.noisy_bit_dithering(image, depth=256, legacy_mode=False)๏
Apply noisy-bit dithering.
- Implements the dithering algorithm presented in :
Allard, R., Faubert, J. (2008) The noisy-bit method for digital displays: converting a 256 luminance resolution into a continuous resolution. Behavior Research Method, 40(3), 735-743.
- Parameters:
image (np.ndarray) โ Floating-point image with values in
[0, 1].depth (int, optional) โ Number of quantization levels.
legacy_mode (bool, optional) โ If True, use MATLAB-compatible rounding.
- Returns:
np.ndarray โ Dithered integer image in
[0, depth - 1].
References
Allard, R. and Faubert, J. (2008). The noisy-bit method for digital displays: converting a 256 luminance resolution into a continuous resolution. Behavior Research Methods, 40(3), 735-743.
Example
>>> processed_image = noisy_bit_dithering(image, depth = 256)
Notes
This example assumes that all rgb values are linearly related to luminance values (e.g. on a Mac, put your LCD monitor gamma parameter to 1 in the Displays section of the System Preferences). If this is not the case, use a lookup table to transform the tim integer values into rgb values corresponding to evenly spaced luminance values.
Frederic Gosselin, 27/09/2022 frederic.gosselin@umontreal.ca
Slight modifications for Matlab compatibility: Nicolas Dupuis-Roy & Mathias Salvas-Hรฉbert, 2025-08-19
References
Allard, R. and Faubert, J. (2008). The noisy-bit method for digital displays: converting a 256 luminance resolution into a continuous resolution. Behavior Research Methods, 40(3), 735-743.
- shinier.utils.uint_to_float01(image, apply_clipping=True)๏
Convert an unsigned integer image to floating point values in [0, 1].
Integer values are divided by the maximum value of their dtype.
- Parameters:
image (np.ndarray) โ Unsigned integer image.
apply_clipping (bool, optional) โ If True, clip output values to
[0, 1]. If False, raise an error when values are out of range.
- Returns:
np.ndarray โ Converted image with dtype
float64.- Raises:
ValueError โ If
apply_clippingis False and values fall outside[0, 1].
- shinier.utils.float01_to_uint(image, apply_clipping=True, apply_rounding=True, bit_size=8, verbose=True)๏
Convert a floating-point image in [0, 1] to an unsigned integer image.
Values are scaled to the full range of the requested unsigned integer dtype.
- Parameters:
image (np.ndarray) โ Floating-point image.
apply_clipping (bool, optional) โ If True, clip values outside
[0, 1]. If False, raise an error when values are out of range.apply_rounding (bool, optional) โ If True, round values before conversion.
bit_size (int, optional) โ Bit size of the unsigned integer output. Supported values are 8, 16, 32, and 64.
verbose (bool, optional) โ If True, warn when clipping is needed.
- Returns:
np.ndarray โ Converted image with unsigned integer dtype.
- Raises:
ValueError โ If
bit_sizeis invalid, or ifapply_clippingis False and the image contains values outside[0, 1].
- shinier.utils.pol2cart(magnitude, angle)๏
Convert polar coordinates to Cartesian coordinates.
- Parameters:
magnitude (np.ndarray) โ Distance from the origin.
angle (np.ndarray) โ Angle in radians.
- Returns:
tuple[np.ndarray, np.ndarray] โ Cartesian
xandycoordinates.
- shinier.utils.cart2pol(x, y)๏
Convert Cartesian coordinates to polar coordinates.
- Parameters:
x (np.ndarray) โ Cartesian x-coordinate.
y (np.ndarray) โ Cartesian y-coordinate.
- Returns:
tuple[np.ndarray, np.ndarray] โ Magnitude and angle in radians.
- shinier.utils.separate(mask, background=0, background_operator='==', smoothing=False, show_figure=False)๏
Separate a mask into foreground and background boolean arrays.
- Parameters:
mask (np.ndarray) โ Source mask image or boolean mask.
background (int or float, optional) โ Background value. Use
300to select the most frequent value in the mask automatically.background_operator (str, optional) โ Operator used to define background pixels relative to
background.smoothing (bool, optional) โ If True, apply median blur to the mask.
show_figure (bool, optional) โ If True, display foreground and background masks.
- Returns:
tuple[np.ndarray, np.ndarray, float] โ Foreground mask, background mask, and resolved background value.
- shinier.utils.image_spectrum(image, rescale=True, fft_padding_mode=0, fft_padding_value=300, fft_padding_ratio=0.5)๏
Compute Fourier magnitude and phase spectra for an image.
- Parameters:
image (np.ndarray) โ Input image.
rescale (bool, optional) โ If True, rescale each channel to
[0, 1]before FFT.fft_padding_mode (int, optional) โ Spatial padding mode before FFT: 0 disabled, 1 reflect, 2 symmetric, 3 constant.
fft_padding_value (int, optional) โ Constant padding value in
[0, 255], or300for mean intensity.fft_padding_ratio (float, optional) โ Fraction of the smallest image side used as padding before FFT.
- Returns:
tuple[np.ndarray, np.ndarray] โ Magnitude and phase spectra.
- shinier.utils.gaussian_kernel(size, sigma=None, coverage=None, n_dim=2)๏
Generate a normalized Gaussian kernel.
Exactly one of
sigmaorcoveragemust be provided.If
coverageis specified,sigmais automatically computed so that the specified fraction of the Gaussianโs total area is contained within the kernel support of givensize.- Parameters:
size (int) โ Size of the kernel. Must be odd.
sigma (float, optional) โ Standard deviation of the Gaussian. Mutually exclusive with
coverage.coverage (float, optional) โ Fraction in
(0, 1)of total Gaussian area contained within the kernel. Mutually exclusive withsigma.n_dim (int, optional) โ Dimensionality of the kernel. Use
1for shape(size,)or2for shape(size, size).
- Returns:
np.ndarray โ Normalized Gaussian kernel.
- Raises:
ValueError โ If inputs are invalid or if both/neither of
sigmaandcoverageare provided.
- shinier.utils.center_surround_kernel(size, sigma_center=None, coverage=None, ratio=1.6, n_dim=2)๏
Generate a centerโsurround (Difference-of-Gaussians) kernel.
The kernel is built as a narrow (center) Gaussian minus a broader (surround) Gaussian, each L1-normalized over the same finite support defined by
size. The resulting kernel is DC-balanced, suitable for centerโsurround/edge-like filtering and strict-ordering feature banks.Exactly one of
sigma_centerorcoveragemust be provided.If
coverageis provided,sigma_centeris chosen so that the specified fraction of the 1D Gaussianโs total area lies within the half-widthsize // 2.sigma_surroundis then derived asratio * sigma_center.- Parameters:
size (int) โ Odd kernel size.
sigma_center (float, optional) โ Standard deviation of the center Gaussian in pixels.
coverage (float, optional) โ Fraction in
(0, 1)of total Gaussian area within the support.ratio (float, optional) โ Surround-to-center sigma ratio.
n_dim (int, optional) โ Use
1for shape(size,)or2for shape(size, size).
- Returns:
np.ndarray โ Zero-mean Difference-of-Gaussians kernel.
- Raises:
ValueError โ If inputs are invalid.
- shinier.utils.laplacian_kernel(size, sigma=None, coverage=None, n_dim=2)๏
Generate a Laplacian-of-Gaussian (LoG) kernel over finite support.
Exactly one of
sigmaorcoveragemust be provided.If
coverageis given,sigmais chosen so the specified fraction of the 1D Gaussian area lies within half-widthsize // 2.- Parameters:
size (int) โ Odd kernel size.
sigma (float, optional) โ Standard deviation of Gaussian envelope in pixels.
coverage (float, optional) โ Fraction in
(0, 1)of total Gaussian area within support.n_dim (int, optional) โ Use
1for shape(size,)or2for shape(size, size).
- Returns:
np.ndarray โ Zero-mean Laplacian-of-Gaussian kernel.
- Raises:
ValueError โ If inputs are invalid.
- shinier.utils.tie_breaking_noise_level(image, min_gap=None, gap_frac_cap=0.001)๏
Return a numerically effective yet rank-safe noise amplitude for any dtype.
The returned noise is large enough to survive rounding of the input dtype (unit-in-the-last-place (ULP)-aware for floats, quantization-aware for uint-x) but small enough not to reorder distinct values. Used to ensure strict ranking in exact histogram specification when pixel ties are present.
- Parameters:
image (np.ndarray) โ Input image. Supported dtypes: uint8, uint16, float16, float32, float64.
min_gap (Optional[float]) โ Smallest nonzero intensity increment in the data (e.g., from np.diff(np.unique(image))). If provided, the returned noise is capped to a fraction of it.
gap_frac_cap (float) โ Fraction of min_gap allowed for noise (default 1e-3).
- Returns:
float โ Recommended amplitude of uniform tie-breaking noise (symmetric ยฑ)
- shinier.utils.print_log(logs, log_path, log_name=None)๏
Write log messages to a text file.
If no
log_nameis supplied, a timestamped filename is generated.- Parameters:
logs (List[str]) โ Log messages to write.
log_path (Union[Path, str]) โ Directory where the log file will be saved.
log_name (Optional[str], optional) โ Optional static text filename. If not specified, a timestamped filename is generated.
- Returns:
None
- shinier.utils.strip_ansi(s)๏
Remove ANSI escape sequences from a string.
- shinier.utils.colorize(text, color)๏
Return text wrapped in ANSI color codes.
- shinier.utils.console_log(msg, indent_level=0, color=None, verbose=True, strip=True)๏
Log a message to the console with optional indentation and color.
- Parameters:
msg (str) โ Message to log.
indent_level (int) โ Number of tab characters to prepend to each line.
color (Optional[str]) โ ANSI color code applied to the message.
verbose (bool) โ If True, print the message to the console.
strip (bool) โ If True, remove ANSI escape sequences from the returned string.
- Returns:
str โ Formatted message, optionally stripped of ANSI color codes.
- shinier.utils.beta_bounds_from_ssim(gradients, ssim, binary_mask=None)๏
Compute Avanakiโs step-size bounds for SSIM gradient ascent.
This computes lower and upper bounds for the scalar step size in an SSIM gradient-ascent update.
- Parameters:
gradients (np.ndarray) โ SSIM gradient map with shape
(H, W)or(H, W, C).ssim (List[float]) โ SSIM value for each channel.
binary_mask (Optional[np.ndarray], optional) โ Boolean mask. Pixels with False are frozen.
- Returns:
list[tuple[float, float]] โ Lower and upper step-size bounds for each channel.
Notes
Norms are computed over all pixels and channels after masking.
- shinier.utils.ssim_sens(image1, image2, data_range=None, use_sample_covariance=False, binary_mask=None)๏
Compute Structural Similarity Index (SSIM) and its gradient.
- Parameters:
image1 (np.ndarray) โ First image.
image2 (np.ndarray) โ Second image.
data_range (Optional[float], optional) โ Dynamic range of pixel values.
use_sample_covariance (bool, optional) โ If True, use sample covariance when computing SSIM.
binary_mask (Optional[np.ndarray], optional) โ Binary mask used to exclude pixels from SSIM averaging.
- Returns:
tuple[np.ndarray, np.ndarray] โ SSIM gradient map and per-channel mean SSIM values.
References
Avanaki, A. N. (2009). Exact global histogram specification optimized for structural similarity. Optical Review, 16, 613-621.
Wang, Z., Bovik, A. C., Sheikh, H. R., and Simoncelli, E. P. (2004). Image quality assessment: from error visibility to structural similarity. IEEE Transactions on Image Processing, 13(4), 600-612.
Notes
Intended to match scikit-image SSIM computations with
win_size=11and Gaussian weights when equivalent parameters are used.
- shinier.utils.hist_match_validation(images, binary_masks, target_hist=None, normalize_rmse=False)๏
Validate histogram matching with correlation and RMSE metrics.
Validates the histogram matching process by comparing image histograms to a target histogram. Uses correlation coefficients and root mean square error (RMSE) as metrics for validation.
- Parameters:
images (ImageListIO) โ Collection of images.
binary_masks (List[np.ndarray]) โ Binary masks with the same size as each image.
target_hist (Optional[np.ndarray], optional) โ Target histogram to compare against. If None, the average histogram is used.
normalize_rmse (bool, optional) โ If True, return normalized RMSE.
- Returns:
tuple[np.ndarray, np.ndarray] โ Correlation coefficients and RMSE values.
- shinier.utils.sf_match_validation(images, target_spectrum=None, normalize_rmse=False)๏
Validate spatial-frequency matching with correlation and RMSE metrics.
Validates spectral match between a set of input images by comparing their rotational averages of magnitude spectra against a computed target spectrum.
- Parameters:
images (ImageListIO) โ Collection of images.
target_spectrum (Optional[np.ndarray], optional) โ Target spectrum to compare against. If None, the average spectrum is used.
normalize_rmse (bool, optional) โ If True, return normalized RMSE.
- Returns:
tuple[np.ndarray, np.ndarray] โ Correlation coefficients and RMSE values.
- shinier.utils.spec_match_validation(images, target_spectrum=None, normalize_rmse=False)๏
Validate Fourier-spectrum matching with correlation and RMSE metrics.
Validates spectral matching of input images by comparing the spectra of each image with a target spectrum. The target spectrum is computed as the average magnitude spectrum of all input images. Computes both the correlation and root mean square error (RMSE) between the magnitude spectra of individual images and the target spectrum.
- Parameters:
images (ImageListIO) โ Collection of images.
target_spectrum (Optional[np.ndarray], optional) โ Target spectrum to compare against. If None, the average spectrum is used.
normalize_rmse (bool, optional) โ If True, return normalized RMSE.
- Returns:
tuple[np.ndarray, np.ndarray] โ Correlation coefficients and RMSE values.
- shinier.utils.compute_rmse(image1, image2, log=False)๏
Compute the root-mean-square error between two images.
- shinier.utils.normalized_rmse(y_true, y_pred, mode='actual range', expected_range=None, eps=1e-12, verbose=False)๏
Compute RMS error guaranteed to lie in [0, 1].
- Parameters:
y_true (np.ndarray) โ Reference array.
y_pred (np.ndarray) โ Predicted or reconstructed array with the same shape as
y_true.mode (str, optional) โ Normalization mode:
"assume [0, 1]","actual range","expected range", or"histogram".expected_range (float, optional) โ Known dynamic range when using
mode="expected range".eps (float, optional) โ Small constant to avoid divide-by-zero.
verbose (bool, optional) โ If True, warn when the normalized RMSE is outside
[0, 1].
- Returns:
float โ Normalized RMSE.
- Raises:
ValueError โ If shapes differ or
expected_rangeis missing when required.
- shinier.utils.get_images_spectra(images, magnitudes=None, phases=None, rescale=True, fft_padding_mode=0, fft_padding_value=300)๏
Compute Fourier spectra for a collection of images.
- Parameters:
images (ImageListIO) โ Collection of images.
magnitudes (Optional[ImageListIO], optional) โ Existing collection to receive computed magnitudes.
phases (Optional[ImageListIO], optional) โ Existing collection to receive computed phases.
rescale (bool, optional) โ If True, rescale each image to
[0, 1]before FFT.fft_padding_mode (int, optional) โ Spatial padding mode before FFT: 0 disabled, 1 reflect, 2 symmetric, 3 constant.
fft_padding_value (int, optional) โ Constant padding value in
[0, 255], or300for mean intensity.
- Returns:
tuple[ImageListIO, ImageListIO] โ Magnitudes and phases for all images.
- shinier.utils.rescale_image(image, target_min=0, target_max=1)๏
Rescale an image to a target numeric range.
- Parameters:
image (np.ndarray) โ Input image.
target_min (float, optional) โ Target minimum value in the output image.
target_max (float, optional) โ Target maximum value in the output image.
- Returns:
np.ndarray โ Rescaled image.
- shinier.utils.load_images_from_folder(folder_path)๏
Load all supported image formats from a folder as NumPy arrays.
This function searches a given folder for image files with supported extensions (e.g., PNG, JPEG, TIFF, BMP, WEBP, GIF) and loads them into memory as NumPy arrays. Non-image files are ignored. Any unreadable images are skipped with an error message.
- Parameters:
folder_path (str) โ Path to the folder containing images.
- Returns:
list[np.ndarray] โ Loaded images. Returns an empty list if the folder does not exist or no valid images are found.
- shinier.utils.load_np_array(path_str)๏
Load a NumPy array from a
.npyfile path.If the file path is not provided, does not exist, or is not a supported file type, a message is logged and
Noneis returned.- Parameters:
path_str (Optional[str]) โ Path to a
.npyfile.- Returns:
Optional[np.ndarray] โ Loaded array, or
None.
- shinier.utils.rescale_images255(images, rescaling_option=2, legacy_mode=False)๏
Rescale image values to the range [0, 255].
- Parameters:
images (ImageListIO) โ Collection of images to rescale.
rescaling_option (Literal[0, 1, 2, 3], optional) โ Rescaling strategy.
0disables rescaling,1rescales each image independently,2uses dataset absolute min/max, and3uses dataset average min/max.legacy_mode (bool, optional) โ If True, convert results with MATLAB-compatible uint8 behavior.
- Returns:
ImageListIO โ Rescaled images.
Notes
When rescaling is enabled, output values are mapped to
[0, 255].
- shinier.utils.uint8_plus(image, verbose=False)๏
Clip, round, and convert an image to
uint8.- Parameters:
image (np.ndarray) โ Image to convert. Values are expected to be in
[0, 255].verbose (bool, optional) โ If True, warn when clipping is needed.
- Returns:
np.ndarray โ Converted
uint8image.
- shinier.utils.apply_median_blur(image, kernel_size=3)๏
Apply a median blur to an image.
- Parameters:
image (np.ndarray) โ Input image, either 2D grayscale or 3D multi-channel.
kernel_size (int, optional) โ Size of the square kernel. Must be odd.
- Returns:
np.ndarray โ Blurred image.
Notes
Tested on
np.uint8images againstcv2.medianBlur.
- shinier.utils.hist2list(hist)๏
Convert a histogram into a sorted list of luminance values.
- Parameters:
hist (np.ndarray) โ Histogram counts of each intensity for each channel.
- Returns:
np.ndarray โ Sorted luminance values for each channel.
- shinier.utils.im3D(image)๏
Ensure that an image has an explicit channel dimension.
- Parameters:
image (np.ndarray) โ 2D or 3D image.
- Returns:
np.ndarray โ 3D image. Grayscale
(H, W)becomes(H, W, 1)and RGB images remain unchanged.
- shinier.utils.imhist(image, mask=None, n_bins=256, normalized=False)๏
Compute image histograms, one per channel.
- Parameters:
image (np.ndarray) โ Input image.
mask (Optional[np.ndarray], optional) โ Boolean mask. If provided, the histogram is computed only inside the mask.
n_bins (int, optional) โ Number of histogram bins.
normalized (bool, optional) โ If True, normalize each histogram so it sums to 1.
- Returns:
np.ndarray โ Histogram counts or probabilities for each channel.
- shinier.utils.rounded_target_hist(target_hist, n_pixels)๏
Convert an ideal target histogram into realizable probabilities.
The returned histogram is obtained by converting the target to integer bin counts that sum exactly to n_pixels, then normalizing those counts back to probabilities. This gives the closest target that an image with n_pixels pixels can actually realize.
- Parameters:
target_hist (np.ndarray) โ One-channel target probabilities or weights, shape
(n_bins,).n_pixels (int) โ Number of pixels available.
- Returns:
np.ndarray โ Rounded one-channel target histogram, shape
(n_bins,).
- shinier.utils.compute_tvd_hist(image_hist, target_hist, n_bins=256, round_target=False, n_pixels=None)๏
Compute Total Variation Distance (TVD) between two histograms.
TVD is defined as
0.5 * sum(abs(p_i - q_i))for two probability distributionspandq.- Parameters:
image_hist (np.ndarray) โ Histogram of the image.
target_hist (np.ndarray) โ Target histogram.
n_bins (int, optional) โ Number of bins in the histograms.
round_target (bool, optional) โ Whether to round the target histogram to the nearest realizable histogram given
n_pixels.n_pixels (Optional[int], optional) โ Number of pixels in the image.
- Returns:
float โ Total Variation Distance between the two histograms.
- shinier.utils.avg_hist(images, binary_masks, normalized=True, n_bins=256, output_list_hist=False)๏
Compute the average histogram of a set of images.
- Parameters:
images (ImageListIO) โ Collection of images.
binary_masks (List[np.ndarray]) โ Binary masks, one per image.
normalized (bool, optional) โ If True, normalize histograms so they sum to 1.
n_bins (int, optional) โ Number of histogram bins.
output_list_hist (bool, optional) โ If True, also return the histogram for each image.
- Returns:
Union[np.ndarray, tuple[np.ndarray, list[np.ndarray]]] โ Average histogram, optionally with per-image histograms.
CLI๏
- shinier.SHINIER.SHINIER_CLI(images=None, masks=None)๏
Interactive CLI to configure and run SHINIER processing.
- Parameters:
images (Optional[np.ndarray], optional) โ Optional in-memory image array. When provided, input-folder discovery is skipped and data is passed directly to
ImageDataset.masks (Optional[np.ndarray], optional) โ Optional in-memory mask array. When provided, mask-folder discovery is skipped and masks are passed directly to
ImageDataset.
- Returns:
ImageProcessor โ Configured and executed processor instance containing results, validation logs, and run metadata.
Notes
This function is interactive and prompts for processing choices unless equivalent data is injected through
imagesandmasks.Examples
Run interactive CLI:
>>> processor = SHINIER_CLI()
Run using in-memory arrays:
>>> processor = SHINIER_CLI(images=my_images, masks=my_masks)
Others๏
Chroma Loss Metrics๏
- class shinier.color.quantify_chroma_loss.ChromaMetrics๏
Bases:
objectChroma-related metrics for one image at one
y1value.Runtime Attributes
y1(float): Low-luminance threshold or tested Y value.affected_fraction(float): Fraction of pixels affected, in [0, 1].entropy_before_bpp(float): Chroma entropy before treatment, in bits per pixel.entropy_after_bpp(float): Chroma entropy after treatment, in bits per pixel.rel_entropy_loss_pct(float): Relative entropy loss, in percent.mean_chroma_before(float): Mean chroma before treatment.mean_chroma_after(float): Mean chroma after treatment.rel_mean_chroma_loss_pct(float): Relative mean chroma loss, in percent.delta_e76_mean(float): Mean CIE76 color difference.delta_e76_p95(float): 95th percentile CIE76 color difference.n_pixels_measured(int): Number of pixels included in the measurement.
- __init__(y1, affected_fraction, entropy_before_bpp, entropy_after_bpp, rel_entropy_loss_pct, mean_chroma_before, mean_chroma_after, rel_mean_chroma_loss_pct, delta_e76_mean, delta_e76_p95, n_pixels_measured)๏
- classmethod __new__(*args, **kwargs)๏
- class shinier.color.quantify_chroma_loss.AggregateMetric๏
Bases:
objectMean and standard deviation summary for a metric across images.
Runtime Attributes
mean(float): Mean value across images.std(float): Standard deviation across images.
- __init__(mean, std)๏
- classmethod __new__(*args, **kwargs)๏
- class shinier.color.quantify_chroma_loss.AggregateRow๏
Bases:
objectAggregate chroma metrics for one
y1value.Runtime Attributes
y1(float): Low-luminance threshold or tested Y value.affected_pct(AggregateMetric): Percentage of affected pixels.entropy_before_bpp(AggregateMetric): Entropy before treatment, in bits per pixel.entropy_after_bpp(AggregateMetric): Entropy after treatment, in bits per pixel.rel_entropy_loss_pct(AggregateMetric): Relative entropy loss, in percent.rel_mean_chroma_loss_pct(AggregateMetric): Relative mean chroma loss, in percent.delta_e76_mean(AggregateMetric): Mean CIE76 color difference.delta_e76_p95(AggregateMetric): 95th percentile CIE76 color difference.
- __init__(y1, affected_pct, entropy_before_bpp, entropy_after_bpp, rel_entropy_loss_pct, rel_mean_chroma_loss_pct, delta_e76_mean, delta_e76_p95)๏
- classmethod __new__(*args, **kwargs)๏
- class shinier.color.quantify_chroma_loss.ChromaInfoRetention๏
Bases:
objectInformation-theoretic chroma retention statistics.
Runtime Attributes
h_before_bpp(float): Chroma entropy before treatment, in bits per pixel.h_after_bpp(float): Chroma entropy after treatment, in bits per pixel.mi_bpp(float): Mutual information estimate, in bits per pixel.cir(float): Chroma information retention in [0, 1].
- __init__(h_before_bpp, h_after_bpp, mi_bpp, cir)๏
- classmethod __new__(*args, **kwargs)๏
- class shinier.color.quantify_chroma_loss.ChromaInfoLossResult๏
Bases:
objectEntropy-only chroma information loss result for one
y1value.This container is kept for backward compatibility with earlier chroma-loss analyses.
Runtime Attributes
y1(float): Low-luminance threshold or tested Y value.h_before_bpp(float): Chroma entropy before treatment, in bits per pixel.h_after_bpp(float): Chroma entropy after treatment, in bits per pixel.loss_bpp(float): Entropy loss in bits per pixel.frac_pixels_affected(float): Fraction of pixels affected, in [0, 1].n_pixels_measured(int): Number of pixels included in the measurement.
- __init__(y1, h_before_bpp, h_after_bpp, loss_bpp, frac_pixels_affected, n_pixels_measured)๏
- classmethod __new__(*args, **kwargs)๏
- shinier.color.quantify_chroma_loss.compute_chroma_metrics_for_image(srgb_01, *, y1, rec_standard='rec709', nbits_per_axis=8, ab_range=(-128.0, 127.0), measure_mask=None, shadow_only=False)๏
Compute chroma-loss metrics for a single image at one
y1value.Strategy: desaturate xy toward the D65 white point using a smoothstep strength s(Y_orig) applied on low luminance only, with Y_orig computed from the ORIGINAL image.
- Metrics:
affected_fraction
joint chroma entropy (Lab a*,b*) before/after, and relative loss
mean chroma C* before/after, and relative loss
ฮE76 mean and p95 (optionally within a measurement mask)
- Parameters:
srgb_01 (np.ndarray) โ sRGB image in [0, 1], shape (H, W, 3).
y1 (float) โ Threshold controlling the low-luminance desaturation range.
rec_standard (REC_STANDARD) โ Color standard:
"rec601","rec709", or"rec2020".nbits_per_axis (int) โ Quantization bits per Lab a* and b* axis for joint entropy.
ab_range (Tuple[float, float]) โ Clipping range for a* and b* before quantization.
measure_mask (Optional[np.ndarray]) โ Optional boolean mask with shape (H, W) restricting all measurements.
shadow_only (bool) โ If True, further restricts measurements to pixels where
Y_orig < y1.
- Returns:
ChromaMetrics โ Chroma metrics for the image and threshold.
- shinier.color.quantify_chroma_loss.aggregate_chroma_metrics(images_srgb_01, *, y1_values, rec_standard='rec709', nbits_per_axis=8, ab_range=(-128.0, 127.0), measure_mask=None, shadow_only=False)๏
Aggregate chroma-loss metrics across a list of images.
- Parameters:
images_srgb_01 (Sequence[np.ndarray]) โ Sequence of sRGB images in [0, 1], each with shape (H, W, 3).
y1_values (Union[np.ndarray, Iterable[float]]) โ Iterable of low-luminance thresholds.
rec_standard (REC_STANDARD) โ Color standard:
"rec601","rec709", or"rec2020".nbits_per_axis (int) โ Quantization bits per Lab a* and b* axis for entropy.
ab_range (Tuple[float, float]) โ Clipping range for a* and b* before quantization.
measure_mask (Optional[np.ndarray]) โ Optional boolean mask with shape (H, W) applied to every image.
shadow_only (bool) โ If True, measurements are restricted to pixels where
Y_orig < y1.
- Returns:
List[AggregateRow] โ One aggregate row per threshold.
- shinier.color.quantify_chroma_loss.generate_chroma_loss_report(images_srgb_01, *, y1_values, rec_standard='rec709', nbits_per_axis=8, ab_range=(-128.0, 127.0), graphics_dir=None, report_dir=None, report_filename='chroma_loss_report.md')๏
Generate a Markdown report and plots for chroma-loss vs
y1.- This function creates:
Aggregate report (mean ยฑ std across images)
Shadow-only report (measurement restricted to Y_orig < Y1)
Plots saved under: shinier/color/assets/graphics/ (by default)
- Parameters:
images_srgb_01 (Sequence[np.ndarray]) โ Sequence of sRGB images in [0, 1], each with shape (H, W, 3).
y1_values (Union[np.ndarray, Iterable[float]]) โ Iterable of low-luminance thresholds.
rec_standard (REC_STANDARD) โ Color standard:
"rec601","rec709", or"rec2020".nbits_per_axis (int) โ Quantization bits per Lab a* and b* axis for entropy.
ab_range (Tuple[float, float]) โ Clipping range for a* and b* before quantization.
graphics_dir (Optional[Union[str, Path]]) โ Output directory for plots. If None, uses the package graphics folder.
report_dir (Optional[Union[str, Path]]) โ Output directory for the Markdown report. If None, uses the package assets folder.
report_filename (str) โ Name of the Markdown report file.
- Returns:
Path โ Path to the generated Markdown report.
- shinier.color.quantify_chroma_loss.chroma_info_loss_bits_per_pixel_vs_y1(srgb_01, *, rec_standard='rec709', y1_values=(0.02, 0.05, 0.08, 0.1, 0.12), nbits_per_axis=8, ab_range=(-128.0, 127.0), measure_mask=None, low_y_only=False)๏
Compute legacy entropy-only chroma information loss vs
y1.Notes
Input must be float in [0, 1]. This function is strict and raises on uint8-style [0, 255] input.
For richer metrics, use
generate_chroma_loss_report.
ImageListIO๏
- class shinier.ImageListIO๏
Bases:
InformativeBaseModelManage an image collection with optional disk-backed memory conservation.
This class supports file-based and in-memory image collections and provides list-like access with validation, grayscale conversion, and controlled temporary storage.
Only
input_data,conserve_memory,as_gray, andsave_dirare user-provided attributes. The classic workflow of SHINIER doesnโt require users to interact with this class directly.- Parameters:
input_data (ImageListType) โ File pattern, list of file paths, or list of in-memory NumPy arrays.
conserve_memory (bool) โ If True, use a temporary directory as backing storage and keep only one image in memory at a time.
as_gray (Literal[0, 1, 2, 3, 4]) โ Grayscale conversion mode applied at load time.
0 = no conversion
1 =
equal-weight RGBaverage2 =
ITU-R BT.601weights3 =
ITU-R BT.709weights4 =
ITU-R BT.2020weights
save_dir (Optional[Path]) โ Output directory used by
final_save_all(). Defaults to the current working directory.
Runtime Attributes
- dataList[Optional[np.ndarray]]
In-memory pixel arrays. Entries are
Nonewhen an image has been evicted to backing storage (conserve_memory=True).- src_pathsList[Optional[Path]]
Original source file paths.
Nonefor images supplied as in-memory arrays.- store_pathsList[Optional[Path]]
Backing-store paths used for lazy loading. For file-backed collections these are the source files; for in-memory collections with
conserve_memory=Truethese point to temporary.npyfiles.- reference_sizeOptional[Tuple[int, int]]
(height, width)of the first image, used to enforce spatial consistency across the collection.- n_channelsOptional[int]
Number of color channels (1 for grayscale, 3 for RGB).
- n_dimsOptional[int]
Number of array dimensions (2 for grayscale, 3 for color).
- n_imagesint
Total number of images in the collection.
- dtypeOptional[np.dtype]
NumPy dtype of the collection, inferred from the first image.
- drangeOptional[Tuple[Any, Any]]
(min, max)dynamic range derived fromdtype. Set to(0, 1)for boolean, to the full integer range for integer dtypes (e.g.(0, 255)foruint8), and doesnโt assume anything for floating-point dtypes since no fixed range is assumed.- has_list_arraybool
Truewhen the collection was initialised from a list of NumPy arrays withconserve_memory=False.
Notes
Collection-level attributes (for example
dtype,n_dims,n_channels,reference_size) are established during initialization. After initialization, shape compatibility is enforced primarily throughreference_sizewhen replacing entries.Access and assignment contracts:
__getitem__validates spatial size and may cast values to the current floating collection dtype when required.__setitem__validates spatial size, accepts the new dtype as-is, and updates collection metadata accordingly.
- close()๏
Release temporary resources associated with this collection.
Notes
This operation is idempotent and safe to call multiple times.
- copy_with_image_list()๏
Return a new copy explicitly backed by an in-memory image list.
- Returns:
ImageListIO โ Collection copy initialized from concrete image arrays.
- final_save_all()๏
Save all images to
save_dirand clean temporary storage.Notes
In memory-conserving mode, images are loaded on demand during saving. After writing all outputs, the instance temporary directory is removed.
- static get_file_format(image_path)๏
Resolve the output format identifier from a file extension.
- Parameters:
image_path (Path) โ Path whose suffix determines the format.
- Returns:
str โ Pillow-compatible format string. Unknown extensions default to
"PNG".
- new_copy(to_list=False)๏
Create a new collection initialized from this instance.
- Parameters:
to_list (bool) โ If True, initialize the new instance from in-memory image arrays produced by
to_list(). If False, initialize from the originalinput_dataand then copy pixel data.- Returns:
ImageListIO โ New collection instance with copied content.
- readonly_copy()๏
Return a deep read-only clone of this collection.
- Returns:
ImageListIO โ Deep-copied instance configured as read-only.
Notes
The copy clears in-memory pixel arrays for memory efficiency and does not retain temporary staging or finalizer state from the original instance.
- to_gray()๏
Convert all images in-place to grayscale according to
as_gray.Notes
This method mutates the current collection.
- to_list()๏
Return all images as a Python list of NumPy arrays.
- Returns:
List[np.ndarray] โ Materialized image arrays in collection order.