video mask based on ofxAlphaMaskShader

I set up a little program based on ofxAlphaMaskShader that creates a video mask with transparency for the dark areas and I’ve run into an odd problem.

So in the program I have one the main video layer that you see and one the masking video layer. I set it up and it all runs fine using live content from my laptop camera, but when i use a movie file as the content i get mangled interlaced garbage. Do you know if there is a difference in loading a movie file and live video to a shader or texture or something?

here is the xcode project for reference:
http://www.sendspace.com/file/djjaag

Thanks in advance.

hey Erik,

Looks like you’ve reversed vidWidth with vidHeight everywhere.

When you set up the video grabber with these dimensions it worked because it scaled the video grabber to that aspect, but when loading the movie it broke because the were inverted

Also there is an update available for 007 of this shader, you may want to look into moving your project up the latest version before getting into 006 too deep: https://github.com/Flightphase/ofxAlphaMask

here’s an updated version to your code that is working for me at the moment:

  
#include "testApp.h"  
  
//--------------------------------------------------------------  
void testApp::setup(){  
	  
//	blur.setup(640, 480);  
	//setup stuff from cv example  
	//init live vid or load movie  
#ifdef _USE_LIVE_VIDEO  
	vidWidth = 800;  
	vidHeight = 600;  
	underLayer.loadImage("beach.png");  
	vidGrabber.setVerbose(true);  
	vidGrabber.initGrabber(vidWidth,vidHeight);  
#else  
	vidWidth = 320;  
	vidHeight = 240;  
	underLayer.loadImage("beach320.png");  
	vidPlayer.loadMovie("fingers.mov");  
	vidPlayer.play();  
#endif  
	  
	//allocate 2 vid windows	  
	baseLayer.allocate(vidWidth,vidHeight);  
	  
	//masking layer  
	maskLayer.allocate(vidWidth,vidHeight);  
  
	  
  
	bLearnBakground = true;  
	threshold = 80;  
	  
	///////////////////////  
	  
	//need this for alpha to come through  
	ofEnableAlphaBlending();  
	//set the texture parameters for the maks shader. just do this at the beginning  
	maskShader.setup("composite", "composite");  
	maskShader.begin();  
	maskShader.setTexture("Tex0", baseLayer.getTextureReference(), 0);  
	maskShader.setTexture("Tex1", maskLayer.getTextureReference(), 1);  
	maskShader.end();  
	  
	  
}  
  
//--------------------------------------------------------------  
void testApp::update(){  
	ofBackground(100,100,100);  
    bool bNewFrame = false;  
	  
	// choose live vid or movie file	  
#ifdef _USE_LIVE_VIDEO  
	vidGrabber.grabFrame();  
	bNewFrame = vidGrabber.isFrameNew();  
#else  
	vidPlayer.idleMovie();  
	bNewFrame = vidPlayer.isFrameNew();  
#endif  
	  
	//if new frame (not the first)  
	if (bNewFrame){  
		  
		// choose live vid or movie file	  
#ifdef _USE_LIVE_VIDEO  
		maskLayer.setFromPixels(vidGrabber.getPixels(), vidWidth,vidHeight);  
		baseLayer.setFromPixels(vidGrabber.getPixels(), vidWidth,vidHeight);  
#else  
		maskLayer.setFromPixels(vidPlayer.getPixels(), vidWidth,vidHeight);  
		baseLayer.setFromPixels(vidPlayer.getPixels(), vidWidth,vidHeight);  
#endif  
		//i think ofxCvGrayscaleImage only likes to be updated this way...  
		maskLayer = baseLayer;  
		maskLayer.threshold(threshold, false);  
		underLayer.update();  
	  
}  
}  
  
//--------------------------------------------------------------  
void testApp::draw(){  
	//	  
//	blur.setBlurParams(4, (float)mouseX / 100.0);  
//	blur.beginRender();  
//	maskLayer.draw(vidHeight,vidWidth);  
	  
	//first draw the bottom layer  
	//bottomLayer.draw(0, 0);  
	//using colorImg rather than bottomLayer  
	underLayer.draw(0,0);  
	baseLayer.draw(vidWidth,vidHeight);  
	maskLayer.draw(vidWidth,vidHeight);  
	//then draw a quad for the top layer using our composite shader to set the alpha  
	maskShader.begin();  
	  
	//our shader uses two textures, the top layer and the alpha  
	//we can load two textures into a shader using the multi texture coordinate extensions  
	glActiveTexture(GL_TEXTURE0_ARB);  
	baseLayer.getTextureReference().bind();  
	  
	glActiveTexture(GL_TEXTURE1_ARB);  
	maskLayer.getTextureReference().bind();  
	  
	//draw a quad the size of the frame  
	glBegin(GL_QUADS);  
	  
	//move the mask around with the mouse by modifying the texture coordinates  
	float maskOffset = 0;  
	glMultiTexCoord2d(GL_TEXTURE0_ARB, 0, 0);  
	glMultiTexCoord2d(GL_TEXTURE1_ARB, 0, 0);		  
	glVertex2f( 0, 0);  
	  
	glMultiTexCoord2d(GL_TEXTURE0_ARB, vidWidth, 0);  
	glMultiTexCoord2d(GL_TEXTURE1_ARB, vidWidth, 0);		  
	glVertex2f( ofGetWidth(), 0);  
	  
	glMultiTexCoord2d(GL_TEXTURE0_ARB, vidWidth, vidHeight);  
	glMultiTexCoord2d(GL_TEXTURE1_ARB, vidWidth, vidHeight );  
	glVertex2f( ofGetWidth(), ofGetHeight());  
	  
	glMultiTexCoord2d(GL_TEXTURE0_ARB, 0, vidHeight);  
	glMultiTexCoord2d(GL_TEXTURE1_ARB, 0, vidHeight);		  
	glVertex2f( 0, ofGetHeight() );  
	  
	glEnd();  
	//deactive and clean up  
	glActiveTexture(GL_TEXTURE1_ARB);  
	maskLayer.getTextureReference().unbind();  
	  
	glActiveTexture(GL_TEXTURE0_ARB);  
	baseLayer.getTextureReference().unbind();  
	  
	maskShader.end();  
//	  
//	blur.endRender();  
//	blur.draw(0, 0, vidHeight, vidWidth, true);  
	  
}  
  

WHOOPS. Total flub, thanks Jim.

Hey erikparr would you be able to post your project file for this so I could have a look. Thanks.

Sorry for the late reply, but here is the code. Just make sure you include the appropriate addons and you should be set.

  
/*  
 *  ofxAlphaMaskShader.h  
 *  
 * Created by James George, [http://www.jamesgeorge.org](http://www.jamesgeorge.org)  
 * in collaboration with FlightPhase [http://www.flightphase.com](http://www.flightphase.com)  
 *  
 * Permission is hereby granted, free of charge, to any person  
 * obtaining a copy of this software and associated documentation  
 * files (the "Software"), to deal in the Software without  
 * restriction, including without limitation the rights to use,  
 * copy, modify, merge, publish, distribute, sublicense, and/or sell  
 * copies of the Software, and to permit persons to whom the  
 * Software is furnished to do so, subject to the following  
 * conditions:  
 *  
 * The above copyright notice and this permission notice shall be  
 * included in all copies or substantial portions of the Software.  
 *  
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,  
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES  
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND  
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT  
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,  
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR  
 * OTHER DEALINGS IN THE SOFTWARE.  
 *  
 * ----------------------  
 *  
 * ofxAlphaMaskShader is not really an addon, but an example  
 * of how to use a shader to have one image become the alpha  
 * channel of another.  
 */  
  
#include "testApp.h"  
  
//--------------------------------------------------------------  
void testApp::setup(){  
	  
//	blur.setup(640, 480);  
	//setup stuff from cv example  
	//init live vid or load movie  
#ifdef _USE_LIVE_VIDEO  
	vidHeight = 600;  
	vidWidth = 800;  
	underLayer.loadImage("beach.png");  
	vidGrabber.setVerbose(true);  
	vidGrabber.initGrabber(vidWidth,vidHeight);  
#else  
	vidHeight = 240;  
	vidWidth = 320;  
	underLayer.loadImage("beach320.png");  
	vidPlayer.loadMovie("fingers.mov");  
	vidPlayer.play();  
#endif  
	  
	//allocate 2 vid windows	  
	baseLayer.allocate(vidWidth,vidHeight);  
	  
	//masking layer  
	maskLayer.allocate(vidWidth,vidHeight);  
  
	  
  
	bLearnBakground = true;  
	threshold = 80;  
	  
	///////////////////////  
	  
	//need this for alpha to come through  
	ofEnableAlphaBlending();  
	//set the texture parameters for the maks shader. just do this at the beginning  
	maskShader.setup("composite", "composite");  
	maskShader.begin();  
	maskShader.setTexture("Tex0", baseLayer.getTextureReference(), 0);  
	maskShader.setTexture("Tex1", maskLayer.getTextureReference(), 1);  
	maskShader.end();  
	  
	  
}  
  
//--------------------------------------------------------------  
void testApp::update(){  
	ofBackground(100,100,100);  
    bool bNewFrame = false;  
	  
	// choose live vid or movie file	  
#ifdef _USE_LIVE_VIDEO  
	vidGrabber.grabFrame();  
	bNewFrame = vidGrabber.isFrameNew();  
#else  
	vidPlayer.idleMovie();  
	bNewFrame = vidPlayer.isFrameNew();  
#endif  
	  
	//if new frame (not the first)  
	if (bNewFrame){  
		  
		// choose live vid or movie file	  
#ifdef _USE_LIVE_VIDEO  
		maskLayer.setFromPixels(vidGrabber.getPixels(), vidWidth,vidHeight);  
		baseLayer.setFromPixels(vidGrabber.getPixels(), vidWidth,vidHeight);  
#else  
		maskLayer.setFromPixels(vidPlayer.getPixels(), vidWidth,vidHeight);  
		baseLayer.setFromPixels(vidPlayer.getPixels(), vidWidth,vidHeight);  
#endif  
		//i think ofxCvGrayscaleImage only likes to be updated this way...  
		maskLayer = baseLayer;  
		maskLayer.threshold(threshold, false);  
		underLayer.update();  
	  
}  
}  
  
//--------------------------------------------------------------  
void testApp::draw(){  
	//	  
//	blur.setBlurParams(4, (float)mouseX / 100.0);  
//	blur.beginRender();  
//	maskLayer.draw(vidWidth,vidHeight);  
	  
	//first draw the bottom layer  
	//bottomLayer.draw(0, 0);  
	//using colorImg rather than bottomLayer  
	underLayer.draw(0,0);  
	baseLayer.draw(vidWidth,vidHeight);  
	maskLayer.draw(vidWidth,vidHeight);  
	//then draw a quad for the top layer using our composite shader to set the alpha  
	maskShader.begin();  
	  
	//our shader uses two textures, the top layer and the alpha  
	//we can load two textures into a shader using the multi texture coordinate extensions  
	glActiveTexture(GL_TEXTURE0_ARB);  
	baseLayer.getTextureReference().bind();  
	  
	glActiveTexture(GL_TEXTURE1_ARB);  
	maskLayer.getTextureReference().bind();  
	  
	//draw a quad the size of the frame  
	glBegin(GL_QUADS);  
	  
	//move the mask around with the mouse by modifying the texture coordinates  
	float maskOffset = 0;  
	glMultiTexCoord2d(GL_TEXTURE0_ARB, 0, 0);  
	glMultiTexCoord2d(GL_TEXTURE1_ARB, 0, 0);		  
	glVertex2f( 0, 0);  
	  
	glMultiTexCoord2d(GL_TEXTURE0_ARB, vidWidth, 0);  
	glMultiTexCoord2d(GL_TEXTURE1_ARB, vidWidth, 0);		  
	glVertex2f( ofGetWidth(), 0);  
	  
	glMultiTexCoord2d(GL_TEXTURE0_ARB, vidWidth, vidHeight);  
	glMultiTexCoord2d(GL_TEXTURE1_ARB, vidWidth, vidHeight );  
	glVertex2f( ofGetWidth(), ofGetHeight());  
	  
	glMultiTexCoord2d(GL_TEXTURE0_ARB, 0, vidHeight);  
	glMultiTexCoord2d(GL_TEXTURE1_ARB, 0, vidHeight);		  
	glVertex2f( 0, ofGetHeight() );  
	  
	glEnd();  
	//deactive and clean up  
	glActiveTexture(GL_TEXTURE1_ARB);  
	maskLayer.getTextureReference().unbind();  
	  
	glActiveTexture(GL_TEXTURE0_ARB);  
	baseLayer.getTextureReference().unbind();  
	  
	maskShader.end();  
//	  
//	blur.endRender();  
//	blur.draw(0, 0, vidWidth, vidHeight, true);  
	  
}  
  
//--------------------------------------------------------------  
void testApp::keyPressed  (int key){  
}  
  
//--------------------------------------------------------------  
void testApp::keyReleased(int key){  
	  
}  
  
//--------------------------------------------------------------  
void testApp::mouseMoved(int x, int y ){  
	  
}  
  
//--------------------------------------------------------------  
void testApp::mouseDragged(int x, int y, int button){  
	  
}  
  
//--------------------------------------------------------------  
void testApp::mousePressed(int x, int y, int button){  
	  
}  
  
//--------------------------------------------------------------  
void testApp::mouseReleased(int x, int y, int button){  
	  
}  
  
//--------------------------------------------------------------  
void testApp::windowResized(int w, int h){  
	  
}  
  
  

  
/*  
 *  ofxAlphaMaskShader.h  
 *  
 * Created by James George, [http://www.jamesgeorge.org](http://www.jamesgeorge.org)  
 * in collaboration with FlightPhase [http://www.flightphase.com](http://www.flightphase.com)  
 *  
 * Permission is hereby granted, free of charge, to any person  
 * obtaining a copy of this software and associated documentation  
 * files (the "Software"), to deal in the Software without  
 * restriction, including without limitation the rights to use,  
 * copy, modify, merge, publish, distribute, sublicense, and/or sell  
 * copies of the Software, and to permit persons to whom the  
 * Software is furnished to do so, subject to the following  
 * conditions:  
 *  
 * The above copyright notice and this permission notice shall be  
 * included in all copies or substantial portions of the Software.  
 *  
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,  
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES  
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND  
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT  
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,  
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR  
 * OTHER DEALINGS IN THE SOFTWARE.  
 *  
 * ----------------------  
 *  
 * ofxAlphaMaskShader is not really an addon, but an example  
 * of how to use a shader to have one image become the alpha  
 * channel of another.  
 */  
  
#ifndef _TEST_APP  
#define _TEST_APP  
  
#include "ofMain.h"  
  
#include "ofxOpenCv.h"  
#include "ofxShader.h"  
#include "ofxFBOTexture.h"  
//#include "ofBlurShader.h"  
//#include "shaderBlur.h"  
  
//#define _USE_LIVE_VIDEO		// uncomment this to use a live camera  
// otherwise, we'll use a movie file  
  
class testApp : public ofBaseApp{  
	  
public:  
	void setup();  
	void update();  
	void draw();  
	  
	void keyPressed  (int key);  
	void keyReleased(int key);  
	void mouseMoved(int x, int y );  
	void mouseDragged(int x, int y, int button);  
	void mousePressed(int x, int y, int button);  
	void mouseReleased(int x, int y, int button);  
	void windowResized(int w, int h);  
	  
#ifdef _USE_LIVE_VIDEO  
	ofVideoGrabber 		vidGrabber;  
#else  
	ofVideoPlayer 		vidPlayer;  
#endif  
	  
	ofxCvColorImage		baseLayer;  
  
	ofxCvGrayscaleImage 	maskLayer;  
	  
	  
	int 				threshold;  
	int					vidWidth;  
	int					vidHeight;  
	bool				bLearnBakground;  
	//ofImage topLayer;  
	ofImage underLayer;  
	ofxShader maskShader;  
	  
	bool hasCamera;  
	  
//	shaderBlur blur;  
	  
};  
  
#endif  
  

Thanx obviousjim for this shader example :slight_smile:

Erik - Thanx for posting your code :), however I keep coming up with the “undefined reference to” errors in particular to ofxshader and I’ve included the appropriate addon scrs/paths/linkage in project as far as I can tell.

One of the error messages:
obj\release\src\main.o:main.cpp|| undefined reference to `ofxShader::ofxShader()’|

Well, maybe I’m missing something, so would it be possible to provide an XML file or list of the necessary addons needed for this project, please ?

Also, I assume this code is applicable for OF ver. 007 since it includes ofxOpenCv.h, etc.
FYI: My specs are in my signature, and I’m using Codeblocks.

By the way, OF Developers/Team, some of the paths/directories for Windows in your XML files do not match the actual paths/directories (nor the file names) in 007 - at least not in my setup (and maybe I did something wrong? However, in general, everything works fine for me), see example below:

1 line example in your XML file for ofxOpenCv 007
…/…/…/addons/ofxOpenCv/libs/opencv/lib/win32/libcxcore.a

My setup/path/directory in 007
C:\Program Files\OpenFrameworks_007\addons\ofxOpenCv\libs\opencv\lib*win_cb*
Notice that “win32” was changed to “win_cb” (I assume for either windows 32or64 & codeblocks?)
Also, I can not find libcxcore.a (one can assume it’s libopencv_core220.a ??), so I just added all the library links :wink:

Hopefully this post is clear and please correct me if I’m totally off :wink:
Thanks for your time,
Julie

hi Julie

I think the linker problem with erik’s code for you is that he is still using ofxShader and in 007 we have put that in the core, as ofShader, try swapping those names and removing ofxShader from your addons.

if you want a simple example https://github.com/Flightphase/ofxAlphaMask/ has a really basic use of this function just to get it started. There’s no windows example file, but it doesn’t have any external dependencies so just using an emptyExample project for windows codeblocks should work for you.

Also re install.xml, see this thread on the answer for that: http://forum.openframeworks.cc/t/using-opencv-addon-in-007-in-codeblocks-gives-me-errors./7802/0

Yes, James, followed your suggestion regarding ofShader and ofxShader above, merci !!! :slight_smile:

By the way, it didn’t work right away since the “member”(code) is different when using either one, for example:

Erik’s code using ofxShader was maskShader.setup(“composite”, “composite”);
and member “setup” does not exist using ofShader, thus used instead maskShader.load(“composite”);

***
And yes, I had already tried your *very* nice simple example before venturing onto this one and thus used the “load” member from it :wink:
Great example and creatively cool !!
***
So, then I ran into other compile errors regarding included library files from ofxOpenCV which I removed - just kept the source files - and then it compiled without errors (will look at other thread you included in re:XML, thanx).

Finally I saw the alpha masking with video and image. However, it looks like it’s duplicating the movie at another size, and some “off” coordinates, etc., but at least it’s producing something. I will go thru code to see if I’ve made a syntax/error in some respect.

Also, I noticed some ofShader log warnings info in the console window - see image below.

Now what I would like to do is to modify this to apply it to movie & live webcam, but maybe that won’t work?..[if busy, no need to reply, I understand]

Again, a big thanx for the quick and helpful reply (and sorry for my late reply, was out getting some wine, ha).

hi julie,

Those warning’s should be a problem. i think it just wants you to declare some more specific things in the shader about what type you’re using, but if you see it working i think you can just ignore them.

as for masking two things with different sizes it’s really tricky to get the texture coordinates right.

If you have a movie and a live webcam, and you want the live cam to mask the movie, try something like below. Also if your movie and your webcam don’t have the same dimensions it will show up stretched

  
  
  
glActiveTexture(GL_TEXTURE0_ARB);  
movie.getTextureReference().bind();  
  
glActiveTexture(GL_TEXTURE1_ARB);  
webcam.getTextureReference().bind();  
  
//draw a quad the size of the frame  
glBegin(GL_QUADS);  
  
//move the mask around with the mouse by modifying the texture coordinates  
float maskOffset = 15 - mouseY;  
glMultiTexCoord2d(GL_TEXTURE0_ARB, 0, 0);  
glMultiTexCoord2d(GL_TEXTURE1_ARB, 0, 0);  
glVertex2f( 0, 0);  
  
glMultiTexCoord2d(GL_TEXTURE0_ARB, movie.getWidth(), 0);  
glMultiTexCoord2d(GL_TEXTURE1_ARB, webcam.getWidth(), 0);  
glVertex2f( movie.getWidth(), 0);  
  
glMultiTexCoord2d(GL_TEXTURE0_ARB, movie.getWidth(), movie.getHeight());  
glMultiTexCoord2d(GL_TEXTURE1_ARB, webcam.getWidth(), webcam.getHeight());  
glVertex2f( movie.getWidth(), movie.getHeight());  
  
glMultiTexCoord2d(GL_TEXTURE0_ARB, 0, movie.getHeight());  
glMultiTexCoord2d(GL_TEXTURE1_ARB, 0, webcam.getHeight());  
glVertex2f( 0, movie.getHeight() );  
  
glEnd();  
  
//deactive and clean up  
glActiveTexture(GL_TEXTURE1_ARB);  
webcam.getTextureReference().unbind();  
  
glActiveTexture(GL_TEXTURE0_ARB);  
movie.getTextureReference().unbind();  
  
maskShader.end();  
  

Yes, James, re. my prev. quick test, it was an error of dimensions; I did not declare my movie dim and thus it was stretched to the dims of the window (main.cpp).

Thanx very much for the code below, it works :slight_smile:

If I’m understanding correctly and doing this right, example of movie + webcam = alpha transparency minus masking function unless using “maskOffSet” with mouse coordinates. Well, I also tried this mask function with webcam on your simple example (hand & xray) and it was rather interesting (however I will not be using mouse control for my project, just webcam detection).

Have a great day! ;D