Blend results in LIGHTEST fashion

edited October 2015 in GLSL / Shaders

I use a shader to select a color.

Here it is for blueish for example: Screen Shot 2015-10-07 at 10.44.06 AM

Here for redish: Screen Shot 2015-10-07 at 10.44.28 AM

Now I wan't to blend the 2 in a LIGHTEST kind of fashion. I tried blend(img, 0, 0, 512, 512, 0, 0, 512, 512, LIGHTEST); but as expected this doesn't work. Even worse, it makes crash JRE a lot it seems! (Is this a processing bug?).

What would be a good way to blend those results? I'm looking for a solution that is scalable (so also use the color select for 7 colors for example).

If possible: -a fast solution (so gpu prefered) -no edits to the current shader (if that makes sense to do)

PImage img;
PShader shader;


void settings() {
  size(1512, 1000, P2D);
}


float[] targetColorRGB = new float[] {0.2, 0.4, 0.7};

float fuzzinessH = 0.1;
float fuzzinessS = 0.3;
float fuzzinessV = 0.3;

// for 2nd color:

float[] targetColor2RGB = new float[] {0.5, 0.3, 0.2};

float fuzziness2H = 0.1;
float fuzziness2S = 0.3;
float fuzziness2V = 0.3;


void setup() {

  img = loadImage("http://" + "localhost:8888/catalogtree/helipad_detection/testImg2.png");
  shader = loadShader("http://" + "localhost:8888/shaders/colorRangeHSV.glsl");

  colorMode(RGB, 1, 1, 1, 1);
}



void draw() {

  background(0);

  shader(shader); 
  shader.set("targetColor", targetColorRGB, 3);

  shader.set("fuzzinessH", fuzzinessH);
  shader.set("fuzzinessS", fuzzinessS);
  shader.set("fuzzinessV", fuzzinessV);
  image(img, 512, 0, height, height);  

  //resetShader();

  // now apply the shader again with different settings

  shader(shader); 
  shader.set("targetColor", targetColor2RGB, 3);

  shader.set("fuzzinessH", fuzziness2H);
  shader.set("fuzzinessS", fuzziness2S);
  shader.set("fuzzinessV", fuzziness2V);

  //image(img, 512, 0, height, height);

  // this also makes crash JRE a lot!
  blend(img, 0, 0, 512, 512, 0, 0, 512, 512, LIGHTEST);

  resetShader();
  // the original
  image(img, 0, 0, 512, 512);
}

The shader:

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

#define PROCESSING_TEXTURE_SHADER

uniform sampler2D texture;
varying vec4 vertTexCoord;

// excepts a color in RGB
uniform vec3 targetColor; 

uniform float fuzzinessH;
uniform float fuzzinessS;
uniform float fuzzinessV;



// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

float norm(in float value, in float start, in float stop) {
    return (value - start) / (stop - start);
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

vec3 rgb2hsv(vec3 c)
{
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));

    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

vec3 hsv2rgb(vec3 c)
{
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

void main() {

  vec3 texColor = texture2D(texture, vertTexCoord.st).rgb;

  vec3 hsvTexColor = rgb2hsv(texColor);
  vec3 hsvTargetColor = rgb2hsv(targetColor);

  float distanceH = distance(hsvTexColor.r, hsvTargetColor.r);
  float distanceS = distance(hsvTexColor.g, hsvTargetColor.g);
  float distanceV = distance(hsvTexColor.b, hsvTargetColor.b);


  /*
  vec3 result;

  if (distanceH < fuzzinessH && distanceS < fuzzinessS && distanceV < fuzzinessV) {

    float normH = norm(distanceH, fuzzinessH, 0);
    float normS = norm(distanceS, fuzzinessS, 0);
    float normV = norm(distanceV, fuzzinessV, 0);

    result = vec3(min(normH, min(normS, normV)));
  }
  else {
    result = vec3(0);
  }
  */

  // the result looks the same without any if statements
  // so we go for that cause shaders without if statements tend to be faster!

  float normH = norm(distanceH, fuzzinessH, 0);
  float normS = norm(distanceS, fuzzinessS, 0);
  float normV = norm(distanceV, fuzzinessV, 0);

  vec3 result = vec3(min(normH, min(normS, normV)));


  gl_FragColor = vec4(result, 1.0);
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Tagged:

Answers

  • did you try other blend modes? because i think ADD would also do what you want, given that you're only using greyscale images.

  • Add will give different results cause 128 grey + 128 grey is full white.

  • edited October 2015

    blend() calls loadPixels() internally which seems to be a little buggy in Processing 3. Anyways, blend() is CPU base, pretty slow and ignores any shader input/output. Use shader based blending instead:

    PImage img;
    PShader shader, blendShader;
    PGraphics offscreen;
    
    void setup() {
        size(1512, 1000, P2D);
        initBlendShader();
        img = loadImage("http" + "://localhost:8888/catalogtree/helipad_detection/testImg2.png");
        offscreen = createGraphics(width, height, P2D);
        offscreen.shader(shader = loadShader("http" + "://localhost:8888/shaders/colorRangeHSV.glsl"));
        colorMode(RGB, 1, 1, 1, 1);
    }
    
    void draw() {
        background(0);
    
        applyShader(0.2, 0.4, 0.7, 0.1, 0.3, 0.3);
        image(offscreen, 0, 0);
    
        applyShader(0.5, 0.3, 0.2, 0.1, 0.3, 0.3);
        blend(offscreen);
    
        image(img, 0, 0, 512, 512);
    }
    
    void blend(PImage image) {
        blendShader.set("textureB", image);
        filter(blendShader);
    }
    
    void applyShader(float red, float green, float blue, float fuzzH, float fuzzS, float fuzzV) {
        shader.set("targetColor", red, green, blue);
        shader.set("fuzzinessH", fuzzH);
        shader.set("fuzzinessS", fuzzS);
        shader.set("fuzzinessV", fuzzV);
        offscreen.beginDraw();
        offscreen.image(img, 0, 0, width, height);
        offscreen.endDraw();
        offscreen.updatePixels();
    }
    
    void initBlendShader() {
        String[] vertSource = {
            "uniform mat4 transform;",
    
            "attribute vec4 vertex;",
            "attribute vec4 texCoord;",
    
            "varying vec2 vertTexCoord;",
    
            "void main() {",
                "vertTexCoord = vec2(texCoord.x, 1.0 - texCoord.y);", // Have to flip Y because Processing...
                "gl_Position = transform * vertex;",
            "}"
        };
        String[] fragSource = {
            "uniform sampler2D texture;",
            "uniform sampler2D textureB;",
    
            "varying vec2 vertTexCoord;",
    
            "void main() {",
                "gl_FragColor = max(texture2D(texture, vertTexCoord), texture2D(textureB, vertTexCoord));",
            "}"
        };
        blendShader = new PShader(this, vertSource, fragSource);
    }
    
  • I basically made the same shader you have but for some reason it does not work.

    http://forum.processing.org/two/discussion/12869/use-an-extra-texture-with-a-shader#latest

    Would be great if you can take a look.

    Btw, would you have preferred if processing had the y axis flipped?

  • Does it work with the #version 410 requirement used in your other thread?

    String[] vertSource = {
        "#version 410",
    
        "uniform mat4 transform;",
    
        "in vec4 vertex;",
        "in vec2 texCoord;",
    
        "out vec2 vertTexCoord;",
    
        "void main() {",
            "vertTexCoord = vec2(texCoord.x, 1.0 - texCoord.y);",
            "gl_Position = transform * vertex;",
        "}"
    };
    String[] fragSource = {
        "#version 410",
    
        "uniform sampler2D texture;",
        "uniform sampler2D textureB;",
    
        "in vec2 vertTexCoord;",
    
        "out vec4 fragColor;",
    
        "void main() {",
            "fragColor = max(texture(texture, vertTexCoord), texture2D(textureB, vertTexCoord));",
        "}"
    };
    

    The problem is Processing is flipping anything along the Y axis, just so P2D and P3D have the same Y direction. It's a pain in the a** because Processing's flipping doesn't always work as expected and you have to code workarounds like in the snippet above.

  • I'm working slowly on my own framework and I'm thinking of matching the opelGL y axis. On the other hand, I think it makes more sense to have 0 at the top...

    And yes! It works with #version 410, not sure if that was it cause I had a tiny other mistake and I have 2 many things open from today :) Anyway, I'm really happy it works!

    I think it makes more sense to use the same shader multiple times and blend those results with a filter. Although I didn't test for performance yet. My color select shader that can use an array of colors might be a lot faster...

  • Yeah, 0 at the top (and positive Y down) in 2D and in the middle (and positive Y up) in 3D like OpenGL and almost any other 3D environment.

    Nice! Glad it works!

    Sure, it should be faster. It would be optimal to handle the selecting and blending of multiple colors ranges in one shader pass.

  • edited October 2015

    I looked in the code for filer cause I wanted to filter only a specific area. Turns out it's written pretty bad.

    https://github.com/processing/processing/blob/master/core/src/processing/opengl/PGraphicsOpenGL.java

    /**
       * This is really inefficient and not a good idea in OpenGL. Use get() and
       * set() with a smaller image area, or call the filter on an image instead,
       * and then draw that.
       */
      @ Override
      public void filter(int kind) {
        PImage temp = get();
        temp.filter(kind);
        set(0, 0, temp);
      }
    
  • Oh no. :(

Sign In or Register to comment.