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: InformativeBaseModel

Class to hold SHINIER processing options.

Subsections๏ƒ

  1. INPUT/OUTPUT images folders

    input_folder, output_folder

  2. MASKS and FIGURE-GROUND separation

    masks_folder, whole_image, background

  3. SHINIER MODE

    mode, legacy_mode, seed, iterations

  4. Grayscale / color

    as_gray, linear_luminance, rec_standard, gamut_strategy

  5. Dithering / Memory

    dithering, conserve_memory

  6. LUMINANCE matching

    safe_lum_match, target_lum

  7. HISTOGRAM matching

    hist_optim, hist_specification, hist_iterations, target_hist

  8. FOURIER matching

    rescaling, target_spectrum, fft_padding_mode, fft_padding_value

  9. 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 != background pixel 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:

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 = False

  • as_gray = 1

  • dithering = 0

  • hist_specification = 1

  • safe_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 (with hybrid or noise tie-breaking strategies). If None, 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_luminance is False:

      computes non-linear grayscale images by applying the perceptual luma weights from the specified rec_standard.

    • When linear_luminance is True:

      computes linear grayscale images by averaging the RGB channels (simple mean(RGB)).

  • 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 is constrain_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, numpy safe_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_data is a list of NumPy arrays, images are first saved as .npy in 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:

  • mean must be in [0, 255] or None.

  • std must be >= 0 or None.

Semantics:

  • 0 uses a dataset-level automatic target for that statistic.

  • None preserves 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. 0 or None) 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).

  • 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.

  • 3 = Gaussian: Coltuc tie-breaking strategy with gaussian filters.

    Adaptive amount of gaussian filters used (min 5, max 7; deterministic local ordering).

  • 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.

Set to None if hist_optim is True. See hist_optim for 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', or None. Default is None.

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 with linear_luminance=True, as_gray=False).

    • See imhist in utils.py to 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 is None.

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).

    • See image_spectrum in utils.py to 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. 300 means: use the mean intensity of the current normalized image. Default is 300.

If 300, the mean intensity of the current normalized image is used. Used only when fft_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: InformativeBaseModel

Class to load and manage a collection of images and masks, keeping track of their state throughout image processing.

Only images, masks, and options are expected as participant/user inputs. For normal usage, only options should be set by the user.

Parameters:
  • images (ImageListType) โ€“ List of images. If not provided, images are loaded from input_folder as defined in Options.

  • masks (ImageListType) โ€“ List of masks, each specifying the image regions to include in processing. If not provided, masks are loaded from masks_folder as defined in Options.

  • options (Optional[Options]) โ€“ Instance of the Options class. If not provided, Options is 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: InformativeBaseModel

Process 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 dataset and options are expected as participant/user inputs. The functions are not meant to be called directly by users. See the demos.md for example usage, documentation.md for more details, and the README.md for 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 ImageListIO container 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_hist and is matched with an exact histogram specification routine on 8-bit bins (n_bins = 256).

Tie handling is selected by options.hist_specification:

  • 1 = noise

  • 2 = moving-average

  • 3 = gaussian

  • 4 = 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_optim is enabled, the method performs additional SSIM guided sub-iterations using sensitivity maps and adaptive step-size control. The number of optimization iterations comes from options.hist_iterations (implemented as hist_iterations + 1 so the last pass remains an exact histogram projection). The random seed set in process is 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=False and as_gray=False), histogram matching is applied to the luminance channel only and chromaticity is restored later. With linear_luminance=True and as_gray=False, matching is done independently per RGB channel. With as_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_hist is 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: 0 and None.

  • 0 requests a dataset-level automatic target for that statistic (for example, (0, 20) uses one automatic mean for the dataset and requests a standard deviation of 20);

  • None requests preservation of each imageโ€™s original statistic (for example, (None, 20) keeps each imageโ€™s original mean and requests a standard deviation of 20).

When safe_lum_match is 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 mean 50 and adjusts the standard deviation first, whereas (0, 50) keeps standard deviation 50 and adjusts the mean first.

Notes

  • The operation works on the processed buffer after the color treatment selected by options.as_gray and options.linear_luminance.

  • Clipping should happen before uint8 conversion because NumPy wraps out-of-range values when casting to uint8.

  • 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.mode and options.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 by options.as_gray, options.linear_luminance, options.rec_standard, and options.gamut_strategy. Final conversion to uint8 is handled by options.dithering and can use noisy-bit or Floyd-Steinberg dithering.

Notes

legacy_mode switches several sub-steps to MATLAB-compatible behavior.

conserve_memory and 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_mode and options.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_spectrum and 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 by options.fft_padding_mode and options.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 modes 5 and 7 and is readjusted when self._is_last_operation through 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_spectrum while 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 modes 6 and 8.

  • 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_mode and options.fft_padding_value.

  • Output values are often outside [0, 1] in intermediate steps and are readjusted when self._is_last_operation through clipping and optional rescaling.

Color Processing๏ƒ

class shinier.color.ColorConverter๏ƒ

Bases: InformativeBaseModel

Encapsulates color-space conversions for Rec.601/709/2020 systems.

This class implements reversible conversion pipelines:

  • sRGB -> linRGB -> XYZ -> CIELAB

  • CIELAB -> 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

  • rec2020 uses the ITU-R BT.2020 inverse OETF.

  • rec709 uses the IEC 61966-2-1 sRGB transfer function.

  • rec601 uses 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, and Y.

  • safe_mode (bool) โ€“ If True, replaces near-zero y values with 1.0 to avoid numerical explosions. If False, preserves tiny y values 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: ColorConverter

Apply 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_other but 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_other but it is not provided.

class shinier.color.GamutControl๏ƒ

Bases: InformativeBaseModel

Manage gamut constraints and repairs for image datasets.

GamutControl implements robust gamut management for pipelines that manipulate luminance and chromaticity separately, for example processing Y while preserving xy in 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๏ƒ

clip

Rely on the converterโ€™s clipping/safe-mode behavior only, without global constraint estimation.

constrain_image_luminance

For each image independently, compute a luminance scaling factor so reconstructed linear RGB stays mostly within bounds, then scale Y. Chromaticity is preserved.

constrain_dataset_luminance

Compute a single global luminance scaling factor from the whole dataset and apply it to every image, preserving relative luminance structure across images.

constrain_image_chrominance

For each image independently, compute a per-image chroma scaling factor by desaturating xy toward the adapting white-point while keeping Y unchanged.

constrain_dataset_chrominance

Compute one global chroma scaling factor for the dataset and apply it to all images by desaturating xy toward the adapting white-point.

Safeguards๏ƒ

[S1] Low-luminance preventive desaturation

In xyY, chromaticity becomes ill-conditioned near Y = 0 because x and y are normalized by X + Y + Z. Small numerical noise can produce unstable colors, so low-luminance pixels can be gently desaturated toward the white-point chromaticity while keeping Y unchanged. Implemented in apply_low_Y_desaturation.

[S2] Chroma validity mask

Excludes ill-conditioned or physically implausible chromaticities, such as y near 0 or x + y slightly above 1, from driving constraint estimation. These pixels may still be repaired later via clipping or safeguards.

[S3] Reliability weighting by luminance

Pixels near Y = 0 and Y = 1 are down-weighted when estimating global factors because tiny xy noise at luminance extremes can produce unrealistically large RGB excursions. Mid-tones therefore dominate the estimate.

[S4] Allowed outlier clipping

Optionally 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 ax is None.

  • dpi (int) โ€“ Matplotlib figure DPI. Used only when ax is 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: object

Create 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 float64 in [0, 1].

mask()๏ƒ

Generate mask as float64 in [0, 1].

MatlabOperators๏ƒ

class shinier.utils.MatlabOperators๏ƒ

Bases: object

Replicate 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 -> 1 and -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_round implementation, 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 float64 and values scaled to [0, 1].

Notes

If the array has constant values, returns an array of zeros to avoid division by zero. Output is float64 for 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 0 for vertical and 1 for 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_kernels kernels.

  • 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:
  1. Direct mapping assuming no isoluminant pixel (no ties): โ€˜noneโ€™

  2. Coltucโ€™s ordering based on filter bank: โ€˜moving-averageโ€™ or โ€˜gaussianโ€™.

  3. Noise-based tie-breaking: โ€˜noiseโ€™.

  4. 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 exceed max_percent. If naive clipping would clip more than max_percent of values, the function rescales the array to reduce clipping until the clipped proportion is approximately max_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_clipping is 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_size is invalid, or if apply_clipping is 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 x and y coordinates.

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 300 to 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], or 300 for 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 sigma or coverage must be provided.

If coverage is specified, sigma is automatically computed so that the specified fraction of the Gaussianโ€™s total area is contained within the kernel support of given size.

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 with sigma.

  • n_dim (int, optional) โ€“ Dimensionality of the kernel. Use 1 for shape (size,) or 2 for shape (size, size).

Returns:

np.ndarray โ€“ Normalized Gaussian kernel.

Raises:

ValueError โ€“ If inputs are invalid or if both/neither of sigma and coverage are 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_center or coverage must be provided.

If coverage is provided, sigma_center is chosen so that the specified fraction of the 1D Gaussianโ€™s total area lies within the half-width size // 2. sigma_surround is then derived as ratio * 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 1 for shape (size,) or 2 for 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 sigma or coverage must be provided.

If coverage is given, sigma is chosen so the specified fraction of the 1D Gaussian area lies within half-width size // 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 1 for shape (size,) or 2 for 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_name is 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=11 and 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_range is 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], or 300 for 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 .npy file path.

If the file path is not provided, does not exist, or is not a supported file type, a message is logged and None is returned.

Parameters:

path_str (Optional[str]) โ€“ Path to a .npy file.

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. 0 disables rescaling, 1 rescales each image independently, 2 uses dataset absolute min/max, and 3 uses 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 uint8 image.

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.uint8 images against cv2.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 distributions p and q.

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 images and masks.

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: object

Chroma-related metrics for one image at one y1 value.

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: object

Mean 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: object

Aggregate chroma metrics for one y1 value.

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: object

Information-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: object

Entropy-only chroma information loss result for one y1 value.

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 y1 value.

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: InformativeBaseModel

Manage 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, and save_dir are 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 RGB average

    • 2 = ITU-R BT.601 weights

    • 3 = ITU-R BT.709 weights

    • 4 = ITU-R BT.2020 weights

  • 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 None when an image has been evicted to backing storage (conserve_memory=True).

src_pathsList[Optional[Path]]

Original source file paths. None for 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=True these point to temporary .npy files.

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 from dtype. Set to (0, 1) for boolean, to the full integer range for integer dtypes (e.g. (0, 255) for uint8), and doesnโ€™t assume anything for floating-point dtypes since no fixed range is assumed.

has_list_arraybool

True when the collection was initialised from a list of NumPy arrays with conserve_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 through reference_size when 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_dir and 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 original input_data and 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.