Yalantis/uCrop · error · CImgInstanceException

RGBtoHSV(): Instance is not a RGB image.

Error message

RGBtoHSV(): Instance is not a RGB image.

What it means

CImg<T>::RGBtoHSV() converts pixel values from RGB to the HSV color space and requires exactly 3 channels (R, G, B planes). Any other spectrum throws CImgInstanceException. Like the other color conversions it checks channel count only, not that the data truly encodes RGB.

Source

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

        case 4 : R = X; G = 0; B = C; break;
        default : R = C; G = 0; B = X;
        }
        p1[N] = (T)((R + m)*255);
        p2[N] = (T)((G + m)*255);
        p3[N] = (T)((B + m)*255);
      }
      return *this;
    }

    //! Convert pixel values from HSL to RGB color spaces \newinstance.
    CImg<Tuchar> get_HSLtoRGB() const {
      return CImg<Tuchar>(*this,false).HSLtoRGB();
    }

    //! Convert pixel values from RGB to HSV color spaces.
    CImg<T>& RGBtoHSV() {
      if (_spectrum!=3)
        throw CImgInstanceException(_cimg_instance
                                    "RGBtoHSV(): Instance is not a RGB 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) {
        const Tfloat
          R = (Tfloat)p1[N],
          G = (Tfloat)p2[N],
          B = (Tfloat)p3[N],
          M = cimg::max(R,G,B),
          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);

View on GitHub (pinned to f788b534b4)

Solutions

  1. Verify img.spectrum()==3; convert grayscale to 3 channels by replication.
  2. Drop alpha with channels(0,2) for RGBA input.
  3. Prefer get_RGBtoHSV() on a validated copy to keep the original intact.
  4. Standardize inputs to RGB at load time so later conversions can assume 3 channels.

Example fix

// before
CImg<unsigned char> img("logo.png"); // RGBA
img.RGBtoHSV(); // throws
// after
CImg<unsigned char> img("logo.png");
if (img.spectrum() == 4) img.channels(0,2);
if (img.spectrum() == 1) img.resize(-100,-100,-100,3);
img.RGBtoHSV();
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: Calling img.RGBtoHSV() on grayscale (spectrum=1), RGBA (spectrum=4), or other non-3-channel images.

Common situations: Grayscale thumbnails or masks; PNGs with alpha channel; images from formats that load as indexed or single-channel; pipelines where a prior op changed the spectrum.

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/b6e7117642cfe279. Report an issue: GitHub.