7yl4r
7yl4r

Reputation: 5348

Trouble with HSI->RGB conversion

I have some strange issue when trying to apply a conversion from HSI to RGB which I am trying to do as part of a homework assignment. To start off, here is my original image:

original image (sorry about the links, I don't have the rep to post images)

I am converting from an image with H,S,I values (0-255). Here is a display of that image in RGB colorspace (Hue=red, sat=green, inten=blue):

HSI src image

To me it looks about right... the white flowers (high intensity) are blue, the hues (red) seem to change with the different colored flowers.

So now I do some equalization on the intensity channel (blue in display) in some boxes, resulting in this:

HSI equalized image: http://i.imgur.com/hgk9K.png ((you must copypasta these 'cause I don't have rep for >2 hyperlinks))

No big deal there. It doesn't look exactly right, but it's hard to say and everything outside the region-of-interest boxes is the same.

Now here comes the problem. After converting this back to RGB I get this result:

converted image: http://i.imgur.com/6wEyw.png

No idea what the problem is here, the green looks okay, but some red/blue pixels seem to be maxed. The three boxes are there as expected, and they're contents seem pretty messed, but that's not the bigger issue here since that could be from my equalization function. Everything outside of those boxes should now be nearly identical to the original image, but somehow it has been jumbled.

I've gone over my code many times and added extra parentheses and data type casts just to be sure, but still can't find the issue. I am confident my formulas are correct, but I must be missing some problem in the way the pixel values are being calculated.

Here is the code I am using to convert from HSI to RGB. It is possible the error is outside of this method, but all the functions used here have been tested elsewhere and seem to work fine.

void colorSpace::HSItoRGB(image &src, image &tgt){
    cout<<"HSI->RGB\n";
    tgt.resize(src.getNumberOfRows(),src.getNumberOfColumns());
    float pi = 3.14159265358979f;
    for (int i=0; i<src.getNumberOfRows(); i++){    //for each pixel
        for (int j=0; j<src.getNumberOfColumns(); j++){
            //re-normalize h,s,i
            float h = ((float)src.getPixel(i,j,H))*pi*2.0f/255.0f;//255/2 instead of 180
            float s = ((float)src.getPixel(i,j,S))/255.0f;//255 instead of 100
            float in= ((float)src.getPixel(i,j,I))/255.0f;
            //compute x y z
            float x = in*(1.0f-s);
            float y = in*( 1.0f + (s*cos(h) / cos(pi/3.0f-h)) );
            float z = 3.0f*in-(x+y);
            float r,g,b;    //set rgb
            if(h<(2.0f*pi/3.0f)){
                b = x;
                r = y;
                g = z;
            }else if(h<(4.0f*pi/3.0f)){//&&2pi/3<=h
                r = x; 
                g = y;
                b = z;
            }else{  //less than 2pi && 4pi/3<=h
                g = x;
                b = y;
                r = z;
            }
            //convert normalized rgb to 0-255 range
            int rr = (int)round(r*255.0f);
            int gg = (int)round(g*255.0f);
            int bb = (int)round(b*255.0f);
            tgt.setPixel(i,j,RED,rr);
            tgt.setPixel(i,j,GREEN,gg);
            tgt.setPixel(i,j,BLUE,bb);
        }
    }
}

Does anybody see any trouble in the code or have any insights from looking at the images?

Upvotes: 1

Views: 1474

Answers (2)

Sm0k3Scr33n
Sm0k3Scr33n

Reputation: 129

The basic idea here is the shift from 0 - 360 on the hue value which will result in rgb values. We also have saturation which is a value from 0.00 to 1.00 or anything in between and also the intensity which is also 0.00 to 1.00.

enter image description here

 ///I use this
// the function result will be the values of the array rgb[3] and will be the  rgb values 0-255

///float H is values 0-360 because there are 360 degrees of color in hsi colorspace
///float S is 0.00 - 1.00 and aything in between
///float I is 0.00 - 1.00 and aything in between
///The input to our function is going to be hsi_to_rgb (Hue, Saturation, Intensity(brightness))

int rgb[3]; ///number of channels rgb = 3

void hsi_to_rgb(float H, float S, float I) {
  int r, g, b;
  if (H > 360) {
    H = H - 360;
  }
  H = fmod(H, 360); // cycle H around to 0-360 degrees
  H = 3.14159 * H / (float)180; // Convert to radians.
  S = S > 0 ? (S < 1 ? S : 1) : 0; // clamp S and I to interval [0,1]
  I = I > 0 ? (I < 1 ? I : 1) : 0;
  if (H < 2.09439) {
    r = 255 * I / 3 * (1 + S * cos(H) / cos(1.047196667 - H));
    g = 255 * I / 3 * (1 + S * (1 - cos(H) / cos(1.047196667 - H)));
    b = 255 * I / 3 * (1 - S);
  } else if (H < 4.188787) {
    H = H - 2.09439;
    g = 255 * I / 3 * (1 + S * cos(H) / cos(1.047196667 - H));
    b = 255 * I / 3 * (1 + S * (1 - cos(H) / cos(1.047196667 - H)));
    r = 255 * I / 3 * (1 - S);
  } else {
    H = H - 4.188787;
    b = 255 * I / 3 * (1 + S * cos(H) / cos(1.047196667 - H));
    r = 255 * I / 3 * (1 + S * (1 - cos(H) / cos(1.047196667 - H)));
    g = 255 * I / 3 * (1 - S);
  }
  //set the output to the array
  rgb[0] = r;
  rgb[1] = g;
  rgb[2] = b;

}

Upvotes: 2

digitalvision
digitalvision

Reputation: 2622

The h values need to be adjusted for the three cases prior to calculating the cos values dependent on them. i.e.:

        if(h<(2.0f*pi/3.0f)){
            y = in*( 1.0f + (s*cos(h) / cos(pi/3.0f-h)) );
            b = x;
            r = y;
            g = z;
        }else if(h<(4.0f*pi/3.0f)){//&&2pi/3<=h
            h -= 2*pi/3;
            y = in*( 1.0f + (s*cos(h) / cos(pi/3.0f-h)) );
            r = x; 
            g = y;
            b = z;
        }else{  //less than 2pi && 4pi/3<=h
            h -= 4*pi/3;
            y = in*( 1.0f + (s*cos(h) / cos(pi/3.0f-h)) );
            g = x;
            b = y;
            r = z;
        }

You will also need to make sure that none of the rr,gg,bb values end up outside the 0..255 interval:

if (rr < 0) rr = 0;
if (rr > 255) rr = 255;

... etc

Upvotes: 1

Related Questions