Urgent Hue and Saturation filter

As a new user today, I am currently experimenting with Processing to port over some iPhone code for high resolution renderings using the Image buffer

For the output (included image) I need to use Hue and Saturation filters.

Currently no support??

blendMode(MULTIPLY);
Etc

But no Hue and Saturation options.

Please help with possible solution.

Thanks all in advance.
(Keeping me busy during Lockdown)

1 Like

Hi Irigima,

It’s a bit hard to understand what is the problem you have?

You want to blend images using Hue and Saturation channels? Or filter image with Hue or Saturation channel?

Processing has colorModes. You can set are you using RGB or HSB (Hue, Saturation and Brightness) mode. I assume that blending is different, depending on the mode, but I haven’t tried.

One option is to do filtering by yourself, pixel by pixel. It’s not too complicated and you can find formulas for blending from internet easily.

Here’s a link how to handle pixels in Processing https://processing.org/tutorials/pixels/

2 Likes

Hi,

Thank you for your reply.

I wish to draw primitives like line, rectangle etc using the filter modes (like what you have in Photoshop).
Calculating per pixel will be very slow as using ~8000x8000 image area rendering ~1000 objects.

Here is an explanation from the manual for the different modes used in the iPhone app:

GRAPHICS MODE X

sets graphics commands compositing mode to X, where X is one of the modes listed below (R = premultiplied result; S = source color and alpha; D = destination color and alpha; Ra, Sa, Da = alpha components of

R, S, D). Default mode is “NORMAL”.

NORMAL : Paints the source image samples over the background image samples.

MULTIPLY : Multiplies the source image samples with the background image samples. This results in colors that are at least as dark as either of the two contributing sample colors.

SCREEN : Multiplies the inverse of the source image samples with the inverse of the background image samples. The result is then also inverted. This results in colors that are at least as light as either of the two contributing sample colors.

OVERLAY : Either multiplies or screens the source image samples with the background image samples, depending on the background color. The result is to overlay the existing image samples while preserving the highlights and shadows of the background.

DARKEN : Creates the composite image samples by choosing the darker samples (either from the source image or the background). The result is that the background image samples are replaced by any source image samples that are darker.

LIGHTEN : Creates the composite image samples by choosing the lighter samples (either from the source image or the background). The result is that the background image samples are replaced by any source image samples that are lighter.

COLORDODGE : Brightens the background image samples to reflect the source image samples. Source image sample values that specify black do not produce a change.

COLORBURN : Darkens the background image samples to reflect the source image samples. Source image sample values that specify white do not produce a change.

SOFTLIGHT : Either darkens or lightens colors, depending on the source image sample color. If the source image sample color is lighter than 50% gray, the background is lightened, similar to dodging. If the source image sample color is darker than 50% gray, the background is darkened, similar to burning. If the source image sample color is equal to 50% gray, the background is not changed. Image samples that are equal to pure black or pure white produce darker or lighter areas, but do not result in pure black or white. The overall effect is similar to what you’d achieve by shining a diffuse spotlight on the source image. Use this to add highlights to a scene.

HARDLIGHT : Either multiplies or screens colors, depending on the source image sample color. If the source image sample color is lighter than 50% gray, the background is lightened, similar to screening. If the source image sample color is darker than 50% gray, the background is darkened, similar to multiplying. If the source image sample color is equal to 50% gray, the background is not changed. Image samples that are equal to pure black or pure white result in pure black or white. The overall effect is similar to what you’d achieve by shining a harsh spotlight on the source image. Use this to add highlights to a scene.

DIFFERENCE : Subtracts either the source image sample color from the background image sample color, or the reverse, depending on which sample has the greater brightness value. Source image sample values that are black produce no change; white inverts the background color values.

EXCLUSION : Produces an effect similar to that produced by “DIFFERENCE”, but with lower contrast. Source image sample values that are black don’t produce a change; white inverts the background color values.

HUE : Uses the luminance and saturation values of the background with the hue of the source image.

SATURATION : Uses the luminance and hue values of the background with the saturation of the source image. Areas of the background that have no saturation (that is, pure gray areas) don’t produce a change.

COLOR : Uses the luminance values of the background with the hue and saturation values of the source image. This mode preserves the gray levels in the image. You can use this mode to color monochrome images or to tint color images.

LUMINOSITY : Uses the hue and saturation of the background with the luminance of the source image. This mode creates an effect that is inverse to the effect created by “COLOR”.

Hope this helps

1 Like

Pixel by pixel drawing is actually pretty fast in Processing if it’s done properly. But using line() and rect() functions is convenient. Blending in processing is set with blendMode() function. You’ll find most of modes you have in your iPhone app, but not Hue or Saturation.

Maybe you can get Hue and Saturation based blending by selecting colorMode(HSB, xx) and then selecting suitable blend mode. Documentation doesn’t much help so I suggest you try out what happens.

1 Like

Thanks for reply.

I don’t expect that combinations will work- but will give them a try.

Unfortunately, it needs Hue and Saturation to work for the result in my example.

When you are drawing those lines and rectangles, do you need anti-aliasing? Lines to be smoothed or can they be pixelated? Drawing 1000 shapes built from pixels doesn’t really take that long and then you could work with HSB mode and calculate blending by yourself.

Hi there.
That is a solution, but will be very slow as need to render 1000+ objects over ~500 iterations and that’s on iPhone resolution.

I require to render more objects at larger scales with a working resolution of 8000 X 8000 in Processing to achieve similar results.

Rendering per object and overall iteration resolution on a pixel level will vastly slow computation down.
(Example image took ~1 minute on iPhone)

Image needs AA off btw.

Thanks anyway for feedback.

1 Like

I had to make a small test. It gave me a bit over 40000 pixel changes in millisecond on my I5 8th generation processor. Pixel changes were in hue or saturation. Image size is not a problem - it’s just memory consumption. Here’s the code. I don’t know if it makes much sense, but it draws a lot of pixels blending in HSB color space. And I’m sure it could be optimized further

PGraphics pg;
int c = 0;
int wid = 8000;
int hei = 8000;

void setup() {
  pg = createGraphics(8000, 8000);
  size(800,800);
  pg.beginDraw();
  pg.colorMode(HSB,100);
  pg.background(100);
  pg.rectMode(CORNER);
  for(int i = 0; i < 500; i++) {
    fill(random(100), random(20,100), random(20,100));
    pg.rect(random(wid), random(hei), random(200,wid/20), random(200,hei/20));
  }
  pg.endDraw();
  image(pg,0,0);
}

void draw() {
  int m = millis();
  c = 0;
  pg.loadPixels();
  for(int i = 0; i < 1000; i++) { 
    if(random(2) < 1) {
      dRect();
    } else {
      dLine();
    }
  }  
  pg.updatePixels();
  image(pg,0,0);
  int m1 = millis();
  int cm = c/(m1-m);
  println(c + " pixels were drawn on round " + frameCount + "\n speed was " + cm + " pixels in millisecond");
  
}

void dRect() {
  int x1 = floor(random(wid));
  int y1 = floor(random(hei));
  int w = floor(random(wid/20));
  int h = floor(random(hei/20));
  color cl;
  float hu = random(100);
  float sa = random(100);
  float br = 0;
  boolean sw = (random(2) < 1);
  for(int xo = 0; xo < w; xo++) {
    for(int yo = 0; yo < h; yo++) {
      if(x1 + xo + (y1+yo)*wid < wid*hei-1) {
        if(sw) {  //hue blending
          sa = saturation(pg.pixels[x1 + xo + (y1+yo)*wid]);
          br = brightness(pg.pixels[x1 + xo + (y1+yo)*wid]);
        } else {  //sat blending
          hu = hue(pg.pixels[x1 + xo + (y1+yo)*wid]);
          br = brightness(pg.pixels[x1 + xo + (y1+yo)*wid]);
        }
        cl = color(hu, sa, br);
        pg.pixels[x1 + xo + (y1+yo)*wid] = cl;
        c++;
      }
    }
  }
  
  
}

void dLine() {
  int x1 = floor(random(wid));
  int y1 = floor(random(hei));
  int w = floor(random(3,9))*2-1; //odd numbers between 5-15
  color cl;
  float hu = random(100);
  float sa = random(100);
  float br = 0;
  boolean sw = (random(2) < 1);
  for(int xo = -w+1; xo < w; xo++) {
    if((x1 + xo + (y1)*wid < wid*hei-1) && (x1 + xo + (y1)*wid > 0)) {
      if(sw) {  //hue blending
        sa = saturation(pg.pixels[x1 + xo + (y1)*wid]);
        br = brightness(pg.pixels[x1 + xo + (y1)*wid]);
      } else {  //sat blending
        hu = hue(pg.pixels[x1 + xo + (y1)*wid]);
        br = brightness(pg.pixels[x1 + xo + (y1)*wid]);
      }
      cl = color(hu, sa, br);
      pg.pixels[x1 + xo + (y1)*wid] = cl;
      c++;
    }
  }
  
}
2 Likes

Thanks.
Have not tried as currently porting over version 1 for high res outputs.

You might find something useful in this library:

I’m not sure what the performance profile will be like. I think the library is more oriented towards still images rather than realtime animation, but I’m not sure.

1 Like

Hi.

Thanks for the suggestion. Unfortunately will not work.

All I need is to draw a rectangle overlapping another using the output as the result of a Hue or Saturation filter blend.
Like in Photoshop as an example.

I have tried your initial code.
Can you tell me the exact methods for its operation?
Slight chance I can implement it.

Many thanks in advance.

Try using SaturationExample to start.
From Examples > Contributed > Image Processing > SaturationExample

Here is a modified version with a box.

import milchreis.imageprocessing.*;
PImage image;
PImage box;
int[] b;

void setup() {
  size(550, 550);
  image = loadImage(dataPath("example.jpg"));
  b = new int[]{200, 50, 60, 395};
  box = image.get(b[0],b[1],b[2],b[3]);
}

void draw() {
  image(image, 0, 0);
  float intensity = map(mouseX, 0, width, 0.0f, 2.0f);
  image(Saturation.apply(box, intensity), b[0], b[1]);
}

That may work somehow.
A big thank you for your kind input.

I’ll see what this code does later tomorrow and will get back to you.

Stay safe!

1 Like

I have tried your initial code.
Can you explain to me the exact methods for its operation?
Slight chance I can implement it.

Many thanks in advance.

I initially saw this when first looking at Processing.

Unfortunately - cannot get your example to work.

How?

  • Couldn’t install the “Image Processing” library from Contributions Manager?
  • Sketch wouldn’t launch?
  • Sketch launched, but could not add an example image from SaturationExample?
  • Sketch ran with image, but image did not change saturation when you moved the mouse?
  • …?

Hi all.

Think I have the answer based on the initial code at the top.
Something like this:

Use a pg createGraphics(8000, 8000) as an external screen as need high resolution output.
Set a random R G B value between 0 and 255 on each (what I am using on iPhone)
Load pixels at a certain rectangular area from pg screen.
Convert above R G B to H S V
Adjust H S V values based on rectangular area using pg.pixels. (For Hue / Saturation options as per image description) to render to pg screen.
Repeat.

Sorry - new to this, but can see that the code above is something like what I have described.

Why are you filling the pixels of a huge buffer with color static? Is it for testing only, or is that your application?

So sorry, but trying to learn this language.
I see the logic, but understanding Syntax and how to accomplish it is a different matter.

Processing can be very powerful, and exactly what I need to generate some real high resolution renders.

Have managed to get this far, but struggling.
Running Saturation filter on iPhone as image.

Need exactly same result on Processing.
Using 1x random rectangle per saturation output.

Am really great-full for everyone’s help!!

PGraphics pg;
  
// Size for pg
// high res 20000 X 10000
int wid = 800;
int hei = 800;
int c;

void
  setup()
{ 
  // Size for pg
  pg = createGraphics(800, 800);
  
  size(800, 800);
  pg.beginDraw(); 
  frameRate(60);
  pg.colorMode(RGB, 255);
  pg.background(10,10,10);
  pg.colorMode(HSB, 255);
  pg.rectMode(CORNER);  
  pg.endDraw(); 
  image(pg, 0, 0);
} 
void
  draw() {
  
  pg.loadPixels(); 
      dRect(); 
        pg.updatePixels();
   
   
  image(pg, 0, 0);

}

void dRect() {
  println(c);
  c++;
  int x1 = floor(random(wid));
  int y1 = floor(random(hei));
  int w = floor(random(150));
  int h = floor(random(150));
  
  color cl; 
  
  float hu;
  float sa;
  float br;
  
  float h2 = int(random(255));
  float s2 = int(random(255));
  float b2 = int(random(255));
  
  pg.colorMode(HSB, 255);
  color c=color(h2,s2,b2);
  s2=saturation(c);
  
  
  for (int xo = 0; xo < w; xo++) { 
    for (int yo = 0; yo < h; yo++) { 
      if (x1+ xo + (y1+yo)*wid < wid*hei-1){

         //hue blending
          //sa = saturation(pg.pixels[x1 + xo + (y1+yo)*wid]); 
          //br = brightness(pg.pixels[x1 + xo + (y1+yo)*wid]);

         //sat blending
          hu = hue       (pg.pixels[x1 + xo + (y1+yo)*wid]); 
          sa = saturation(pg.pixels[x1 + xo + (y1+yo)*wid]);
          br = brightness(pg.pixels[x1 + xo + (y1+yo)*wid]);
          
          pg.colorMode(HSB, 255);
          
         // swapping s2 will result in saturation filter 
        cl = color(s2, hu, br); 
        pg.pixels[x1 + xo + (y1+yo)*wid] = cl; 
      }
    }
  }
}