saveImage plus Alpha

Hi again,

generally it’s not hard to save an Image with alpha information. My question though is if there is a good way to get rid of the background without any aliasing artifacts in a simple way.

I simply want to save a .png without any background, only the shapes I drew. Is there a way to do that?

Did you find a good way to do this?

yes, what I did is to render everything to an FBO with a transparent background and then grab those pixels and put it into ofImage to save it.

Sweet. Would you mind sharing the code?

Have a look at Zach Gage’s FBOTexture class at http://www.openframeworks.cc/forum/view-…-1&start=15 (I think the link is broken, but you can find it in Theo’s render manager example).

and make the following mods:

  
  
void ofFBOTexture::allocate(int w, int h, bool autoClear) {  
  imageSaver.setUseTexture(false); // add ofImage imageSaver to class header  
  imageSaver.allocate(texData.width, texData.height, OF_IMAGE_COLOR_ALPHA);  
}  
  
  
void ofFBOTexture::saveImage(string fileName) {  
  glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);  
  glReadPixels(0, 0, texData.width, texData.height, texData.glType, GL_UNSIGNED_BYTE, imageSaver.getPixels());  
  imageSaver.saveImage(fileName);  
}  
  

Actually I wrote a little multithreaded image saver, which I use instead of ofImage, so you can write sequence of PNGs (which are heavy to compress). Posted that in a separate thread (pardon the pun :P)
http://forum.openframeworks.cc/t/getting-pixels-from-a-texture/1681/5 on using PBO’s to read the framebuffer asynchronously.

Hey Memo,

I tried using your mods to the ofFBOTexture, but the saveImage function only saves the image that is displayed but not the actual buffer, is there anyway to change that?

Thanks!

ah, i should have mentioned that you need to call the save function while the fbo is bound. a more robust solution would be something like:

  
void ofFBOTexture::saveImage(string fileName) {  
	bool alreadyIn = _isActive;   // save current state of fbo (bound or not)  
	if(!alreadyIn) begin(); // if it isn't bound, bind it  
  
	glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);  
	glReadPixels(0, 0, texData.width, texData.height, texData.glType, GL_UNSIGNED_BYTE, imageSaver.getPixels());  
	imageSaver.saveThreaded(fileName);  
	  
	if(!alreadyIn) end();  // if fbo wasn't bound when the function was called, unbind it  
}  

where _isActive is a protected variable that keeps track of whether the fbo is currently bound or not

This is how i worked my way around it :

  
void ofFBOTexture::saveImage(string fileName) {  
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);  
	glReadPixels(0, 0, texData.width, texData.height, texData.glType, GL_UNSIGNED_BYTE, imageSaver.getPixels());  
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);  
	imageSaver.saveImage(fileName);	  
}  

This may not be the place, but how do i access the members RGB of the pixel? i tried something like :
unsigned char* color=imageSaver.getPixels()+appWidth*y+x;
But from there how do i know which one is R,G,B?
EDIT :

  
	unsigned char* toto=imageBuffer.imageSaver.getPixels()+4*((768-y)*1024+x);  
	int val = (int)*toto;  
	cout << "\n R :" << val;  
	val = (int)*(toto+1);  
	cout << "\n G :" << val;  
	val = (int)*(toto+2);  
	cout << "\n B :" << val;  
	val = (int)*(toto+3);  
	cout << "\n A :" << val << "\n";  

And the saved image is mirrored (upside down), maybe it’s because i asked for .png?

Hello,

I am having this problem now too… I am trying to render something to an FBO with a transparent background, and then grab those pixels back later and store them in an ofImage for some special processing.

Can’t seem to get it working though… Anyone have some snippets of how this would be done?

Cheers!

oh ya… I’m using the ofFBOTexture class from here: http://forum.openframeworks.cc/t/fbo-addon-for-0.06/1866/11

I’m doing some testing and I’m getting it to grab into an ofImage what I draw into the FBO… but it’s offset on the y-axis by almost half (2px less) the FBO’s height.

And also, it’s not taking it as a transparent background.

My ofFBOTexture::clear method, looks like this…

  
  
void ofFBOTexture::clear(bool transparent)  
{  
	glClearColor(bgColor[0],bgColor[1],bgColor[2], (transparent?0.0f:bgColor[3]));  
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear Screen And Depth Buffer  
}  
  

And here’s my testApp::setup and draw methods.

  
  
void testApp::setup(){  
	ofEnableAlphaBlending();  
	  
	myFBO.allocate(500, 500, false);  
	myFBO.clear(true);  
	  
	myFBO.setupScreenForMe();  
	myFBO.swapIn();  
	ofSetColor(255, 0, 0, 127);  
	ofFill();  
	ofCircle(250, 250, 50);  
	ofSetColor(255, 255, 255);  
	myFBO.swapOut();  
	myFBO.setupScreenForThem();  
  
	myFBO.setupScreenForMe();  
	myFBO.swapIn();  
	grabbed.grabScreen(0, 0, 500, 500);  
	grabbed.setImageType(OF_IMAGE_COLOR_ALPHA);  
	myFBO.swapOut();  
	myFBO.setupScreenForThem();  
  
}  
  
void testApp::draw(){  
	ofSetColor(0, 127, 0);  
	ofFill();  
	ofRect(0, 0, ofGetWidth(), ofGetHeight());  
	ofSetColor(255, 255, 0);  
	for(int i=0; i < ofGetWidth(); i += 25)  
		ofLine(i, 0, i, ofGetHeight());  
	ofSetColor(255, 255, 255);  
	  
	grabbed.draw(ofGetFrameNum(), 10);  
}  
  

The testApp doesn’t do much - basically just drawing a circle to the FBO, capturing it, then drawing the image moving across the screen.

did you setup the FBO texture with GL_RGBA?

yeah, it’s got GL_RGBA… here is the line from the allocate method

  
glTexImage2D(texData.textureTarget, 0, GL_RGBA, texData.tex_w, texData.tex_h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);  

After talking to Moka, he mentioned I should use glReadPixels instead of grabScreen … which makes sense :slight_smile: but it doesn’t seem to read the pixels in a format that ofImage likes…

I’m basically doing this…

  
  
	unsigned char* pixels;	  
	myFBO.swapIn();  
  
	glPixelStorei(GL_PACK_ALIGNMENT, 1);  
	glReadPixels(0, 0, myFBO.getWidth(), myFBO.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, pixels);  
	  
	grabbed.setFromPixels(pixels, myFBO.getWidth(), myFBO.getHeight(), OF_IMAGE_COLOR_ALPHA);  
	grabbed.saveImage("test1.png");  
	myFBO.swapOut();  
  

I get an image saved which has a transparent background, but only shows alternating rows of little white ‘v’ shapes and dots. If I don’t draw any white pixels to my FBO, those go away and I have just a transparent image.

I am wondering if there’s something I should do to the pixels between glReadPixels and image.setFromPixels … any advice?

oh yeah… if I draw the FBO directly to screen, it looks exactly like it should…

ok… after talking to Moka a little more, we found the problem… kind of a noob mistake on my part too :slight_smile:

I was able to save an alpha png without using an FBO by directly swapping the data. This may be easier if you are not concerned about doing so on the graphics card, (although we do this realtime as well) - feel free to use it if it is helpful.

  
  
  
void makeRGBAComposite(ofxCvColorImage* _colorImg, ofxCvGrayscaleImage* _maskImg, ofImage* _composite, int _w, int _h, bool _fillBlack) {  
    unsigned char * colorImg = _colorImg->getPixels(); // 3 channel image  
    unsigned char * maskImg = _maskImg->getPixels(); // 1 channel image  
    _composite->setImageType(OF_IMAGE_COLOR_ALPHA);  
    unsigned char * composite = _composite->getPixels(); // 4 channel image  
    int limit = _w*_h;  
    int RGBAval;  
    int colorVal;  
    int pixel;  
      
    for(int i = 0; i < limit; i++, maskImg++) {  
        for(int k=0; k<4; k++) { //each of the 4 channels in the image  
            //translate values  
            RGBAval = i*4 + k;  
            colorVal = i*3 + k;  
              
            //filter channels  
            if(k == 3) {  
                //alpha channel  
                pixel = *maskImg;  
            } else {	  
                if(*maskImg < 1 && _fillBlack){  
                    pixel = 0; //make it black if the mask is black.   
                } else {  
                    pixel = colorImg[colorVal];  
                }  
            }  
              
            *(composite+RGBAval) = pixel;  
        }  
    }   
}  
  
  
  

1 Like