Equivalent of Processing’s loadPixels, pixels[] not using ofImage

Hi!

I am trying to create Metaballs like this tutorial.
I tried to write the equivalent of this Processing’s code in oF.
I tried to use ofImage, but it was too heavy for the animation.
Are there any ways to do this not using ofImage?

Processing code that I want to use:

color pink = color(255, 102, 204);
loadPixels();
for (int i = 0; i < (width*height/2)-width/2; i++) {
  pixels[i] = pink;
}
updatePixels();

My oF code:

void ofApp::draw(){
    ofColor pink = ofColor(255, 102, 204);
    
    ofImage img;
    img.allocate(300, 300, OF_IMAGE_COLOR);
    for(int width = 0; width < ofGetWidth(); width++){
        for(int height = 0; height < ofGetHeight(); height++){
            img.setColor(width, height, ofColor(pink));
        }
    }
    
    img.update();
    img.draw(0, 0);
}

Thank you!

you are calling img.allocate(300, 300, OF_IMAGE_COLOR); in draw which means it’s being reallocated every frame – that’s probably going to slow things down.

can you try putting this ofApp.h:

ofImage img;

in ofApp::setup

img.allocate(300, 300, OF_IMAGE_COLOR);

and remove

ofImage img;
    img.allocate(300, 300, OF_IMAGE_COLOR);

from draw

1 Like

Hi @zach !

Thank you so much!
It is much faster now.

When I use img.allocate(300, 300), it is still fast, but when I change the window size and img.allocate(600, 600), it going to slow down with Blob class of this tutorial. Are there any ways to make it faster?

Thanks

ofApp.cpp:

void ofApp::draw(){
for(int x = 0; x < 600; x++){
        for(int y = 0; y < 600; y++){
            float sum = 0;
            for(int blobNum = 0; blobNum < 10; blobNum++){
                float distance = ofDist(x, y, blobs[blobNum].getPosX(), blobs[blobNum]      .getPosY());
                sum += 10 * blobs[blobNum].getRadius() / distance;
            }
            img.setColor(x,y, ofColor(sum));
        }
    }
    
    img.update();
    img.draw(0,0);
}

Blob.hpp:

class Blob {
    
public:
    Blob();
    void setup(float _x, float _y);
    void update();
    float getPosX();
    float getPosY();
    float getRadius();
    
    
private:
    ofVec2f pos;
    float radius;
    ofVec2f vel;
};

Blob.cpp:

#include "Blob.hpp"

Blob::Blob(){
   
}

void Blob::setup(float _x, float _y){
    pos.set(_x,_y);
    vel.set(ofRandom(-1.0, 1.0), ofRandom(-1.0, 1.0));
    vel * ofRandom(2.0, 5.0);
    radius = ofRandom(120, 400);
}

void Blob::update(){
    pos += vel;
    if (pos.x > ofGetWidth() || pos.x < 0) {
        vel.x *= -1;
    }
    if (pos.y > ofGetHeight() || pos.y < 0) {
        vel.y *= -1;
    }
}


float Blob::getPosX(){
    return pos.x;
}

float Blob::getPosY(){
    return pos.y;
}

float Blob::getRadius(){
    return radius;
}

Hi @kotaonaga, you might see if you can speed things up by moving the all the code before and including img.update() (from you post above) into ofApp::update(). oF seems to like having all the updating code in ofApp::update(), and separate from the drawing code in ofApp::draw(). Presumably, your Blobs are also in a std::vector and that you’re updating them as needed in ofApp::update(). Hope this helps!

1 Like

HI @TimChi

Thank you for your advice!

I moved my updating code in ofApp::update(). it is still going to be slow when I make the window bigger, but my code looks better now.

Hey @kotaonaga, I goofed around with my own version of this today as I had some time. It ran pretty slow on my linux laptop, but I could get it to speed up nicely by using a smaller image, and then scaling the image to a larger size with image.draw(0, 0, ofGetWidth(), ofGetHeight()). My laptop ran the app at 23 frames per second if I used 11 Blobs and an image width of ofGetWidth()/4 and height of ofGetHeight()/4. The app ran at full speed (60 fps) at 1/6 of ofGetWidth() and ofGetHeight().

If I remember correctly, an ofImage has an ofPixels for the CPU, and an ofTexture for the GPU. When an image is drawn as a larger size than it really is, some of the points must extrapolated from the closest ofTexture points. But, this is kind of what the application is calculating anyway, but for all of the pixels in a full-size image. So, give this a try if want it to speed it up a bit. The smaller the size of the allocated image, the faster the frame rate will be.

to make it faster

for(int x = 0; x < 600; x++){
        for(int y = 0; y < 600; y++){
            float sum = 0;
            for(int blobNum = 0; blobNum < 10; blobNum++){
                float distance = ofDist(x, y, blobs[blobNum].getPosX(), blobs[blobNum]      .getPosY());
                sum += 10 * blobs[blobNum].getRadius() / distance;
            }
            img.setColor(x,y, ofColor(sum));
        }
    }

you will probably need to look at optimizing the inner for loop :

 float distance = ofDist(x, y, blobs[blobNum].getPosX(), blobs[blobNum]      .getPosY());
                sum += 10 * blobs[blobNum].getRadius() / distance;

for example, 600x600 with 11 blobs that’s (60060011 = 3,960,000) calls to ofDist (which involves a square root function). Maybe you can simplify / optimize that somehow?

Hey @TimChi,

I tried to use 11 Blobs and an image width of ofGetWidth()/4 and height of ofGetHeight()/4 using img.allocate(ofGetWidth()/4, ofGetHeight()/4. I also make the window size 600 * 600.
The output has jaggies, but it became much faster than the previous one. That’s an awesome idea.

And, I didn’t know that an ofImage has an ofPixels for the CPU, and an ofTexture for the GPU. I will check these out!

Thank you so much for your help!

Hey @zach

wow that is huge. I see. I will try to optimize that.

Thank you for your advice:)!

Hey @kotaonaga, I had to use ofClamp() to keep the color between 0 and 255. The ofColor data are unsigned chars, which automatically roll over between 0 and 255. So for unsigned chars, 255 + 1 = 0 (black), and 0 - 1 = 255 (white). I get the feeling that colors are automatically clamped in the Processing version (so, 255 + 1 = 255). So, try clamping the value of “sum” to get rid of the “jaggies”:

for(int x = 0; x < 600; x++){
        for(int y = 0; y < 600; y++){
            float sum = 0;
            for(int blobNum = 0; blobNum < 10; blobNum++){
                float distance = ofDist(x, y, blobs[blobNum].getPosX(), blobs[blobNum]      .getPosY());
                sum += 10 * blobs[blobNum].getRadius() / distance;
                sum = ofClamp(sum, 0.0, 255.0); // clamp the value to eliminate the jaggies
            }
            img.setColor(x,y, ofColor(sum));
        }
    }

Also, to make your code a bit more “safe” (not accessing memory outside of the objects), you can confine the loops to run over the .size() of the image. You can confine the inner loops to the actual number of Blob if you use a std::vector, which also has a .size() method. If your Blob were in std::vector called blobs, your code for this portion would then look something like this:

// use img.getWidth() and img.getHeight() so the loops will only run over the dimensions of the img
for(int x = 0; x < img.getWidth(); x++){
        for(int y = 0; y < img.getHeight() y++){
            float sum = 0;
            for(int blobNum = 0; blobNum < blobs.size(); blobNum++){
                float distance = ofDist(x, y, blobs.at(blobNum).getPosX(), blobs.at(blobNum).getPosY());
                sum += 10 * blobs.at(blobNum).getRadius() / distance;
            }
            img.setColor(x,y, ofColor(sum));
        }
    }

Hi @TimChi!

I tried to use ofClamp() and make my code more “safe” as you said! Now, the output doesn’t have jaggies!!

Thank you so much!!