finding center of openCV blobs

trying to find the x, y position (center of the blob) of openCV blobs, but not sure what properties to check for this, here is my draw function I am using, its working well, just don’t know where to go from here. Is there maybe documentation somewhere?

  
void testApp::draw(){  
    //UI stuff  
	  
	ofPushStyle();  
	ofEnableBlendMode(OF_BLENDMODE_ALPHA);  
	ofPopStyle();  
      
    // openCV  
      
    ofxCvBlob blob = contourFinder.blobs.at(1).boundingRect;  
      
	ofSetHexColor(0xffffff);  
    colorImg.draw(0, 0, 320, 240);  
	grayDiff.draw(0, 240, 320, 240);  
    ofRect(320, 0, 320, 240);  
    contourFinder.draw(320, 0, 320, 240);  
	ofColor c(255, 255, 255);  
	for(int i = 0; i < contourFinder.nBlobs; i++) {  
		ofRectangle r = contourFinder.blobs.at(i).boundingRect;  
		r.x += 320; r.y += 240;  
		c.setHsb(i * 64, 255, 255);  
		ofSetColor(c);  
		ofRect(r);  
	}  
}  
  

guessing it has to do with ofRectangle r, but I don’t know how I would target certain blobs, is this an object that I can pull things out of or something? for example: r[1].position

ofxCvBlob has a centroid property so blob.centroid

so to target a specific (lets say first one) one I would write “contourFinder.blobs.at(0).centeroid” ?

yep - but it is “centroid” :slight_smile:

oops! thanks :slight_smile:

ok having some issues, I am trying to print the value of the centroid and its giving me error of:

  
no variable overload '='  

here is what I am doing:

textApp.cpp

  
#include "testApp.h"  
  
void testApp::setup(){  
      
	bLearnBackground = false;  
      
    vidGrabber.setVerbose(true);  
    vidGrabber.initGrabber(320,240);  
      
    colorImg.allocate(320,240);  
    grayImage.allocate(320,240);  
    grayBg.allocate(320,240);  
    grayDiff.allocate(320,240);  
      
    // UI  
      
    float dim = 20;  
	float xInit = OFX_UI_GLOBAL_WIDGET_SPACING;  
    float length = 320-xInit;  
	  
    drawPadding = false;  
      
    gui = new ofxUICanvas(660, 60, length+xInit, ofGetHeight());  
    gui->addSlider("sliderThreshold", 0.0, 255.0, sliderThreshold, length-xInit,dim);  
      
    ofAddListener(gui->newGUIEvent,this,&testApp::guiEvent);  
      
      
    // TEXT  
    myFont.loadFont("KLAVIKAR.TTF", 32);  
  
      
}  
  
void testApp::update(){  
    vidGrabber.update();  
    //do we have a new frame?  
    if (vidGrabber.isFrameNew()){  
		colorImg.setFromPixels(vidGrabber.getPixelsRef());  
        grayImage = colorImg; // convert our color image to a grayscale image  
        if (bLearnBackground == true) {  
            grayBg = grayImage; // update the background image  
            bLearnBackground = false;  
        }  
		grayDiff.absDiff(grayBg, grayImage);  
        grayDiff.threshold(sliderThreshold);  
		contourFinder.findContours(grayDiff, 5, (340*240)/4, 4, false, true);  
    }  
}  
  
void testApp::draw(){  
    //UI stuff  
	  
	ofPushStyle();  
	ofEnableBlendMode(OF_BLENDMODE_ALPHA);  
	ofPopStyle();  
      
    // openCV  
      
    centroid = contourFinder.blobs.at(0).centroid;  
      
	ofSetHexColor(0xffffff);  
    colorImg.draw(0, 0, 320, 240);  
	grayDiff.draw(0, 240, 320, 240);  
    ofRect(320, 0, 320, 240);  
    contourFinder.draw(320, 0, 320, 240);  
	ofColor c(255, 255, 255);  
	for(int i = 0; i < contourFinder.nBlobs; i++) {  
		ofRectangle r = contourFinder.blobs.at(i).boundingRect;  
		r.x += 320; r.y += 240;  
		c.setHsb(i * 64, 255, 255);  
		ofSetColor(c);  
		ofRect(r);  
	}  
      
    //render text  
    ofSetHexColor(0xe0e0e0);  
    myFont.drawString(centroid, 660, 40);  
    myFont.drawString("Visualheroizer", 660, 70);  
}  
  
void testApp::keyPressed(int key) {  
	bLearnBackground = true;  
      
    // UI Stuff  
    switch (key)  
    {  
        case 'p':  
        {  
            drawPadding = !drawPadding;  
            gui->setDrawWidgetPadding(drawPadding);  
        }  
            break;  
              
        case 'g':  
        {  
            gui->toggleVisible();  
        }  
            break;  
        default:  
            break;  
    }  
}  
void testApp::guiEvent(ofxUIEventArgs &e)  
{  
	string name = e.widget->getName();  
	int kind = e.widget->getKind();  
	  
	if(name == "sliderThreshold")  
	{  
		ofxUISlider *slider = (ofxUISlider *) e.widget;  
		sliderThreshold = slider->getScaledValue();  
        cout << "value: " << slider->getScaledValue() << endl;  
	}  
	    else if(name == "RSLIDER")  
    {  
        ofxUIRangeSlider *rslider = (ofxUIRangeSlider *) e.widget;  
        cout << "valuelow: " << rslider->getScaledValueLow() << endl;  
        cout << "valuehigh: " << rslider->getScaledValueHigh() << endl;  
    }  
    else if(name == "PAD")  
    {  
        ofxUI2DPad *pad = (ofxUI2DPad *) e.widget;  
        cout << "value x: " << pad->getScaledValue().x << endl;  
        cout << "value y: " << pad->getScaledValue().y << endl;  
    }  
    else if(name == "CSLIDER" || name == "CSLIDER 2")  
    {  
        ofxUIRotarySlider *rotslider = (ofxUIRotarySlider *) e.widget;  
        cout << "value: " << rotslider->getScaledValue() << endl;  
    }  
}  
  
void testApp::exit()  
{  
    delete gui;  
}  
  
//void ofxCvImage::blurGaussian(int value=3);  
  
//--------------------------------------------------------------  
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)  
{  
      
      
}  
  
//--------------------------------------------------------------  
void testApp::gotMessage(ofMessage msg){  
      
}  
  
//--------------------------------------------------------------  
void testApp::dragEvent(ofDragInfo dragInfo){  
      
}  

test.h

  
#pragma mark once  
  
#include "ofMain.h"  
#include "ofxUI.h"  
#include "ofxOpenCv.h"  
  
  
  
class testApp : public ofBaseApp{  
      
public:  
      
    void setup();  
    void update();  
    void draw();  
    void exit();   
      
    void keyPressed  (int key);  
    void keyReleased(int key);  
      
    ofVideoGrabber vidGrabber;  
	ofxCvColorImage colorImg;  
      
	ofxCvGrayscaleImage grayImage;  
    ofxCvGrayscaleImage grayBg;  
    ofxCvGrayscaleImage grayDiff;  
    ofxCvContourFinder contourFinder;  
    int threshold;  
    int centerOne;  
    bool bLearnBackground;  
      
    // UI STUFF  
    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);  
	void dragEvent(ofDragInfo dragInfo);  
	void gotMessage(ofMessage msg);  
      
	ofxUICanvas *gui;  
	void guiEvent(ofxUIEventArgs &e);  
    bool drawPadding;  
	float sliderThreshold;  
    ofxCvBlob centroid;  
      
    // font/text  
    ofTrueTypeFont myFont;  
      
};  

the centroid is a ofPoint/ofVec3f so I think you have to do

  
    myFont.drawString(ofToString(centroid.x), 660, 40);    
  

now I get:

centroid = contourFinder.blobs.at(0).centroid;

code:

  
centroid = contourFinder.blobs.at(0).centroid;  

error

  
no viable overload '='  

code:

  
 myFont.drawString(ofToString(centroid.x), 660, 40);    

error:

  
no memeber named 'x' in 'ofxCvBlob'  

I do see centroid in the documentation, but not sure how to use it :confused:

http://www.openframeworks.cc/documentation/ofxOpenCv/ofxCvBlob.html#centroid

Hi Lukasz,

Try this:

  
stringstream ss;  
ss << "Blob: ";  
// make sure there is at least one blob, so that we can get the first one //  
if(contourFinder.blobs.size() > 0) {  
     // pull out the centroid, which is an ofPoint / ofVec3f  
     ofVec3f centroid = contourFinder.blobs[0].centroid;  
     // store the x,y,z coords from the centroid in the string  
     ss << centroid << endl;  
     ofFill();  
     ofCircle( centroid, 20 );  
} else {  
     ss << " NOT FOUND." << endl;  
}  
ofDrawBitmapString( ss.str().c_str(), 20, 20);  

ah, this worked perfect.

what is the difference between ofDrawBitmapString and drawString?
also is there a way to loop through objects in C++? I have done this in JS to figure out types and what the properties and methods are, not sure if you can do that in C++

Sure. There are a few ways, the most common being a for loop

  
for(int i = 0; i < contourFinder.blobs.length(); i++ ) {  
    ofVec3f centroid = contourFinder.blobs[i].centroid;  
}  

seems to give me an error of:

  
No member named 'length' in 'std::vector<ofxCvBlob, std::allocator <ofxCVBlob>>'  

sorry, try size() instead of length()