Opacity with blending

I’m implementing a divide blend mode by hand to do background removal from a bunch of images. In Photoshop, I can also set the opacity of the background layer to make it a little less harsh, like this:

05%20AM

But I can’t figure out how to implement that here! If I reduce the RGB pixel values in the background image it just makes the result darker.

My current divide code:

PImage bg = loadImage(bgFilename);
bg.loadPixels();

PImage img = loadImage(inputFilename);
img.loadPixels();
for (int i=0; i<img.pixels.length; i++) {
  float imgR = (img.pixels[i] >> 16 & 0xFF) * 256;
  float imgG = (img.pixels[i] >> 8  & 0xFF) * 256;
  float imgB = (img.pixels[i]       & 0xFF) * 256;

  float bgR = (bg.pixels[i] >> 16 & 0xFF) + 1;        // +1 to avoid dividing by 0
  float bgG = (bg.pixels[i] >> 8  & 0xFF) + 1;
  float bgB = (bg.pixels[i]       & 0xFF) + 1;

  img.pixels[i] = color(imgR/bgR, imgG/bgG, imgB/bgB);
}
img.updatePixels();
1 Like
bg.updatePixels();

That’s for a start. I didn’t check the rest yet though

Hi Jeff,

Purely out of personal interest, could you please share the leaf image without the divide blending mode activated? I’ve applied similar settings (divide blend mode + 70% opacity) on top of various images, but can’t detect any visual difference. Or am I misunderstanding something?

Hey Tiemen, it’s really subtle with the leaf images I have. But applied to something more drastic, you can definitely see the difference.

(Dumb forum says “new users” (been here for years) can’t post more than one image, so this is getting split into two posts…)

100% opacity:

2 Likes

50% opacity

In my case, the difference might be so slight it’s not worth figuring out, but now I’m obsessed and really want to know :slight_smile:

1 Like

Shouldn’t matter, since I don’t actually display the background image, just access its pixel values to do the math.

It seems to me that you’re code is working, although I had to add void setup and draw. Is this the result you’re going for?

divided

PImage img, bg;

void setup(){
  size(1280, 720);
  noLoop();

  img = loadImage("orange.jpg");
  bg = loadImage("grapes.jpg");

  img.loadPixels();
  bg.loadPixels();
  for (int i = 0; i < img.pixels.length; i++) {
    float imgR = (img.pixels[i] >> 16 & 0xFF) * 256;
    float imgG = (img.pixels[i] >> 8  & 0xFF) * 256;
    float imgB = (img.pixels[i]       & 0xFF) * 256;
  
    float bgR = (bg.pixels[i] >> 16 & 0xFF) + 1;
    float bgG = (bg.pixels[i] >> 8  & 0xFF) + 1;
    float bgB = (bg.pixels[i]       & 0xFF) + 1;
  
    img.pixels[i] = color(imgR/bgR, imgG/bgG, imgB/bgB);
  }
  img.updatePixels();
}

void draw(){
  image(img, 0, 0);
  //saveFrame("divided.jpg");
}

Only thing that seems to be missing in your code is the opacity part :slight_smile:

edit: or is that opacity added by not multiplying the bg.pixels by 256?

1 Like

Yep, I just included the basic code for the image processing but it does work correctly – my question really was just about the opacity :wink:

I’m honestly not sure what the * 255 does but without it the resulting image is black.

High five Jeff, think we did it! :raised_hand::boom::raised_hand:

50% opacity

100% opacity

PImage img_bkg, img_act, layer_blended;
float opacity = 0.5; // insert value between 0 and 1

void setup() {
  size(1280, 720);
  noLoop();
  img_bkg = loadImage("orange.jpg");
  img_act = loadImage("grapes.jpg");
  layer_blended = createImage(width, height, RGB);
}

void draw() {
  loadPixels();
  for (int i = 0; i < img_bkg.pixels.length; i++) {    
    // get RGB values for all pixels of the background image
    float bkgR = (img_bkg.pixels[i] >> 16 & 255);
    float bkgG = (img_bkg.pixels[i] >> 8  & 255);
    float bkgB = (img_bkg.pixels[i]       & 255);

    // get RGB values for all pixels of the image on top
    float actR = (img_act.pixels[i] >> 16 & 255);
    float actG = (img_act.pixels[i] >> 8  & 255);
    float actB = (img_act.pixels[i]       & 255);

    // get RGB values for all pixels if the blending mode 'divide' would be active ...
    float bldR = (bkgR / actR) * 255;
    float bldG = (bkgG / actG) * 255;
    float bldB = (bkgB / actB) * 255;
    // ... and use it to construct a new image called 'layer_blended'
    layer_blended.pixels[i] = color(bldR, bldG, bldB);
  }
  updatePixels();
  
  image(img_bkg, 0, 0);
  tint(255, opacity * 255);
  image(layer_blended, 0, 0);
}

The article Photoshop Blend Modes Explained helped a great deal to understand the mathematics behind the blend modes. What it doesn’t explain however is how blending modes work in co-operation with different opacity settings, yet the solution is actually quite simple:

  1. draw the background image
  2. place a transparent version of the blended image on top of it

Hope that answers your questions :slight_smile:

2 Likes

“We did it?” No, you did it! That’s awesome and so obvious (once you realize it :slight_smile: ). It makes total sense that the layers in PS are essentially sitting on top of a white canvas that we don’t see. Thanks for doing the detective work!

This makes me think that a P5-focused tutorial on Photoshop layers/blend modes would be super fun.

2 Likes

Also, it makes sense now why the original numbers are multiplied by 255 – essentially that creates the white layer they’re sitting on, which then gets reduced when you divide by another number in the 0-255 range.

Ok, taking a closer look I realize one key question: is there a way to do this without tint? In other words, direct pixel manipulation. I’d like to avoid using a PGraphics buffer if possible and there must be a way to do this just with math, right?

P5/Photoshop tutorial sound like a great idea, go for it!

The tint can be replaced by adding a fourth parameter to the layer_blended.pixels[i]. Mayhaps we can take out or reduce the use of PImages as well, but I’m handing over that case to you fellow detective Jefflock Holmpson. Good luck and let us know if you have any breakthroughs

1 Like

+10 for that. Seriously, that would be amazing, and might have as big an audience as collision detection.