I am trying to create a algorithm that create rising bubbles colored by pixels from video capture via webcam (on Mac). I have succeeded in create floating bubbles and then when i try to add the colorization, nothing appears on my screen…
By the way, i am a newbie in Processing
Can someone help me please?
Thank you,
Rémy
import processing.video.*;
Bubble [] bubbles = new Bubble [200];
Capture video;
void setup(){
size (640,480);
smooth();
for (int k = 0; k < bubbles.length; k++){
bubbles[k] = new Bubble (random(0,width), random(0,height));
}
video = new Capture (this,width,height,30);
video.start();
}
void draw(){
if (video.available()){
video.read();
}
background(0);
video.loadPixels();
for (int i = 0; i < video.width; i++){
for (int j = 0; j < video.height; j++){
int loc = (video.width-i-1) + j*video.width;
color c = video.pixels[loc];
for (int k = 0; k < bubbles.length; k++){
bubbles[k].display(c);
bubbles[k].ascend();
}
}
}
}
class Bubble{
float x,y;
float diam;
float speed;
float tempC;
Bubble(float tempX,float tempY){
x = tempX;
y = tempY;
diam = random(5,50);
speed = random(0,2);
}
void display(float tempC){
fill(tempC);
noStroke();
ellipseMode(CENTER);
ellipse (x,y,diam,diam);
}
void ascend(){
y -=speed;
if (y < -diam){
y = diam + height;
}
}
}
Hi, check the code below. Notice I remove a set of for loops as they are not needed and it will slow down your sketch unnecessarily. I removed some blocks in your code just for debugging. The main reason your code does not work, or the color were offset, is because you need to work for integer values for the bubble position. I mean, you could use floats for positions. However, when you multiply position by a factor (say posx=602.25 * video.height), the fractional part introduces an artificial offset on that position. The proper way would be posx=int(602.25) * video.height
One thing: If you introduce speed to your sketch, make sure you run it at a lower frameRate. Otherwise, you will not be able to see the bubbles in your sketch as they will drift toward the edge in no time.
Kf
import processing.video.*;
Bubble [] bubbles;
Capture video;
void setup() {
size (640, 480);
noStroke();
ellipseMode(CENTER);
bubbles = new Bubble [20];
for (int k = 0; k < bubbles.length; k++) {
bubbles[k] = new Bubble (int(random(0, width)), int(random(0, height)));
}
surface.setTitle(Capture.list()[1]);
video = new Capture (this, Capture.list()[1]);
video.start();
}
void draw() {
if (video.available()==true) {
video.read();
// image(video, 0, 0);
video.loadPixels();
for (int k = 0; k < bubbles.length; k++) {
int loc = int((video.width - bubbles[k].x - 1) + bubbles[k].y*video.width);
//int loc = int(bubbles[k].x + bubbles[k].y*video.width);
color c = video.pixels[loc];
bubbles[k].display(c);
if (dist(mouseX, mouseY, bubbles[k].x, bubbles[k].y)<15) {
int loc2 = int(mouseX + mouseY*video.width);
color cc = video.pixels[loc2];
bubbles[k].display(cc);
println(mouseX, mouseY, bubbles[k].x, bubbles[k].y);
}
}
}
}
class Bubble {
int x, y;
float diam;
float speed;
float tempC;
Bubble(int tempX, int tempY) {
x = tempX;
y = tempY;
diam = random(1, 15);
speed = 0;//random(0, 2);
}
void display(color tempC) {
fill(tempC);
ellipse (x, y, diam, diam);
}
}
Thank you for your answer. I understood the need of the integer values here, it makes sens to me. But i didn’t get why i can’t use a for loop within another one… Indeed, i have one for the pixels numbering and another one for the bubbles array. It is two different set of numbers with no primarily links… Why do i have to set them under the same loop?
Also, i changed a bit the code with your advices and it is exactly the render that i wanted except for the bubbles. I wanted them to go back outside at the bottom of the screen when they reach the top.
void ascend(){
y -=speed;
if (y < -diam){
y = int(diam + height);
}
Don’t know why but it doesn’t work… Do you have an idea why?
Also, why can’t i set a random speed to the bubbles?
Thank you for your help.
Best,
Rémy
import processing.video.*;
Bubble [] bubbles;
Capture video;
void setup() {
size (640, 480);
smooth();
bubbles = new Bubble [400];
for (int k = 0; k < bubbles.length; k++) {
bubbles[k] = new Bubble (int(random(0, width)), int(random(0, height)));
}
video = new Capture (this, width, height,30);
video.start();
}
void draw() {
if (video.available()==true) {
video.read();
video.loadPixels();
background(0);
for (int k = 0; k < bubbles.length; k++) {
int loc = int((video.width - bubbles[k].x - 1) + bubbles[k].y*video.width);
color c = video.pixels[loc];
bubbles[k].display(c);
bubbles[k].ascend();
}
}
}
class Bubble {
int x, y;
float diam;
float speed;
float tempC;
Bubble(int tempX, int tempY) {
x = tempX;
y = tempY;
diam = random(1, 30);
speed = random(0.5);
}
void display(color tempC) {
fill(tempC);
noStroke();
ellipse (x, y, diam, diam);
}
void ascend(){
y -=speed;
if (y < -diam){
y = int(diam + height);
}
}
}
Actually there is a link. You see, you have a pixel domain that projects into color space. You also have a bubble array. You do not need to interact ALL over the pixel domain to find out the color of the bubble. Why? Because each bubble has a position, and with this info you can project to the color domain. As I demonstrated in my code, you need only one for loop to assign the proper color to each bubble. Notice there is another way to do this which is easier to read and less code:
which uses the get() function to inquire for the color at certain position. Notice this code only shows the concept. In your original code, I believe you are using a mirror concept along the width of your sketch.
After debugging your sketch, I implemented two changes. The first one is in the definition of loc inside the for loop. The second change was turning potion back to float since you want to work with speeds lower than one aka. for speeds between 0 and 1, you need to work with float positions.
On a side note, this gives a parallax effect. Neat.
Kf
import processing.video.*;
Bubble [] bubbles;
Capture video;
void setup() {
size (640, 480);
smooth();
bubbles = new Bubble [400];
for (int k = 0; k < bubbles.length; k++) {
bubbles[k] = new Bubble (int(random(0, width)), int(random(0, height)));
}
video = new Capture (this, width, height, 30);
video.start();
}
void draw() {
if (video.available()==true) {
video.read();
video.loadPixels();
background(0);
for (int k = 0; k < bubbles.length; k++) {
int loc = int((video.width - int(bubbles[k].x) - 1) + abs(int(bubbles[k].y) % video.height)*video.width);
if(loc>width*height) println(bubbles[k].x,bubbles[k].y,loc,frameCount);
color c = video.pixels[loc];
bubbles[k].display(c);
bubbles[k].ascend();
}
}
}
class Bubble {
float x, y;
float diam;
float speed;
float tempC;
Bubble(int tempX, int tempY) {
x = tempX;
y = tempY;
diam = random(1, 30);
speed = random(0.5);
}
void display(color tempC) {
fill(tempC);
noStroke();
ellipse (x, y, diam, diam);
}
void ascend() {
y -=speed;
if (y < -diam) {
y = int(diam + height);
}
}
}
I got it. Thank you for the explanation. It makes more sens to load only the pixels needed.
Moreover, those lines of code below are more abstract for me.
int loc = int((video.width - int(bubbles[k].x) - 1) + abs(int(bubbles[k].y) % video.height)*video.width);
if(loc>width*height){
println(bubbles[k].x,bubbles[k].y,loc,frameCount);
}
Yes i am using a mirror concept so i get the main architecture and the use of integers now, but why using an absolute value and a modulo? Even if i don’t understand the process, it is very effective and the algorithm is working perfectly!
The abs and modulo was my hack to ensure that the loc calculation gives a result within the pixel space. Your checking in ascend() allows bubble’s y position to take negative values which will result in negative values when calculating the loc variable. I say this is a hack as it got everything working. However, this could be optimized by removing the hack and implementing proper boundary checking. If your code runs as you want, then there is no need to sweat it.