Description
While testing broadcasting behavior across the deltaE_* functions, I ran into a crash in deltaE_ciede2000 when lab1 and lab2 don't have the same number of dimensions — for example, comparing a whole image of colors against a single reference color. The other functions (deltaE_cie76, deltaE_ciede94, deltaE_cmc) handle this fine, so it looks specific to deltaE_ciede2000.
This isn't limited to the dev _skimage2 module — the same buggy pattern is present in the current released skimage.color.deltaE_ciede2000 as well, so this affects real users on the latest stable release too.
What's going on:
channel_axis = channel_axis % lab1.ndim
This normalizes channel_axis using only lab1's number of dimensions, and then the same value gets reused for lab2 too when calling np.moveaxis a few lines down — which breaks if lab2 doesn't have that many dimensions to begin with.
The other three functions don't do this pre-normalization step at all — they just pass channel_axis straight into np.moveaxis, and numpy normalizes it separately for each array based on its own shape. That's why they don't have this problem.
Worth noting: the original PR that added these functions (#665) explicitly says broadcasting mismatched shapes should work (x.shape == (3,) vs y.shape == (10, 10, 3)), so this looks like an unintentional regression introduced when channel_axis was added later, not a deliberate restriction.
I already have a fix worked out (normalize the axis separately for each array instead of sharing one value) and it passes against this case, a few other shape combos, and the existing 29 tests in test_delta_e.py. Happy to open a PR if that sounds good.
Way to reproduce
import numpy as np
from skimage.color import deltaE_cie76, deltaE_ciede94, deltaE_ciede2000
lab1 = np.array([[50., 20., 30.], [60., -10., 15.]]) # image: 2 colors, shape (2, 3)
lab2 = np.array([55., 18., 28.]) # single reference color, shape (3,)
deltaE_cie76(lab1, lab2) # works fine -> array([ 5.74456265, 31.27299154])
deltaE_ciede94(lab1, lab2) # works fine -> array([ 5.12397703, 23.28484273])
deltaE_ciede2000(lab1, lab2) # AxisError: source: axis 1 is out of bounds for array of dimension 1
Swapping the order (lab1 as the single color, lab2 as the image) fails too, but with a different error: ValueError: not enough values to unpack (expected 3, got 2).
Version information
3.14.4 (main, Jun 18 2026, 14:25:02) [GCC 15.2.0]
scikit-image version: 0.26.1rc0.dev0+git20260909.b6e9c5ce5
numpy version: 2.5.3
scipy version: 1.18.1
Description
While testing broadcasting behavior across the
deltaE_*functions, I ran into a crash indeltaE_ciede2000whenlab1andlab2don't have the same number of dimensions — for example, comparing a whole image of colors against a single reference color. The other functions (deltaE_cie76,deltaE_ciede94,deltaE_cmc) handle this fine, so it looks specific todeltaE_ciede2000.This isn't limited to the dev
_skimage2module — the same buggy pattern is present in the current releasedskimage.color.deltaE_ciede2000as well, so this affects real users on the latest stable release too.What's going on:
This normalizes
channel_axisusing onlylab1's number of dimensions, and then the same value gets reused forlab2too when callingnp.moveaxisa few lines down — which breaks iflab2doesn't have that many dimensions to begin with.The other three functions don't do this pre-normalization step at all — they just pass
channel_axisstraight intonp.moveaxis, and numpy normalizes it separately for each array based on its own shape. That's why they don't have this problem.Worth noting: the original PR that added these functions (#665) explicitly says broadcasting mismatched shapes should work (
x.shape == (3,)vsy.shape == (10, 10, 3)), so this looks like an unintentional regression introduced whenchannel_axiswas added later, not a deliberate restriction.I already have a fix worked out (normalize the axis separately for each array instead of sharing one value) and it passes against this case, a few other shape combos, and the existing 29 tests in
test_delta_e.py. Happy to open a PR if that sounds good.Way to reproduce
Swapping the order (
lab1as the single color,lab2as the image) fails too, but with a different error:ValueError: not enough values to unpack (expected 3, got 2).Version information