Attracting a particle system to ofxOpenCv centroid

Hey everyone,

I am working on pimping up one of my projects to be able to send a number of various randomized data via OSC and I have been banging my head against the wall with a particular issue. I have a working ofxOpenCV powered blob tracker class that calculates a centroid and gives me the x & y coordinates. I would like my particle system (which is another class within the same project) to be attracted to these coordinates (instead of to mouseX & mouseY). What I am struggling with is how to communicate these coordinates to the particle class. I tried OSC but I didn’t manage to receive any data that way and now I am a bit lost. Any ideas how to make two classes communicate coordinates between one another would be very much welcome. Thank you :slight_smile:

Hi @Frogsie,

Well there is many implementation of particules systems, without code it’s hard to judge it, but if you have some to share, we can help you.

Using OSC doesn’t make the most sense, as OSC is usually meant for communication between different machines, or applications, but less in the same application.

An easier way would be to have an event listener from one class, and trigger an event from the ofxOpenCV class, if you have to go with something which doesn’t expose properties in a public way. This method also has the advantage of giving yo the freedom of not having to update the centroids at the same framerate as the update function (framerate of the app).

The most simple way would be to get the center of the centroids, expose it publicly, and retrieve it in the particule system:

ofVec2f retriveCentercentroid(){
return ofVec2f(blob.centroidofPoint.x, blob.centroidofPoint.y);
}

Then read it in the particule system.

Again it would be great to see where in the code there is an issue as pseudo code can be implemented in 100 different ways…

Hope this helps,

Best,

P

1 Like

Hey Pierre,
thank you so much for your response, I really appreciate it. I had a strange feeling about using OSC within the same app but couldn’t think up anything else so I tried regardless but your solution seems much more sensible. Let me share the code from the two classes I am trying to communicate between:

This is my computer vision / blob tracker update function with the coordinates of the centroid calculated at the bottom (these are ‘a’ and ‘b’ as I am already using ‘x’ and ‘y’ in my particle system). This part of the code works and if I print them, they return values fine.

vid.update();

if (vid.isFrameNew()){
    
    colorImg.setFromPixels(vid.getPixels());
    colorImg.mirror(false, true);

    grayImage = colorImg;
    
    if (bLearnBackground == true){
        grayBg = grayImage; // Note: this is 'operator overloading'
        bLearnBackground = false; // Latch: only learn it once.
    }
    
    grayDiff.absDiff(grayBg, grayImage);
    
    grayDiff.threshold(thresholdValue);
    
    contourFinder.findContours(grayDiff, 200, 3000, 10, true);
    
}

int numBlobs = contourFinder.blobs.size();
for (int i = 0; i < numBlobs; ++i){
    ofxCvBlob & blob = contourFinder.blobs[i];
    a = ofMap(blob.centroid.x, 0, 320, 0, ofGetWidth());
    b = ofMap(blob.centroid.y, 0, 240, 0, ofGetHeight());
    ofVec2f c(a,b);
    ofVec2f v = p - c;
    float d = v.length();
    float f = 10000 / d / d;
    v = v.getNormalized() * f;
    p += v;
    
        }

}

Now these are the relevant snippets of my particle code:

}
void DemoParticles::reset(){
//the unique val allows us to set properties slightly differently for each particle
uniqueVal = ofRandom(-100, 100);

pos.x = ofRandomWidth();
pos.y = ofRandomHeight();

vel.x = ofRandom(-3.9, 3.9);
vel.y = ofRandom(-3.9, 3.9);

frc   = glm::vec2(0,0);

scale = ofRandom(0.5, 1.0);

drag  = ofRandom(0.96, 0.998);

}

void DemoParticles::update(){

if (messageBuffer.size()>maxBufferSize) messageBuffer.pop_back();

    glm::vec2 attractPt(ofGetMouseX(), ofGetMouseY());

    frc = attractPt-pos; 
    frc = glm::normalize(frc); 

    vel *= drag; //apply drag
    vel += frc * 0.6; //apply force
        
    }
}

pos += vel;

if( pos.x > ofGetWidth() ){
    pos.x = ofGetWidth();
    vel.x *= -1.0;
}else if( pos.x < 0 ){
    pos.x = 0;
    vel.x *= -1.0;
}
if( pos.y > ofGetHeight() ){
    pos.y = ofGetHeight();
    vel.y *= -1.0;
}
else if( pos.y < 0 ){
    pos.y = 0;
    vel.y *= -1.0;
} 

}
void DemoParticles::draw(){

ofSetColor(255, 63, 180);
ofFill(); 
ofDrawCircle(pos.x, pos.y, scale * 4.0);

}

This is also working with the particles being attracted to the mouseX and mouseY at the moment. I also have another class for the ParticleSys which is a parent to this DemoParticle class but I don’t think it is relevant as the attraction happens in this one. Is this a bit clearer?

I am wondering where I would implement the code snippet you suggested? Would this be in one of the classes .h files or within the .cpp? And how would the other side of the code look like for the receiving part?

Again, thank you so much, I am learning quite a bit with this already :slight_smile:

Lucie

Hello,

Yes i can help to get the events.

you can try to use this as an exa,mple to check out how to create a custom event<->:

#in ofApp.h

#include "ofEvent.h"

class ExampleClassCreationEvent{
    public :
    ExampleClassCreationEvent(){}
    void fireEvent(){
        
        std::vector < ofVec2f > centers;
        int amnt = int(ofRandom(20));
        for(int i = 0; i < amnt; i++){
            centers.push_back(ofVec2f(ofRandom(1.), ofRandom(1.)));
        }
        
        std::cout << "firing " << std::endl;
        ofNotifyEvent(newVectorVec2fEvent,centers,this);
    }
    ofEvent<std::vector < ofVec2f > > newVectorVec2fEvent;
};

class ExampleClassListingEvent{
    public :
    ExampleClassListingEvent(){}
    
    void printEvent(std::vector < ofVec2f >& events){
        
        std::cout << "listening " << std::endl;
        for(int i = 0; i < events.size(); i++){
            std::cout << "events[" << ofToString(i) << "] = " << ofToString(events[i]) << std::endl;
        }
    }
    
};

class ofApp : public ofBaseApp{
	public:
		void setup();
		void update();
		void draw();
		
		void keyPressed(int key);
    
    ExampleClassCreationEvent creator;
    ExampleClassListingEvent listener;

};

and

#in ofApp.cpp
#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    ofAddListener(creator.newVectorVec2fEvent,&listener,&ExampleClassListingEvent::printEvent);

}

//--------------------------------------------------------------
void ofApp::update(){
    
}

//--------------------------------------------------------------
void ofApp::draw(){

}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
    if(key == ' '){
        creator.fireEvent();
    }
}

This fires events every time you press the space bar, with a random amount of events fired every time, so to match the random amount of centroids you will get.
Now all you need to do is place the vector(n pushing elements into it) inside your numBlob for loop, then fire the event at the end of the loop.

For the particules, you should try to change the particule to have an index, and have this index correspond to the index of the event in the foor loop of the listener ( modulo the size of the ofVec2f event).

Let me know if that makes sense and if you manage to wrap your head around itl.

Best.,

P