Erase the pixels of an image with a custom shape

Hi Forum,

For some reason I’m having a hard time working through this problem, though it’s quite simple…
I feel that there are several other topics here that approach a solution to a problem similar to mine, but I haven’t found anything that quite fits…

Let me explain:

I have a colour video, paused on the first frame.
I have an image that is dynamically made as a black and white thumbnail from the pixels of the video.
This part is done…

Basically what I want is to have a ‘transparency eraser’ (currently just using a 2D Primitive, but eventually will be a CV Blob) that grabs the colour of the ofImage and updates it to be transparent…
We can do it on a per pixel basis for the time being, and worry about the shapes later…

I’ve been playing around with the getPixelsRef method of ofImage, and I think it’s along the lines of what I need to do… Eg.:

  
    // Get Pixels  
    ofPixelsRef fore = foreground.getPixelsRef();  
    ofPixelsRef back = background.getPixelsRef();  
      
    // Iterate  
    for(int x = 0; x < foreground.getWidth()/2; x++) {  
        for (int y = 0; y < foreground.getHeight(); y++) {  
            ofColor c = back.getColor(x, y);  
            fore.setColor(x, y, c);  
        }  
    }  
      
    // Update Pixels  
    foreground.setFromPixels(fore);  

(this is just a leading experiment, not the production code)

I feel that I’m over thinking this a bit too much, and possibly over looking something. To a certain extent, I likely have some of my lexicon wrong, and am missing some of the answers hidden on the forum…

I’ve thought of looking into using FBO’s too, but I feel that I can do this with an ofImage, and would rather try to solve it this way first…

I’m sure that people here have dealt with this before, so I’d really appreciate any leads…

Thanks in advance,

~ Jesse

I’d just like to edit this a bit, in hope of getting a reply…

So, I’ve played around with a few different ways of doing this, and this is what I have for now:

Essentially…

  1. Load a video, pause it on the first frame
  • this works
  1. Make an RGB ofImage out of the video frame
  • this works
  1. Make a black & white image out of the previous image
  • this works, but the pixels of the thumbnail seem to be set to black and white too - i think i’m doing something dumb with my reference that i pass to the makeGrayscale() method.
  1. update the current pixel of the foreground (the b+w image) to be the color of the pixel in the same location of the thumbnail (the color image).
  • this kinda works, but the problem with step #3 is affecting this.
  • also, I need to figure out how to do this with a larger custom shape (ie more than one pixel at a time)…

Any thoughts ?!?

  
#include "testApp.h"  
  
  
//--------------------------------------------------------------  
void testApp::setup(){  
      
    // Canvas  
    ofSetBackgroundAuto(false);  
    ofBackground(100);  
    ofEnableAlphaBlending();  
      
    // Video  
    video.loadMovie("movies/fingers.mov");  
    video.firstFrame();  
    video.setPaused(true);  
  
    // Background  
    thumbnail.setFromPixels(video.getPixels(), video.getWidth(), video.getHeight(), OF_IMAGE_COLOR);  
    thumbnail.resize(ofGetWidth(), ofGetHeight());  
  
    // Convert To Grayscale  
    foreground = convertToGrayscale(&thumbnail);  
  
      
}  
  
//--------------------------------------------------------------  
ofImage testApp::convertToGrayscale(ofImage *img) {  
      
    ofPixelsRef pixels = img->getPixelsRef();  
      
    for(int x = 0; x < img->getWidth(); x++){  
        for(int y = 0; y < img->getHeight(); y++){  
            ofColor color = img->getColor(x, y);  
            float avg = color.r + color.g + color.b;  
            avg /= 3;  
            pixels.setColor(x, y, avg);  
        }  
    }  
      
    ofImage newImage;  
    newImage.setFromPixels(pixels);  
    newImage.update();  
      
    return newImage;  
}  
  
//--------------------------------------------------------------  
void testApp::update(){  
      
    colorImage(&foreground);  
  
}  
  
  
//--------------------------------------------------------------  
void testApp::colorImage(ofImage *img) {  
      
    ofPixelsRef pixels = background.getPixelsRef();  
    ofColor color = background.getColor(ofGetMouseX(), ofGetMouseY());  
    color.set(color.r * 3, color.g * 3, color.b * 3);  
    pixels.setColor(ofGetMouseX(), ofGetMouseY(), color);  
    foreground.setFromPixels(pixels);  
    foreground.update();  
      
}  
  
//--------------------------------------------------------------  
void testApp::draw(){  
      
    // Draw Video  
    video.draw(0, 0, ofGetWidth(), ofGetHeight());  
      
    // Draw Foreground Thumbnail  
    foreground.draw(0, 0);  
  
}  
  
  

in convert, pixels points to the input img. Since its a pointer the the pixel data from the input image, the changes are acting on the input image.

You could do

  
  
    ofImage newImage;  
    newImage.allocate(img->getWidth(), img->getHeight(), ...);    
    ofPixelsRef pixels = newImage->getPixelsRef();    
    ...     
  

but you dont need to do the conversion manually if you want to do the whole image, just allocate an image for foreground, copy it to thumbnail using the assignment operator = and then set its image type to grey scale, eg

  
  
thumbnail.setFromPixels(video.getPixels(), video.getWidth(), video.getHeight(), OF_IMAGE_COLOR);    
thumbnail.resize(ofGetWidth(), ofGetHeight());    
foreground = thumbnail;  
foreground.setImageType(OF_IMAGE_GRAYSCALE);  
  

For converting a subset to color, you have the same reference thing going on, you are getting a pixels ref from the background and updating that, change the pixels ref to the foreground pixels. I think you can call setColor() on an image directly, so it would be ofColor c = background.getColor(x,y); foreground.setColor(x, y, c); foreground.update();

Ok, that makes sense…

I had a suspicion that the reference was acting on the original image, so thanks for making that clear.
Also, thanks for the setImageType() hint - I never knew about that method before!

I’ll take a look at the updating of the color too and report back - thanks!

hi

here are some more techniques with multi texturing and stencils… ->http://forum.openframeworks.cc/t/mask-ofvideoplayer/9193/0

greetings ascorbin

1 Like

Cheers. In the end, I wound up modifying the alphaMaskingShaderExample, as it fit what I needed to do…

For those who are interested, here’s the source code at this stage - essentially a straight port of the above named example:

  
//--------------------------------------------------------------  
void testApp::setup(){  
      
    ofEnableAlphaBlending();  
    ofBackground(100);  
      
    // Video  
    video.loadMovie("movies/fingers.mov");  
    video.firstFrame();  
    video.setPaused(true);  
      
    // Resize  
    int width = video.getWidth();  
    int height = video.getHeight();  
    ofSetWindowShape(width, height);  
  
    // Background  
    thumbnail.setFromPixels(video.getPixels(), height, OF_IMAGE_COLOR);  
    thumbnail.resize(width, height);  
      
    // Convert To Grayscale  
    foreground = thumbnail;  
    foreground.setImageType(OF_IMAGE_GRAYSCALE);  
      
    // Brush  
    brush.loadImage("images/brush.png");  
      
    // FBOs  
    maskFbo.allocate(width, height);  
    fbo.allocate(width, height);  
      
    // Shader  
    string shaderProgram = "#version 120\n \  
    #extension GL_ARB_texture_rectangle : enable\n \  
    \  
    uniform sampler2DRect tex0;\  
    uniform sampler2DRect maskTex;\  
    \  
    void main (void){\  
    vec2 pos = gl_TexCoord[0].st;\  
    \  
    vec3 src = texture2DRect(tex0, pos).rgb;\  
    float mask = texture2DRect(maskTex, pos).r;\  
    \  
    gl_FragColor = vec4( src , mask);\  
    }";  
      
    shader.setupShaderFromSource(GL_FRAGMENT_SHADER, shaderProgram);  
    shader.linkProgram();  
      
    maskFbo.begin();  
        ofClear(0,0,0,255);  
    maskFbo.end();  
      
    fbo.begin();  
        ofClear(0,0,0,255);  
    fbo.end();  
  
    bBrushDown = false;  
  
}  
  
//--------------------------------------------------------------  
void testApp::update(){  
      
    //  
    maskFbo.begin();  
        if (bBrushDown){  
            ofSetColor(ofColor::aliceBlue);  
            brush.draw(mouseX-25, mouseY-25, 50, 50);  
        }  
    maskFbo.end();  
      
    //  
    fbo.begin();  
        ofClear(0, 0, 0, 0);  
        shader.begin();  
            shader.setUniformTexture("maskTex", maskFbo.getTextureReference(), 1);  
            thumbnail.draw(0, 0);  
        shader.end();  
    fbo.end();  
      
    // Idle Video  
    video.update();  
  
}  
  
//--------------------------------------------------------------  
void testApp::draw(){  
      
    ofSetColor(255, 255);  
      
    // Draw B+W Foreground  
    foreground.draw(0, 0);  
      
    // Dra FBO  
    fbo.draw(0, 0);  
      
}  
  
//--------------------------------------------------------------  
void testApp::mousePressed(int x, int y, int button){  
      
    bBrushDown = true;  
  
}  
  
//--------------------------------------------------------------  
void testApp::mouseReleased(int x, int y, int button){  
  
    bBrushDown = false;  
  
}  
  

1 Like

FYI, I’ve started a new thread for the next step in the problem - please see and comment if you have any ideas! thx!

http://forum.openframeworks.cc/t/determining-when-a-custom-‘brush’-has-painted-the-entire-screen/12970/0

Hi, Jesses
Would you like to share the test.h file code
Basically now what I want to do is create like a steam mask for the video, after then I can use mouse to erase the steam.

can you explain how did you declare the thumbnail and foreground in test.h file?

Thanks for the heads up. I can adapt this to my needs as well. I apparently need to skim through all of the examples! I’m trying to mask the exteriors of a vector of ofPolyline shapes … so that I can make cool fills and stuff on the insides, with iterations of 2d primitives and what-not, bounded by ofPaths or else ofPolylines --I haven’t decided how this is going down yet. when I found your post, I was definitely working with fbo’s to store stroke and fill pixels separately, and trying to figure out how to make one part of my drawing transparent, so … anyways, all is going well with everyone’s help. Thank yous

this is such a great community!

Sorry Cihang, just saw this.
Still want to see the code ?

the easiest way to do this now is to use an fbo, clear it towhite and draw to it any shape as you r “eraser” in black. then set that fbo’s texture as the alpha mask for the image you want toset parts as transparent:

//.h
ofImage img;
ofFbo fbo;

//setup
img.load(...);
fbo.allocate(img.getWidth(), img.getHeight(), GL_LUMINANCE)
img.getTexture().setAlphaMask(fbo.getTexture());
fbo.begin();
ofClear(255,255);
fbo.end();

//draw
fbo.begin();
ofSetColor(0);
//draw something
fbo.end();

ofSetColor(255);
img.draw();
1 Like