Title: WATERFALL
Year: 2013
Type: Interactive Installation , Processing 2.0 + Webcam
Site: http://santi.tv/wf
WATERFALL explores the possibilities of using a webcam as the main tool for interaction in a spatial installation. Using frame differentiation, the outline of any moving object is clearly determined with high accuracy, causing a response on a variable amount of three dimensional particles that behave as a waterfall. The installation is adaptable to any space and works in low to very well lit conditions, and depending on the processor speed, can control up to 5000 particles at a time. I chose not to use any external Processing libraries or highly unique hardware - such as an IR camera or Kinect - in order to make the project as universal as possible minimizing the technical requirements. It can run non-stop for long periods of time ( 20 days and counting with no crashes) and has been presented on a large HD 16 monitors video wall in Kuwait as well as on a standard resolution monitor at Electronics Alive VII, in Tampa. Waterfall has been tested on Mac Intels running in 64 Bits, and I am still trying to make it work on a windows machine...
Waterfall runs on any Mac computer with OSX, with either the existing or an attached webcam.
PROCESSING CODE
Year: 2013
Type: Interactive Installation , Processing 2.0 + Webcam
Site: http://santi.tv/wf
WATERFALL explores the possibilities of using a webcam as the main tool for interaction in a spatial installation. Using frame differentiation, the outline of any moving object is clearly determined with high accuracy, causing a response on a variable amount of three dimensional particles that behave as a waterfall. The installation is adaptable to any space and works in low to very well lit conditions, and depending on the processor speed, can control up to 5000 particles at a time. I chose not to use any external Processing libraries or highly unique hardware - such as an IR camera or Kinect - in order to make the project as universal as possible minimizing the technical requirements. It can run non-stop for long periods of time ( 20 days and counting with no crashes) and has been presented on a large HD 16 monitors video wall in Kuwait as well as on a standard resolution monitor at Electronics Alive VII, in Tampa. Waterfall has been tested on Mac Intels running in 64 Bits, and I am still trying to make it work on a windows machine...
Waterfall runs on any Mac computer with OSX, with either the existing or an attached webcam.
PROCESSING CODE
// ############################################################################################ // // ####### WATERFALL V 1.0 FOR 2 POINT 0 CONCEPTS ############################################ // // ########################################################################################## // // ####### SANTIAGO ECHEVERRY - 2012 ####################################################### // // ######################################################################################## // // ####### INSPIRED BY GOLAN LEVIN'S MOTION DIFFERENCING ################################# // // ###################################################################################### // // ####### ON THE MAC SET THE MAIN SCREEN AS #1 AND THE VIDEOWALL DISPLAY AS #2 ######## // // ####### #1 IS THE MAIN SCREEN #2 IS THE VIDEOWALL ################################## // // ####### BEFORE STARTING PROCESSING CONNECT THE ROCKETFISH EXTERNAL USB CAMERA ##### // // ################################################################################# // // ####### OPEN PROCESSING PREFERENCES USINGUNDER THE PROCESSING MENU ## // // ####### CHECK BOX AND INCREASE AVAILABLE MEMORY TO 512 MB ####################### // // ####### VERY IMPORTANT >>>> RUN SKETCHES ON DISPLAY 2 = VIDEOWALL ############## // // ####### LAUNCH PROGRAMS IN 64 BIT MODE ######################################## // // ####### ###################################################################### // // ####### THE SHARP VIDEO WALL HAS A RESOLUTION OF 1366X768 PX PER DISPLAY #### // // ####### THT TOTAL RESOLUTION OF THE WALL IS DISPLAYRES X AMOUNT OF DISPLAYS #// // ####### CHANGE resW AND resH TO THE DISPLAY'S RES IF NOT 1366X768 ######### // // ####### AND CHANGE THE AMOUNT OF COLUMNS AND ROWS CREATED BY THE DISPLAYS // // ####### FOR A 2X2 DISPLAY CHANGE screencols AND screenrows TO 2 EACH #### // // ####### MAKE SURE THE CODE WINDOW IS ON DISPLAY #1 SO YOU CAN SEE IT ### // // ####### UNDER "SKETCH" AT THE TOP >>>> SELECT "PRESENT" >>NOT<< "RUN" # // // ####### UNDER "SKETCH" AT THE TOP >>>> SELECT "PRESENT" >>NOT<< "RUN"# // // ####### TO STOP PRESENTATION UNDER "SKETCH" AT THE TOP SELECT "STOP" // // #################################################################### // import java.util.ArrayList; import processing.video.*; int resW = 1440; //1366; // HORIZONTAL RESOLUTION FOR EACH INDIVIDUAL DISPLAY int resH = 900; // 768; // VERTICAL RESOLUTION FOR EACH INDIVIDUAL DISPLAY int screencols = 1; // CHANGE TO THE AMOUNT OF HORIZONTAL MONITORS IN THE VIDEO WALL int screenrows = 1; // CHANGE TO THE AMOUNT OF VERTICAL MONITORS IN THE VIDEO WALL int ratio = 2; // DO NOT USE 1, 3, 6, 7, 9 int stageW = resW * screencols; // HORIZONTAL RES ON VIDEO WALL, HOW MANY MONITORS int stageH = resH * screenrows; // VERTICAL RES ON VIDEO WALL, HOW MANY MONITORS int videoW = 320; // 640; //1280; // DO NOT CHANGE int videoH = 180; // 360; // 720 // DO NOT CHANGE float resRatio = 1366/1280; float HD = 16/9; /////////////////////////////////////////////////////////////////////////////////////// int cols = videoW / ratio; // AMOUNT OF GRID CELLS HORIZONTALLY int rows = videoH / ratio; // AMOUNT OF GRID CELLS VERTICALLY float cW = stageW / cols; // CELL WIDTH float cH = stageH / rows; // CELL HEIGHT float cR = (cW + cH)/2; // CELL DEPTH Capture video; float diffR; float diffG; float diffB; float diffT; int numPixels; int[] previousFrame; float movementSum = 0; ArrayList brightestCellsX; ArrayList brightestCellsY; ArrayList brightestCellsZ; ArrayList brightestCells; ///// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> float newX; float newY; float newZ = 0; // ################################## LOGOS ################## PImage sharp; PImage delite; PImage twopto; // ############################################################ ///// PARTICLE VARS ball p[]; //////////////////////////////// //// ######################## ADJUST TO FIT AMBIENT LIGHT ################## //// ############################# THRESHHOLD LIMIT ######################## //// ################ USE THE LEFT AND RIGHT ARROW KEYS TO CONTROL ######### int th; // threshhold between 0 and 255 //// ######################## ADJUST TO FIT AMBIENT LIGHT ################## ///////// int flagvideo; //// THIS TURNS VIDEO DISPLAY ON AND OFF FOR TESTING PURPOSES int flaglogos; //// THIS SHOWS THE LOGOS //////// int np; // NUMBER OF PARTICLES:::: VERY IMPORTANT //////////////////////////////// void setup() { // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SETUP ////// INITIAL VALUE OF THRESHOLD th = 30; ///// VARIABLES FOR VISIBILITY OF ELEMENTS ON STAGE flagvideo = -1; /// HIDE THE VIDEO BY DEFAULT flaglogos = 1; /// SHOW THE LOGOS BY DEFAULT ////////////////////////////////// size(stageW, stageH, P3D); background(0); /////////////////////////////////////////////////// init IMAGES sharp = loadImage("logo_sharp.jpg"); // 1354, 311 delite = loadImage("logo_delite.jpg"); // 2170, 1676 twopto = loadImage("logo_twopto.png"); // 1950, 1850 /////////////////////////////////////////////////// init video String[] cameras = Capture.list(); if (cameras.length == 0) { println("There are no cameras available for capture."); exit(); } else { println("Available cameras:"); for (int i = 0; i < cameras.length; i++) { println("INDEX = " + i + " // CAMERAS = " + cameras[i]); } } video = new Capture(this, videoW, videoH); // video = new Capture(this, videoW, videoH, "Rocketfish HD Webcam"); frameRate(30); video.start(); /// >>>>>>>>>>>>>>>>>>>>>>>>>>> /// >>>>>>>>>>>>>>>>>>>>>>>>>>> DECLARE ARRAYLISTs TO STORE ALL THE X,Y,Z,B VALUES brightestCellsX = new ArrayList(); brightestCellsY = new ArrayList(); brightestCellsZ = new ArrayList(); brightestCells = new ArrayList(); /// >>>>>>>>>>>>>>>>>>>>>>>>>>> ////////////////////////////////// AMOUNT OF PARTICLES np = 2000; // AMOUNT OF PARTICLES ////////////////////////////////// INIT PARTICLES createParticles(); //////////////////////////////////////////// SETUP AMOUNT OF PIXELS FOR pixels[] ARRAY numPixels = video.width * video.height; // Create an array to store the previously captured frame previousFrame = new int[numPixels]; loadPixels(); } //////// END OF SETUP void createParticles() { // init particles p = new ball[np]; for (int i=0;i 255) { th = 255; } println("THRESHOLD ++ >> " + th); break; //**************************************************************** case 37: /// LEFT ARROW th --; if (th < 1) { th = 1; } println("THRESHOLD -- >> " + th); break; //**************************************************************** case 38: //// UP ARROW => INCREASE THE AMOUNT OF SPHERES - RESETS BUT GOOD FOR TESTING PERFORMANCE np+=20; println("BLOCKS ++ >> " + np); createParticles(); //////// RESET THE AMOUNT OF PARTICLES for (int i=0;i => INCREASE THE AMOUNT OF SPHERES - RESETS BUT GOOD FOR TESTING PERFORMANCE np-=20; println("BLOCKS -- >> " + np); createParticles(); //////// RESET THE AMOUNT OF PARTICLES if (np < 1) { np = 1; } for (int i=0;i SHOW THE LOGOS flaglogos *= -1; break; //**************************************************************** case 86: //// V ====== > SHOW THE VIDEO flagvideo *= -1; break; //**************************************************************** } // END SWITCH } // ############################################################ void draw() { background(0); ///////////// LOGOS if (flaglogos > 0) { pushMatrix(); translate(0,0,20); beginShape(); ///////////////////////////////////// SHARP fill(255); rect((width/2 - width/16),(height - width*0.23/4) ,width/8,width*0.23/8); texture(sharp); endShape(); beginShape(); //////////////////////////////////// DELITE fill(255); rect((width/25),(height - width/10), width*1.294/14,width/14); texture(delite); endShape(); beginShape(); ///////////////////////////////////// 2PT0 fill(255); rect((width - width/7),(height - width*1.054/8) ,width/10,width*1.054/10); texture(twopto); endShape(); popMatrix(); } else { background(0); } ////////////// lights(); noStroke(); video.read(); video.loadPixels(); //// DRAW VIDEO FOR TESTING PURPOSES if ( flagvideo > 0 ) { image(video,0,0, width/9, width/16); } else { image(video,0,0, -320, 90); } // COMMENT WHEN PRESENTING // >>>>>>>>>>>>>>>>>>> START MOTION DETECTION movementSum = 0; // Amount of movement in the frame for (int i = 0; i < numPixels; i++) { // For each pixel in the video frame... color currColor = video.pixels[i]; color prevColor = previousFrame[i]; // Extract the red, green, and blue components from current pixel float currR = (currColor >> 16) & 0xFF; // Like red(), but faster float currG = (currColor >> 8) & 0xFF; float currB = currColor & 0xFF; float currT = brightness(currColor); // Extract red, green, and blue components from previous pixel float prevR = (prevColor >> 16) & 0xFF; float prevG = (prevColor >> 8) & 0xFF; float prevB = prevColor & 0xFF; float prevT = brightness(prevColor); // Compute the difference of the red, green, and blue values diffR = abs(currR - prevR); diffG = abs(currG - prevG); diffB = abs(currB - prevB); diffT = abs(currT - prevT); movementSum = diffT; // Render the difference image to the screen pixels[i] = round(diffT); // Save the current color into the 'previous' buffer previousFrame[i] = currColor; } // >>>>>>>>>>>>>>>>>>> END MOTION DETECTION // >>>>>>>>>>>>>>>>>>> GENERATE PARTICLES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< for (int i=0;i >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // <<<<<<<<<<<<<<<<<<< START MOTION SENSITIVITY >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> if (movementSum <= 0) { brightestCellsX.clear(); brightestCellsY.clear(); brightestCellsZ.clear(); brightestCells.clear(); } else { for (int j=0; j < video.height; j+=ratio) { for (int k=0; k < video.width; k+=ratio) { int loc = (video.width - k - 1) + (j*video.width); // MIRROR float c = pixels[loc]; if ( c > th ) { // GRAYSCALE BRIGHTNESS > THRESHHOLD newX = k * stageW/videoW; newY = j * stageH/videoH; // DETERMINE AREA WITH HIGHEST CONTRAST THEREFORE HIGHEST MOTION brightestCellsX.add(new Float(newX)); brightestCellsY.add(new Float(newY)); brightestCellsZ.add(new Float(newZ)); brightestCells.add(c); /////////////////////////////////////////// DRAWS THE GREEN CELLS switch(flag) { case 1: fill(0, 255 , 0); noStroke(); pushMatrix(); translate(newX, newY, newZ); box(int(cW*3/4),int(cR*3/4),1); popMatrix(); break; case -1: break; } } // END PIXELS loc } } } }