Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting RGB to RGBW

I know this has already been asked, but the anwser given there doesn't work. I've spent over an hour looking for a formula or algorithm, but have found nothing. As a result, I've started writing my own algorithm to convert RGB to RGBW in the most efficient way possible. This is what I've currently got:

        //'Ri', 'Gi', and 'Bi' correspond to the Red, Green, and Blue inputs.
        var M = Math.Max(Ri, Math.Max(Gi, Bi)); //The maximum value between R,G, and B.
        int Wo =0; //White output
        int Ro=0; //Red output
        int Go=0; //Green output
        int Bo=0; //Blue output

        int av = 0; //Average between the two minimum values
        int hR = 0; //Red with 100% hue
        int hG = 0; //Green with 100% hue
        int hB = 0; //Blue with 100% hue

        //These 4 lines serve to figure out what the input color is with 100% hue.
        float multiplier = 255.0f / M;
        hR = Convert.ToInt32(Ri * multiplier);
        hG = Convert.ToInt32(Gi * multiplier);
        hB = Convert.ToInt32(Bi * multiplier);

        //Depending on the maximum value, get an average of the least used colors, weighted for their importance in the overall hue.
        //This is the problematic part
        if (M == Ri)
           av = (Bi*hB + Gi*hG) / (hB+hG);
        else if (M == Gi)
            av = (Ri*hR + Bi*hB) / (hR+hB);
        else if (M == Bi)
            av = (Gi*hG + Ri*hR) / (hG+hR);

        //Set the rgbw colors
        Wo = av;
        Bo = Bi - av;
        Ro = Ri - av;
        Go = Gi - av;
        if (Wo < 1) Wo = 0;
        if (Bo < 1) Bo = 0;
        if (Ro < 1) Ro = 0;
        if (Go < 1) Go = 0;
        if (Wo > 255) Wo = 255;
        if (Bo > 255) Bo = 255;
        if (Ro > 255) Ro = 255;
        if (Go > 255) Go = 255;

It works fine if the color I'm dealing with is a primary color, but not in any other case. What would make it work everywhere? Am I even on the right track?

EDIT: Here's a .gif of the issue I'm running into. the RGBW values are all the way at the bottom enter image description here

like image 497
user2950509 Avatar asked Oct 28 '16 19:10

user2950509


People also ask

How do you convert RGB to color?

Hex to RGB conversionGet the 2 left digits of the hex color code and convert to decimal value to get the red color level. Get the 2 middle digits of the hex color code and convert to decimal value to get the green color level.

Can HSV color be converted to RGB?

RGB = hsv2rgb( HSV ) converts the hue, saturation, and value (HSV) values of an HSV image to red, green, and blue values of an RGB image. rgbmap = hsv2rgb( hsvmap ) converts an HSV colormap to an RGB colormap.


2 Answers

I've finally figured out how to convert RGB to RGBW, turns out my previous method was completely wrong:

//Get the maximum between R, G, and B
float tM = Math.Max(Ri, Math.Max(Gi, Bi));

//If the maximum value is 0, immediately return pure black.
if(tM == 0)
   { return new rgbwcolor() { r = 0, g = 0, b = 0, w = 0 }; }

//This section serves to figure out what the color with 100% hue is
float multiplier = 255.0f / tM;
float hR = Ri * multiplier;
float hG = Gi * multiplier;
float hB = Bi * multiplier;  

//This calculates the Whiteness (not strictly speaking Luminance) of the color
float M = Math.Max(hR, Math.Max(hG, hB));
float m = Math.Min(hR, Math.Min(hG, hB));
float Luminance = ((M + m) / 2.0f - 127.5f) * (255.0f/127.5f) / multiplier;

//Calculate the output values
int Wo = Convert.ToInt32(Luminance);
int Bo = Convert.ToInt32(Bi - Luminance);
int Ro = Convert.ToInt32(Ri - Luminance);
int Go = Convert.ToInt32(Gi - Luminance);

//Trim them so that they are all between 0 and 255
if (Wo < 0) Wo = 0;
if (Bo < 0) Bo = 0;
if (Ro < 0) Ro = 0;
if (Go < 0) Go = 0;
if (Wo > 255) Wo = 255;
if (Bo > 255) Bo = 255;
if (Ro > 255) Ro = 255;
if (Go > 255) Go = 255;
return new rgbwcolor() { r = Ro, g = Go, b = Bo, w = Wo };

enter image description here

Any optimization ideas are more than welcome :)

like image 178
user2950509 Avatar answered Sep 25 '22 13:09

user2950509


I made an algorithm that takes into account the color temperature of your "white" LED (as this can vary based on the strip - mine was warm-white, which is 4500k). Gist is here, code is below, and blog post with more context is Ouch! My Eyes! Arduino WS2812B Setup & RGBW Transformation (Cabinet Light Pt. 3).

// Reference, currently set to 4500k white light:
// https://andi-siess.de/rgb-to-color-temperature/
const uint8_t kWhiteRedChannel = 255;
const uint8_t kWhiteGreenChannel = 219;
const uint8_t kWhiteBlueChannel = 186;

// The transformation has to be normalized to 255
static_assert(kWhiteRedChannel >= 255 ||
              kWhiteGreenChannel >= 255 ||
              kWhiteBlueChannel >= 255);

CRGBW GetRgbwFromRgb2(CRGB rgb) {
  uint8_t r = rgb.r;
  uint8_t g = rgb.g;
  uint8_t b = rgb.b;

  // These values are what the 'white' value would need to
  // be to get the corresponding color value.
  double whiteValueForRed = r * 255.0 / kWhiteRedChannel;
  double whiteValueForGreen = g * 255.0 / kWhiteGreenChannel;
  double whiteValueForBlue = b * 255.0 / kWhiteBlueChannel;

  // Set the white value to the highest it can be for the given color
  // (without over saturating any channel - thus the minimum of them).
  double minWhiteValue = min(whiteValueForRed,
                             min(whiteValueForGreen,
                                 whiteValueForBlue));
  uint8_t Wo = (minWhiteValue <= 255 ? (uint8_t) minWhiteValue : 255);

  // The rest of the channels will just be the original value minus the
  // contribution by the white channel.
  uint8_t Ro = (uint8_t)(r - minWhiteValue * kWhiteRedChannel / 255);
  uint8_t Go = (uint8_t)(g - minWhiteValue * kWhiteGreenChannel / 255);
  uint8_t Bo = (uint8_t)(b - minWhiteValue * kWhiteBlueChannel / 255);

  return CRGBW(Ro, Go, Bo, Wo);
}
like image 26
Daniel Murphy Avatar answered Sep 25 '22 13:09

Daniel Murphy