Yalantis/uCrop · error · CImgInstanceException

HSVtoRGB(): Instance is not a HSV image.

Error message

HSVtoRGB(): Instance is not a HSV image.

What it means

CImg<T>::HSVtoRGB() converts HSV values back to RGB and requires exactly 3 channels (H, S, V planes). Any other spectrum throws CImgInstanceException. It validates only the channel count, so 3-channel data that isn't HSV will convert silently but incorrectly.

Source

Thrown at ucrop/src/main/jni/CImg.h:37245

          C = M - cimg::min(R,G,B),
          H = 60*(C==0?0:M==R?cimg::mod((G-B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
          S = M<=0?0:C/M;
        p1[N] = (T)H;
        p2[N] = (T)S;
        p3[N] = (T)(M/255);
      }
      return *this;
    }

    //! Convert pixel values from RGB to HSV color spaces \newinstance.
    CImg<Tfloat> get_RGBtoHSV() const {
      return CImg<Tfloat>(*this,false).RGBtoHSV();
    }

    //! Convert pixel values from HSV to RGB color spaces.
    CImg<T>& HSVtoRGB() {
      if (_spectrum!=3)
        throw CImgInstanceException(_cimg_instance
                                    "HSVtoRGB(): Instance is not a HSV image.",
                                    cimg_instance);

      T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
      const longT whd = (longT)width()*height()*depth();
      cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
      for (longT N = 0; N<whd; ++N) {
        Tfloat
          H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
          S = (Tfloat)p2[N],
          V = (Tfloat)p3[N],
          C = V*S,
          X = C*(1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1)),
          m = V - C;
        Tfloat R, G, B;
        switch ((int)H) {
        case 0 : R = C; G = X; B = 0; break;
        case 1 : R = X; G = C; B = 0; break;

View on GitHub (pinned to f788b534b4)

Solutions

  1. Ensure spectrum()==3 before calling (replicate grayscale channels; strip alpha).
  2. Assert img.spectrum()==3 between the forward and inverse conversions.
  3. Audit intermediate steps (save/load, resize with spectrum change) that could alter the channel count.
  4. If the image is grayscale it needs no HSV round trip — skip the conversion instead of forcing channels.

Example fix

// before
CImg<float> hsv = rgb.get_RGBtoHSV();
hsv = hsv.get_channel(2); // kept only V
hsv.HSVtoRGB(); // throws
// after
CImg<float> hsv = rgb.get_RGBtoHSV(); // keep all 3 channels
hsv.HSVtoRGB(); // ok
Defensive patterns

Strategy: type-guard

Validate before calling

template<typename T> bool is3Channel(const CImg<T>& img) { return img.spectrum() == 3; }
if (is3Channel(img)) img.HSVtoRGB();

Type guard

template<typename T> bool isHsvShape(const CImg<T>& img) { return img.spectrum() == 3; }

Try / catch

try {
  img.HSVtoRGB();
} catch (const CImgInstanceException& e) {
  std::fprintf(stderr, "HSVtoRGB requires 3 channels, image has %d\n", img.spectrum());
}

Prevention

When it happens

Trigger: Calling img.HSVtoRGB() on grayscale (spectrum=1) or RGBA (spectrum=4) images, or after channel-count changed between RGBtoHSV() and HSVtoRGB().

Common situations: Round-trip broken by grayscale save/load or alpha-channel operations; applying HSVtoRGB to raw single-channel hue or brightness buffers.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of Yalantis/uCrop@f788b534b4 (2026-09-08). Data as JSON: /api/errors/2e63be88ffd30d61. Report an issue: GitHub.