BradLarson / GPUImage

An open source iOS framework for GPU-based image and video processing
http://www.sunsetlakesoftware.com/2012/02/12/introducing-gpuimage-framework
BSD 3-Clause "New" or "Revised" License
20.23k stars 4.61k forks source link

Hue Filter needed #276

Closed iamcam closed 12 years ago

iamcam commented 12 years ago

There is some expressed need, including 274, for a hue filter, as in Photoshop and other image editing apps.

BradLarson commented 12 years ago

It would really help if someone would find a working calculation that does this. That last few I tried were rejected by people because they didn't match Core Image's hue adjustments, so I gave up. Frankly, I don't do much work on the color correcting side so I'm going to need some help here.

iamcam commented 12 years ago

I saw this one the other day but couldn't figure out how to get it to compile. I have no doubt that somebody who understands GLSL could get it in no time. http://stackoverflow.com/a/9234854/694080

Edit Never mind, I fiddled enough with it and didn't realize I mis-read the post. I was able to get the shader working after some time, but the results don't seem to be correct. I'll see if i can dig anything up.

iamcam commented 12 years ago

I think i'm going to pass on figuring this out. The above SO answer gets you close, but not quite all the way simply by expanding the precision of the float constants at the top (ref'd from wikipedia). Note it says approximate, which this is.

@BradLarson How approximate is acceptable enough to make it into a filter?


      const highp  vec4  kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0);
      const highp  vec4  kRGBToI     = vec4 (0.595716, -0.274453, -0.321263, 0.0);
      const highp  vec4  kRGBToQ     = vec4 (0.211456, -0.522591, 0.31135, 0.0);
       const highp  vec4  kYIQToR   = vec4 (1.0, 0.9563, 0.6210, 0.0);
      const highp  vec4  kYIQToG   = vec4 (1.0, -0.2721, -0.6474, 0.0);
      const highp  vec4  kYIQToB   = vec4 (1.0, -1.1070, 1.7046, 0.0);

I also converted the functions over here but my output looked exactly like the the source, no matter how much i adjusted the hue. See the code below - not sure what's going on there. I believe everything is in the [0-1] range, so I should be seeing something.

NSString *const kGPUImageHueFragmentShaderString = SHADER_STRING
(

 varying highp vec2 textureCoordinate;

 uniform sampler2D inputImageTexture;
 uniform lowp float hueAdjust;

 void main ()
 {
     // Sample the input pixel
     mediump vec4 color   = texture2D(inputImageTexture, textureCoordinate);

     //Make HSL from RGB
     highp float r = color.r;
     highp float g = color.g;
     highp float b = color.b;
     highp float a = color.a;

     highp float maxValA = max(r, g);
     highp float maxVal = max(maxValA,b);

     highp float minValA = min(r, g);
     highp float minVal = min(minValA, b);

     highp float h = (maxVal + minVal) / 2.0;
     highp float s = (maxVal + minVal) / 2.0;
     highp float l = (maxVal + minVal) / 2.0;

     if(maxVal == minVal){
         h = s = 0.0; // achromatic
     }else{
         highp float d = maxVal - minVal;
         s = l > 0.5 ? d / (2.0 - maxVal - minVal) : d / (maxVal + minVal);
         if(maxVal == r){
                 h = (g - b) / d + (g < b ? 6.0 : 0.0);
         } else if(maxVal == g){
                 h = (b - r) / d + 2.0;
         } else if( maxVal == b){
                 h = (r - g) / d + 4.0; 
         }
         h /= 6.0;
     }

     // Make the user's adjustments
     h += hueAdjust;

     if(s == 0.0){
         r = g = b = l; // achromatic
     }else{
         highp float q;
         if(l < 0.5){
             q = l * (1.0 + s);
         } else {
             q =  l + s - l * s;
         }
         highp float p = 2.0 * l - q;         

         highp float t = 0.0;

//         //function hue2rgb(p, q, t){
//         // RED
         t =  h + float(1/3);
         if(t < 0.0) t += 1.0;
         if(t > 1.0) t -= 1.0;
         if(t < float(1/6)) r = p + (q - p) * 6.0 * t;
         if(t < float(1/2)) r = q;
         if(t < float(2/3)) r = p + (q - p) * (float(2/3) - t) * 6.0;

         // GREEN
         t = h;
         if(t < 0.0) t += 1.0;
         if(t > 1.0) t -= 1.0;
         if(t < float(1/6)) g = p + (q - p) * 6.0 * t;
         if(t < float(1/2)) g = q;
         if(t < float(2/3)) g = p + (q - p) * (float(2/3) - t) * 6.0;

         //BLUE
         t = h - float(1/3);
         if(t < 0.0) t += 1.0;
         if(t > 1.0) t -= 1.0;
         if(t < float(1/6)) b= p + (q - p) * 6.0 * t;
         if(t < float(1/2)) b= q;
         if(t < float(2/3)) b= p + (q - p) * (float(2/3) - t) * 6.0;
     }
     color.r = r;
     color.g = g;
     color.b = b;
     highp vec4 newColor = vec4(r,g,b,a);

     color.a = a;

     // Save the result
     gl_FragColor = newColor; // NOTHING!?!?!? UGH
 }
 );
BradLarson commented 12 years ago

Even if you have something approximate, send me a pull request for it and I'll roll it in. It's better to have something that's close than nothing at all.

iamcam commented 12 years ago

I'm on the fence. A few of the samples looked fairly close, but when I started comparing other images it looks farther off. I'll fiddle with it a little bit more, then I'll post it. Worse come to worse it'll just take a little bit of fudging to get the desired effect. Maybe somebody else has an idea.

iamcam commented 12 years ago

Using the updated code in the original SO question gave more accurate shifts, BUT it also tinted the entire frame, including whites... unacceptable. I couldn't quite figure out why it was shifting everything. Maybe somebody could figure it out, because I think it has potential to be a better calculation.

I'm done trying different algos, so I've posted a pull request for the accepted SO answer. Pull Req #289

michaelloistl commented 12 years ago

@iamcam Would it be possible to add also a HueBlendfilter now where you have been able to add the HueFilter to the framework. Would be a great help ;-)

liovch commented 12 years ago

@iamcam Here's a filter based on GIMP source code: https://gist.github.com/3176346 As far as I can see it works exactly like GIMP's Hue-Saturation filter (GIMP 2.8.0 Windows).

I'm not sure if GIMP license is compatible with GPUImage, probably not. So I won't push this code into GPUImage until I or someone else rewrites it.

BradLarson commented 12 years ago

No, any GPL code is not compatible with this framework. I've explicitly avoided pulling anything from GIMP for that reason. A cleanroom reimplementation would be needed.

iamcam commented 12 years ago

Ugh. I didn't realize gimp was GPL.