├── README.md ├── aura ├── DataUpload.java ├── PolygonBlob.pde ├── README.md ├── aura.pde └── facebook.png └── socialflow ├── DataUpload.java ├── PolygonBlob.pde ├── README.md ├── bg.jpg ├── facebook.png ├── particle.pde ├── script.php ├── shiftBlur3.pde ├── snapshot.jpeg └── socialflow.pde /README.md: -------------------------------------------------------------------------------- 1 | Kinect Projects 2 | ================================= 3 | 4 | This repository hosts Kinect based projects I have experimented with 5 | 6 | socialflow 7 | ----------- 8 | 9 | A modified and extended version of flowing particles to represent humans using glowing light, this project also allows users to have a basic interaction with the screen and upload user generated snapshots to a Facebook album 10 | 11 | aura 12 | ----- 13 | 14 | A visualization of auras around people, this project also allows users to have a basic interaction with the screen and upload user generated snapshots to a Facebook album -------------------------------------------------------------------------------- /aura/DataUpload.java: -------------------------------------------------------------------------------- 1 | /* Upload script to convert the Image to a POST request 2 | * Courtesy philho 3 | * Source http://bazaar.launchpad.net/~philho/+junk/Processing/files/head:/ImageUpload/ 4 | */ 5 | 6 | import java.net.*; 7 | import java.io.*; 8 | import javax.imageio.*; 9 | import javax.imageio.stream.*; 10 | import java.awt.image.BufferedImage; 11 | 12 | class DataUpload 13 | { 14 | /** The field name as expected by the PHP script, equivalent to the name in the tag input type="file" 15 | * in an HTML upload form. 16 | */ 17 | private static final String FIELD_NAME = "image"; 18 | /** PHP script name. */ 19 | // I hard-code it here, I suppose there is no need for several scripts per applet... 20 | // and source can be edited for your own sketch. 21 | // Can be easily transformed into a mutable field specified at construction time or with a setter 22 | private static final String SCRIPT_NAME = "script.php"; 23 | /** URL path to the PHP script. */ 24 | // Same remark here: hardcoded for usage within a given sketch 25 | private static final String BASE_URL = "http://yourwebsite.com/extension/"; 26 | /** A computed, hopefully unique (not found in data) Mime boundary string, 27 | * separating the various parts of the message. 28 | */ 29 | private String boundary; 30 | /** Made of the URL and the server script name. Can add parameters too. */ 31 | private String uploadURL; 32 | /** The connection to the server. */ 33 | private HttpURLConnection connection; 34 | /** The output stream to write the binary data. */ 35 | private DataOutputStream output; 36 | 37 | DataUpload() 38 | { 39 | // Mime boundary of the various parts of the message. 40 | boundary = "-----MyuploadcodeforLuxInstallation---Yourrandomstringhere-----" + System.currentTimeMillis(); 41 | // We can add optional parameters, eg. a string given by the user, parameters used, etc. 42 | uploadURL = BASE_URL + "/" + SCRIPT_NAME;// + "?optionalParam=value&foo=bar"; 43 | } 44 | 45 | /** Pushes any binary data to server. */ 46 | boolean UploadBinaryData(String fileName, String dataMimeType, byte[] data) 47 | { 48 | try 49 | { 50 | boolean isOK = StartPOSTRequest(fileName, dataMimeType); 51 | if (!isOK) 52 | return false; 53 | 54 | // Spit out the data bytes at once 55 | output.write(data, 0, data.length); // throws IOException 56 | 57 | // And actually do the send (flush output and close it) 58 | EndPOSTRequest(); 59 | } 60 | catch (Exception e) 61 | { 62 | e.printStackTrace(); 63 | return false; // Problem 64 | } 65 | finally 66 | { 67 | if (output != null) 68 | { 69 | try { output.close(); } catch (IOException ioe) {} 70 | } 71 | } 72 | 73 | return true; // OK 74 | } 75 | 76 | /** Pushes image to server. Similar to UploadBinaryData but is given a BufferedImage 77 | * and guesses the Mime type with the file name extension. 78 | */ 79 | boolean UploadImage(String fileName, BufferedImage image) 80 | { 81 | String imageType = null, imageMimeType = null; 82 | boolean bUseOtherMethod = false; 83 | if (fileName.endsWith("png")) 84 | { 85 | imageType = "png"; 86 | imageMimeType = "image/png"; 87 | } 88 | else if (fileName.endsWith("jpg")) 89 | { 90 | imageType = "jpg"; 91 | imageMimeType = "image/jpeg"; 92 | } 93 | else if (fileName.endsWith("jpeg")) 94 | { 95 | imageType = "jpeg"; 96 | imageMimeType = "image/jpeg"; 97 | bUseOtherMethod = true; 98 | } 99 | else 100 | { 101 | return false; // Unsupported image format 102 | } 103 | 104 | try 105 | { 106 | boolean isOK = StartPOSTRequest(fileName, imageMimeType); 107 | if (!isOK) 108 | return false; 109 | 110 | // Output the encoded image data 111 | if (!bUseOtherMethod) 112 | { 113 | // Uses the default method 114 | ImageIO.write(image, imageType, output); 115 | } 116 | else 117 | { 118 | // Alternative for better Jpeg quality control 119 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 120 | 121 | java.util.Iterator iter = ImageIO.getImageWritersByFormatName(imageType); 122 | if (iter.hasNext()) 123 | { 124 | ImageWriter writer = (ImageWriter) iter.next(); 125 | ImageWriteParam iwp = writer.getDefaultWriteParam(); 126 | iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 127 | iwp.setCompressionQuality(1.0f); 128 | 129 | ImageOutputStream ios = new MemoryCacheImageOutputStream(baos); 130 | writer.setOutput(ios); 131 | writer.write(image); 132 | byte[] b = baos.toByteArray(); 133 | output.write(b, 0, b.length); 134 | } 135 | } 136 | 137 | // And actually do the send (flush output and close it) 138 | EndPOSTRequest(); 139 | } 140 | catch (Exception e) 141 | { 142 | e.printStackTrace(); 143 | return false; // Problem 144 | } 145 | finally 146 | { 147 | if (output != null) 148 | { 149 | try { output.close(); } catch (IOException ioe) {} 150 | } 151 | } 152 | 153 | return true; // OK 154 | } 155 | 156 | /** Reads output from server. */ 157 | String GetServerFeedback() 158 | { 159 | if (connection == null) 160 | { 161 | // ERROR: Can't get server feedback without first uploading data! 162 | return null; 163 | } 164 | BufferedReader input = null; 165 | StringBuffer answer = new StringBuffer(); 166 | try 167 | { 168 | input = new BufferedReader(new InputStreamReader(connection.getInputStream())); 169 | String answerLine = null; 170 | do 171 | { 172 | answerLine = input.readLine(); 173 | if (answerLine != null) 174 | { 175 | answer.append(answerLine + "\n"); 176 | } 177 | } while (answerLine != null); 178 | } 179 | catch (Exception e) 180 | { 181 | // Can display some feedback to user there, or just ignore the issue 182 | e.printStackTrace(); 183 | return null; // Problem 184 | } 185 | finally 186 | { 187 | if (input != null) 188 | { 189 | try { input.close(); } catch (IOException ioe) {} 190 | } 191 | } 192 | 193 | return answer.toString(); 194 | } 195 | 196 | int GetResponseCode() 197 | { 198 | int responseCode = -1; 199 | if (connection == null) 200 | { 201 | // ERROR: Can't get server response without first uploading data! 202 | return -1; 203 | } 204 | // Note that 200 means OK 205 | try 206 | { 207 | responseCode = connection.getResponseCode(); 208 | } 209 | catch (IOException ioe) 210 | { 211 | } 212 | return responseCode; 213 | } 214 | 215 | /*-- Private section --*/ 216 | 217 | private boolean StartPOSTRequest(String fileName, String dataMimeType) 218 | { 219 | try 220 | { 221 | URL url = new URL(uploadURL); // throws MalformedURLException 222 | connection = (HttpURLConnection) url.openConnection(); // throws IOException 223 | // connection is probably of HttpURLConnection type now 224 | 225 | connection.setDoOutput(true); // We output stuff 226 | connection.setRequestMethod("POST"); // With POST method 227 | connection.setDoInput(true); // We want feedback! 228 | connection.setUseCaches(false); // No cache, it is (supposed to be) a new image each time, even if URL is always the same 229 | 230 | // Post multipart data 231 | // Set request headers 232 | // Might put something like "Content-Length: 8266" 233 | connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); 234 | // throws IllegalStateException, NullPointerException 235 | 236 | // Open a stream which can write to the URL 237 | output = new DataOutputStream(connection.getOutputStream()); 238 | // the get throws IOException, UnknownServiceException 239 | 240 | // Write content to the server, begin with the tag that says a content element is coming 241 | output.writeBytes("--" + boundary + "\r\n"); // throws IOException 242 | 243 | // Describe the content: 244 | // filename isn't really important here, it is probably ignored by the upload script, or can be set to user name if logged in 245 | output.writeBytes("Content-Disposition: form-data; name=\"" + FIELD_NAME + 246 | "\"; filename=\"" + fileName + "\"\r\n"); 247 | // Mime type of the data, like image/jpeg or image/png 248 | // Likely to be ignored by the PHP script (which can't trust such external info) but (might be) mandatory and nice to indicate anyway 249 | output.writeBytes("Content-Type: " + dataMimeType + "\r\n"); 250 | // By default it is Base64 encoding (that's what most browsers use), but here we don't use this, 251 | // for simplicity sake and because it is less data to transmit. As long as destination server understands it... 252 | // See http://www.freesoft.org/CIE/RFC/1521/5.htm for details 253 | output.writeBytes("Content-Transfer-Encoding: binary\r\n\r\n"); 254 | } 255 | catch (Exception e) // Indistinctly catch all kinds of exceptions this code can throw at us 256 | { 257 | // Can display some feedback to user there, or just ignore the issue 258 | e.printStackTrace(); 259 | return false; // Problem 260 | } 261 | 262 | return true; 263 | } 264 | 265 | private boolean EndPOSTRequest() 266 | { 267 | try 268 | { 269 | // Close the multipart form request 270 | output.writeBytes("\r\n--" + boundary + "--\r\n\r\n"); 271 | 272 | // And actually do the send (flush output and close it) 273 | output.flush(); // throws IOException 274 | } 275 | catch (Exception e) // Indistinctly catch all kinds of exceptions this code can throw at us 276 | { 277 | // Can display some feedback to user there, or just ignore the issue 278 | e.printStackTrace(); 279 | return false; // Problem 280 | } 281 | 282 | return true; 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /aura/PolygonBlob.pde: -------------------------------------------------------------------------------- 1 | // an extended polygon class quite similar to the earlier PolygonBlob class (but extending Toxiclibs' Polygon2D class instead) 2 | // Courtesy Amnon Owed 3 | 4 | class PolygonBlob extends Polygon2D { 5 | // the createPolygon() method is nearly identical to the one presented earlier 6 | // see the Kinect Flow Example for a more detailed description of this method (again, feel free to improve it) 7 | void createPolygon() { 8 | ArrayList> contours = new ArrayList>(); 9 | int selectedContour = 0; 10 | int selectedPoint = 0; 11 | 12 | // create contours from blobs 13 | for (int n=0 ; n 100) { 16 | ArrayList contour = new ArrayList(); 17 | for (int m=0; m 15 || dp > 15) { 26 | if (contour.size() > 0) { 27 | contour.add(new PVector(eB.x*kinectWidth, eB.y*kinectHeight)); 28 | contours.add(contour); 29 | contour = new ArrayList(); 30 | } else { 31 | contour.add(new PVector(eA.x*kinectWidth, eA.y*kinectHeight)); 32 | } 33 | } else { 34 | contour.add(new PVector(eA.x*kinectWidth, eA.y*kinectHeight)); 35 | } 36 | } 37 | } 38 | } 39 | } 40 | 41 | while (contours.size() > 0) { 42 | 43 | // find next contour 44 | float distance = 999999999; 45 | if (getNumPoints() > 0) { 46 | Vec2D vecLastPoint = vertices.get(getNumPoints()-1); 47 | PVector lastPoint = new PVector(vecLastPoint.x, vecLastPoint.y); 48 | for (int i=0; i c = contours.get(i); 50 | PVector fp = c.get(0); 51 | PVector lp = c.get(c.size()-1); 52 | if (fp.dist(lastPoint) < distance) { 53 | distance = fp.dist(lastPoint); 54 | selectedContour = i; 55 | selectedPoint = 0; 56 | } 57 | if (lp.dist(lastPoint) < distance) { 58 | distance = lp.dist(lastPoint); 59 | selectedContour = i; 60 | selectedPoint = 1; 61 | } 62 | } 63 | } else { 64 | PVector closestPoint = new PVector(width, height); 65 | for (int i=0; i c = contours.get(i); 67 | PVector fp = c.get(0); 68 | PVector lp = c.get(c.size()-1); 69 | if (fp.y > kinectHeight-5 && fp.x < closestPoint.x) { 70 | closestPoint = fp; 71 | selectedContour = i; 72 | selectedPoint = 0; 73 | } 74 | if (lp.y > kinectHeight-5 && lp.x < closestPoint.y) { 75 | closestPoint = lp; 76 | selectedContour = i; 77 | selectedPoint = 1; 78 | } 79 | } 80 | } 81 | 82 | // add contour to polygon 83 | ArrayList contour = contours.get(selectedContour); 84 | if (selectedPoint > 0) { Collections.reverse(contour); } 85 | for (PVector p : contour) { 86 | add(new Vec2D(p.x, p.y)); 87 | } 88 | contours.remove(selectedContour); 89 | } 90 | } 91 | 92 | // ================================================== 93 | // drawBlobsAndEdges() from the Processing forums 94 | // ================================================== 95 | void drawBlobsAndEdges(boolean drawBlobs, boolean drawEdges) 96 | { 97 | noFill(); 98 | Blob b; 99 | EdgeVertex eA,eB; 100 | 101 | int iPolyCount=0; 102 | 103 | for (int n=0 ; n0) { 102 | { 103 | user1 = kinect.getUsersPixels(SimpleOpenNI.USERS_ALL); // find out which pixels have users in them 104 | blobs.pixels = user1 ; // clean up blobs.pixels 105 | for (int i = 0; i < user1.length; i++) { // populate the pixels array from the sketch's current contents 106 | if (user1[i] != 0) { // if the current pixel is on a user 107 | blobs.pixels[i] = 255; // make it white and distinguishable 108 | } 109 | else 110 | { 111 | blobs.pixels[i] = 0; 112 | } 113 | } 114 | 115 | theBlobDetection.computeBlobs(blobs.pixels); 116 | poly = new PolygonBlob(); // initialize a new polygon 117 | poly.createPolygon(); // create the polygon from the blobs (custom functionality, see class) 118 | polyglow(gfx, poly); // Shine, baby ! 119 | } 120 | } 121 | } 122 | 123 | // Helper method to initialize the P2D layer to glow mode thanks to flight404 124 | 125 | void setupgl() 126 | { 127 | pgl = (PGraphicsOpenGL) g; 128 | GL gl = pgl.beginPGL().gl; // JOGL's GL object 129 | gl.glDisable(GL.GL_DEPTH_TEST); // This fixes the overlap issue 130 | gl.glEnable(GL.GL_BLEND); // Turn on the blend mode 131 | gl.glBlendFunc(GL.GL_SRC_ALPHA,GL.GL_ONE); // Define the blend mode 132 | gl.glClear(GL.GL_DEPTH_BUFFER_BIT); 133 | } 134 | 135 | // A function to easily setup features required from the Kinect OpenNI interface 136 | 137 | void setupKinect(SimpleOpenNI kinect) 138 | { 139 | kinect.setMirror(true); // mirror the image to make the display more intuitive 140 | kinect.enableUser(SimpleOpenNI.SKEL_PROFILE_NONE); // We need to enable user tracking but do not use skeleton joints here 141 | kinect.enableGesture(); // Enable Gesture tracking to be used to find the hand the first time 142 | kinect.enableHands(); // Enable hand tracking to be used for "Share on Facebook" feature 143 | kinect.addGesture("RaiseHand"); // Tell the kinect that we would like it to report a Raised Hand gesture 144 | } 145 | 146 | 147 | // An experiment to make a body "glow" 148 | void polyglow(ToxiclibsSupport gfx, PolygonBlob poly) 149 | { 150 | noFill(); 151 | tint(255, 255); 152 | strokeWeight(10); 153 | stroke(250, 250, 52); 154 | scale(reScale); 155 | gfx.polygon2D(poly); 156 | blendMode(ADD); 157 | dispimg = get(); 158 | scale(1/reScale); 159 | dispimg.resize(1600/4, 0); 160 | dispimg.filter(BLUR, 2); 161 | dispimg.resize(1600, 0); 162 | tint(255, 180); 163 | image(dispimg,0,0); 164 | } 165 | 166 | // SimpleOpenNI callback method when the Kinect "loses" a user 167 | 168 | void onLostUser(int userId) 169 | { 170 | println("Lost the user " + userId); 171 | kinect.stopTrackingSkeleton(userId); 172 | if(nou_old>0) nou_old -=1; // Shouldn't be needing this but there do seem to be some glitches, so necessary not to kill people unnecessarily 173 | println("Number of users " + nou_old); 174 | } 175 | 176 | // SimpleOpenNI callback method when the Kinect finds a user it was tracking is gone for more than 10 seconds, counts as an exit 177 | 178 | void onExitUser(int userId) 179 | { 180 | println("User exit " + userId); 181 | kinect.stopTrackingSkeleton(userId); // Stop reminiscing, it hurts 182 | if(nou_old>0) nou_old -= 1; 183 | print("Number of users " + nou_old); 184 | } 185 | 186 | // SimpleOpenNI callback method when the Kinect finds a new user 187 | 188 | void onNewUser(int userId) { 189 | println("start pose detection for" + userId); 190 | nou_old += 1; // We have a newcomer in our midst 191 | println("Number of users " + nou_old); 192 | //kinect.requestCalibrationSkeleton(userId,true); // Still unable to decide whether this is necessary to make tracking more reliable 193 | } 194 | 195 | 196 | // ----------------------------------------------------------------- 197 | // hand events 5 198 | 199 | void onCreateHands(int handId, PVector position, float time) { 200 | kinect.convertRealWorldToProjective(position, position); 201 | println("Found hands"); 202 | } 203 | 204 | void onUpdateHands(int handId, PVector position, float time) { 205 | PVector p = new PVector(); 206 | if(position.x>kinectWidth*0.8 && position.y>0.75*kinectHeight) 207 | { 208 | if(canUpload) // Wouldn't want to flood Facebook with another post when this one hasn't gone through, would we ? 209 | { 210 | canUpload = false; 211 | println("Clicking"); 212 | new Poster(4); 213 | } 214 | } 215 | }oh wait, i'll ju 216 | 217 | void onDestroyHands(int handId, float time) { 218 | kinect.addGesture("RaiseHand"); 219 | } 220 | 221 | // ----------------------------------------------------------------- 222 | // gesture events 6 223 | void onRecognizeGesture(String strGesture,PVector idPosition,PVector endPosition) 224 | { 225 | println("Recognized Gesture"); 226 | kinect.startTrackingHands(endPosition); 227 | kinect.removeGesture("RaiseHand"); 228 | } 229 | 230 | // A method to post the image to your own server. Thanks a ton to philho for this 231 | // Use this method to post the image to your own server. You can then use a script there to store, manipulate and post images to Facebook, twitter etc 232 | 233 | void post(Timer tim) 234 | { 235 | DataUpload du = new DataUpload(); 236 | boolean bOK = false; 237 | // Upload the currently displayed image with a fixed name, and the chosen format 238 | // We need a new buffered image without the alpha channel 239 | BufferedImage imageNoAlpha = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 240 | loadPixels(); 241 | imageNoAlpha.setRGB(0, 0, width, height, g.pixels, 0, width); 242 | // Ideally shouldn't be needing this section but something I have done posts all the images inverted, so flipping them back 243 | BufferedImage dimg = new BufferedImage(width,height,imageNoAlpha.getColorModel().getTransparency()); 244 | Graphics2D g2 = dimg.createGraphics(); 245 | g2.drawImage(imageNoAlpha,0,0,width,height,0,height,width,0,null); 246 | g2.dispose(); 247 | // end of inversion section 248 | bOK = du.UploadImage("snapshot.jpeg", dimg); 249 | if (!bOK) 250 | return; // Some problem on Java side. Do nothing 251 | 252 | // Get the answer of the PHP script 253 | int rc = du.GetResponseCode(); 254 | String feedback = du.GetServerFeedback(); 255 | println("----- " + rc + " -----\n" + feedback + "---------------"); 256 | // A flash of white light, like that at the moment of creation. Of a snapshot. 257 | fill(255); 258 | rect(0,0,width,height); 259 | tim.cancel(); // Cancel the timer, we are done with it 260 | } 261 | 262 | // A class to handle posting data to a web server 263 | 264 | public class Poster{ 265 | Timer tim; 266 | 267 | public Poster(int seconds) 268 | { 269 | tim = new Timer(); 270 | tim.schedule(new PostTask(),seconds*1000); 271 | } 272 | class PostTask extends TimerTask { 273 | 274 | public void run() { 275 | println("Time's up!"); 276 | post(tim); // Passing the timer as a parameter to cancel the timer in the posting method. Was getting system crashes otherwise 277 | canUpload = true; // Re-enable upload, we just posted one 278 | } 279 | } 280 | } -------------------------------------------------------------------------------- /aura/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitdaf/kinect/1f5179b01e69844af4e8b143c24b144e755c67b9/aura/facebook.png -------------------------------------------------------------------------------- /socialflow/DataUpload.java: -------------------------------------------------------------------------------- 1 | /* Upload script to convert the Image to a POST request 2 | * Courtesy philho 3 | * Source http://bazaar.launchpad.net/~philho/+junk/Processing/files/head:/ImageUpload/ 4 | */ 5 | 6 | import java.net.*; 7 | import java.io.*; 8 | import javax.imageio.*; 9 | import javax.imageio.stream.*; 10 | import java.awt.image.BufferedImage; 11 | 12 | class DataUpload 13 | { 14 | /** The field name as expected by the PHP script, equivalent to the name in the tag input type="file" 15 | * in an HTML upload form. 16 | */ 17 | private static final String FIELD_NAME = "image"; 18 | /** PHP script name. */ 19 | // I hard-code it here, I suppose there is no need for several scripts per applet... 20 | // and source can be edited for your own sketch. 21 | // Can be easily transformed into a mutable field specified at construction time or with a setter 22 | private static final String SCRIPT_NAME = "script.php"; 23 | /** URL path to the PHP script. */ 24 | // Same remark here: hardcoded for usage within a given sketch 25 | private static final String BASE_URL = "http://yourwebsite.com/extension/"; 26 | /** A computed, hopefully unique (not found in data) Mime boundary string, 27 | * separating the various parts of the message. 28 | */ 29 | private String boundary; 30 | /** Made of the URL and the server script name. Can add parameters too. */ 31 | private String uploadURL; 32 | /** The connection to the server. */ 33 | private HttpURLConnection connection; 34 | /** The output stream to write the binary data. */ 35 | private DataOutputStream output; 36 | 37 | DataUpload() 38 | { 39 | // Mime boundary of the various parts of the message. 40 | boundary = "-----MyuploadcodeforLuxInstallation---Yourrandomstringhere-----" + System.currentTimeMillis(); 41 | // We can add optional parameters, eg. a string given by the user, parameters used, etc. 42 | uploadURL = BASE_URL + "/" + SCRIPT_NAME;// + "?optionalParam=value&foo=bar"; 43 | } 44 | 45 | /** Pushes any binary data to server. */ 46 | boolean UploadBinaryData(String fileName, String dataMimeType, byte[] data) 47 | { 48 | try 49 | { 50 | boolean isOK = StartPOSTRequest(fileName, dataMimeType); 51 | if (!isOK) 52 | return false; 53 | 54 | // Spit out the data bytes at once 55 | output.write(data, 0, data.length); // throws IOException 56 | 57 | // And actually do the send (flush output and close it) 58 | EndPOSTRequest(); 59 | } 60 | catch (Exception e) 61 | { 62 | e.printStackTrace(); 63 | return false; // Problem 64 | } 65 | finally 66 | { 67 | if (output != null) 68 | { 69 | try { output.close(); } catch (IOException ioe) {} 70 | } 71 | } 72 | 73 | return true; // OK 74 | } 75 | 76 | /** Pushes image to server. Similar to UploadBinaryData but is given a BufferedImage 77 | * and guesses the Mime type with the file name extension. 78 | */ 79 | boolean UploadImage(String fileName, BufferedImage image) 80 | { 81 | String imageType = null, imageMimeType = null; 82 | boolean bUseOtherMethod = false; 83 | if (fileName.endsWith("png")) 84 | { 85 | imageType = "png"; 86 | imageMimeType = "image/png"; 87 | } 88 | else if (fileName.endsWith("jpg")) 89 | { 90 | imageType = "jpg"; 91 | imageMimeType = "image/jpeg"; 92 | } 93 | else if (fileName.endsWith("jpeg")) 94 | { 95 | imageType = "jpeg"; 96 | imageMimeType = "image/jpeg"; 97 | bUseOtherMethod = true; 98 | } 99 | else 100 | { 101 | return false; // Unsupported image format 102 | } 103 | 104 | try 105 | { 106 | boolean isOK = StartPOSTRequest(fileName, imageMimeType); 107 | if (!isOK) 108 | return false; 109 | 110 | // Output the encoded image data 111 | if (!bUseOtherMethod) 112 | { 113 | // Uses the default method 114 | ImageIO.write(image, imageType, output); 115 | } 116 | else 117 | { 118 | // Alternative for better Jpeg quality control 119 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 120 | 121 | java.util.Iterator iter = ImageIO.getImageWritersByFormatName(imageType); 122 | if (iter.hasNext()) 123 | { 124 | ImageWriter writer = (ImageWriter) iter.next(); 125 | ImageWriteParam iwp = writer.getDefaultWriteParam(); 126 | iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 127 | iwp.setCompressionQuality(1.0f); 128 | 129 | ImageOutputStream ios = new MemoryCacheImageOutputStream(baos); 130 | writer.setOutput(ios); 131 | writer.write(image); 132 | byte[] b = baos.toByteArray(); 133 | output.write(b, 0, b.length); 134 | } 135 | } 136 | 137 | // And actually do the send (flush output and close it) 138 | EndPOSTRequest(); 139 | } 140 | catch (Exception e) 141 | { 142 | e.printStackTrace(); 143 | return false; // Problem 144 | } 145 | finally 146 | { 147 | if (output != null) 148 | { 149 | try { output.close(); } catch (IOException ioe) {} 150 | } 151 | } 152 | 153 | return true; // OK 154 | } 155 | 156 | /** Reads output from server. */ 157 | String GetServerFeedback() 158 | { 159 | if (connection == null) 160 | { 161 | // ERROR: Can't get server feedback without first uploading data! 162 | return null; 163 | } 164 | BufferedReader input = null; 165 | StringBuffer answer = new StringBuffer(); 166 | try 167 | { 168 | input = new BufferedReader(new InputStreamReader(connection.getInputStream())); 169 | String answerLine = null; 170 | do 171 | { 172 | answerLine = input.readLine(); 173 | if (answerLine != null) 174 | { 175 | answer.append(answerLine + "\n"); 176 | } 177 | } while (answerLine != null); 178 | } 179 | catch (Exception e) 180 | { 181 | // Can display some feedback to user there, or just ignore the issue 182 | e.printStackTrace(); 183 | return null; // Problem 184 | } 185 | finally 186 | { 187 | if (input != null) 188 | { 189 | try { input.close(); } catch (IOException ioe) {} 190 | } 191 | } 192 | 193 | return answer.toString(); 194 | } 195 | 196 | int GetResponseCode() 197 | { 198 | int responseCode = -1; 199 | if (connection == null) 200 | { 201 | // ERROR: Can't get server response without first uploading data! 202 | return -1; 203 | } 204 | // Note that 200 means OK 205 | try 206 | { 207 | responseCode = connection.getResponseCode(); 208 | } 209 | catch (IOException ioe) 210 | { 211 | } 212 | return responseCode; 213 | } 214 | 215 | /*-- Private section --*/ 216 | 217 | private boolean StartPOSTRequest(String fileName, String dataMimeType) 218 | { 219 | try 220 | { 221 | URL url = new URL(uploadURL); // throws MalformedURLException 222 | connection = (HttpURLConnection) url.openConnection(); // throws IOException 223 | // connection is probably of HttpURLConnection type now 224 | 225 | connection.setDoOutput(true); // We output stuff 226 | connection.setRequestMethod("POST"); // With POST method 227 | connection.setDoInput(true); // We want feedback! 228 | connection.setUseCaches(false); // No cache, it is (supposed to be) a new image each time, even if URL is always the same 229 | 230 | // Post multipart data 231 | // Set request headers 232 | // Might put something like "Content-Length: 8266" 233 | connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); 234 | // throws IllegalStateException, NullPointerException 235 | 236 | // Open a stream which can write to the URL 237 | output = new DataOutputStream(connection.getOutputStream()); 238 | // the get throws IOException, UnknownServiceException 239 | 240 | // Write content to the server, begin with the tag that says a content element is coming 241 | output.writeBytes("--" + boundary + "\r\n"); // throws IOException 242 | 243 | // Describe the content: 244 | // filename isn't really important here, it is probably ignored by the upload script, or can be set to user name if logged in 245 | output.writeBytes("Content-Disposition: form-data; name=\"" + FIELD_NAME + 246 | "\"; filename=\"" + fileName + "\"\r\n"); 247 | // Mime type of the data, like image/jpeg or image/png 248 | // Likely to be ignored by the PHP script (which can't trust such external info) but (might be) mandatory and nice to indicate anyway 249 | output.writeBytes("Content-Type: " + dataMimeType + "\r\n"); 250 | // By default it is Base64 encoding (that's what most browsers use), but here we don't use this, 251 | // for simplicity sake and because it is less data to transmit. As long as destination server understands it... 252 | // See http://www.freesoft.org/CIE/RFC/1521/5.htm for details 253 | output.writeBytes("Content-Transfer-Encoding: binary\r\n\r\n"); 254 | } 255 | catch (Exception e) // Indistinctly catch all kinds of exceptions this code can throw at us 256 | { 257 | // Can display some feedback to user there, or just ignore the issue 258 | e.printStackTrace(); 259 | return false; // Problem 260 | } 261 | 262 | return true; 263 | } 264 | 265 | private boolean EndPOSTRequest() 266 | { 267 | try 268 | { 269 | // Close the multipart form request 270 | output.writeBytes("\r\n--" + boundary + "--\r\n\r\n"); 271 | 272 | // And actually do the send (flush output and close it) 273 | output.flush(); // throws IOException 274 | } 275 | catch (Exception e) // Indistinctly catch all kinds of exceptions this code can throw at us 276 | { 277 | // Can display some feedback to user there, or just ignore the issue 278 | e.printStackTrace(); 279 | return false; // Problem 280 | } 281 | 282 | return true; 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /socialflow/PolygonBlob.pde: -------------------------------------------------------------------------------- 1 | // an extended polygon class with my own customized createPolygon() method (feel free to improve!) 2 | // Courtesy Amnon Owed 3 | 4 | class PolygonBlob extends Polygon { 5 | 6 | // took me some time to make this method fully self-sufficient 7 | // now it works quite well in creating a correct polygon from a person's blob 8 | // of course many thanks to v3ga, because the library already does a lot of the work 9 | void createPolygon() { 10 | // an arrayList... of arrayLists... of PVectors 11 | // the arrayLists of PVectors are basically the person's contours (almost but not completely in a polygon-correct order) 12 | ArrayList> contours = new ArrayList>(); 13 | // helpful variables to keep track of the selected contour and point (start/end point) 14 | int selectedContour = 0; 15 | int selectedPoint = 0; 16 | 17 | // create contours from blobs 18 | // go over all the detected blobs 19 | for (int n=0 ; n 100) { 23 | // create a new contour arrayList of PVectors 24 | ArrayList contour = new ArrayList(); 25 | // go over all the edges in the blob 26 | for (int m=0; m 15 || dp > 15) { 41 | // if the current contour size is bigger than zero 42 | if (contour.size() > 0) { 43 | // add final point 44 | contour.add(new PVector(eB.x*kinectWidth, eB.y*kinectHeight)); 45 | // add current contour to the arrayList 46 | contours.add(contour); 47 | // start a new contour arrayList 48 | contour = new ArrayList(); 49 | // if the current contour size is 0 (aka it's a new list) 50 | } else { 51 | // add the point to the list 52 | contour.add(new PVector(eA.x*kinectWidth, eA.y*kinectHeight)); 53 | } 54 | // if both distance are smaller than 15 (aka the points are close) 55 | } else { 56 | // add the point to the list 57 | contour.add(new PVector(eA.x*kinectWidth, eA.y*kinectHeight)); 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | // at this point in the code we have a list of contours (aka an arrayList of arrayLists of PVectors) 65 | // now we need to sort those contours into a correct polygon. To do this we need two things: 66 | // 1. The correct order of contours 67 | // 2. The correct direction of each contour 68 | 69 | // as long as there are contours left... 70 | while (contours.size() > 0) { 71 | 72 | // find next contour 73 | float distance = 999999999; 74 | // if there are already points in the polygon 75 | if (npoints > 0) { 76 | // use the polygon's last point as a starting point 77 | PVector lastPoint = new PVector(xpoints[npoints-1], ypoints[npoints-1]); 78 | // go over all contours 79 | for (int i=0; i c = contours.get(i); 81 | // get the contour's first point 82 | PVector fp = c.get(0); 83 | // get the contour's last point 84 | PVector lp = c.get(c.size()-1); 85 | // if the distance between the current contour's first point and the polygon's last point is smaller than distance 86 | if (fp.dist(lastPoint) < distance) { 87 | // set distance to this distance 88 | distance = fp.dist(lastPoint); 89 | // set this as the selected contour 90 | selectedContour = i; 91 | // set selectedPoint to 0 (which signals first point) 92 | selectedPoint = 0; 93 | } 94 | // if the distance between the current contour's last point and the polygon's last point is smaller than distance 95 | if (lp.dist(lastPoint) < distance) { 96 | // set distance to this distance 97 | distance = lp.dist(lastPoint); 98 | // set this as the selected contour 99 | selectedContour = i; 100 | // set selectedPoint to 1 (which signals last point) 101 | selectedPoint = 1; 102 | } 103 | } 104 | // if the polygon is still empty 105 | } else { 106 | // use a starting point in the lower-right 107 | PVector closestPoint = new PVector(width, height); 108 | // go over all contours 109 | for (int i=0; i c = contours.get(i); 111 | // get the contour's first point 112 | PVector fp = c.get(0); 113 | // get the contour's last point 114 | PVector lp = c.get(c.size()-1); 115 | // if the first point is in the lowest 5 pixels of the (kinect) screen and more to the left than the current closestPoint 116 | if (fp.y > kinectHeight-5 && fp.x < closestPoint.x) { 117 | // set closestPoint to first point 118 | closestPoint = fp; 119 | // set this as the selected contour 120 | selectedContour = i; 121 | // set selectedPoint to 0 (which signals first point) 122 | selectedPoint = 0; 123 | } 124 | // if the last point is in the lowest 5 pixels of the (kinect) screen and more to the left than the current closestPoint 125 | if (lp.y > kinectHeight-5 && lp.x < closestPoint.y) { 126 | // set closestPoint to last point 127 | closestPoint = lp; 128 | // set this as the selected contour 129 | selectedContour = i; 130 | // set selectedPoint to 1 (which signals last point) 131 | selectedPoint = 1; 132 | } 133 | } 134 | } 135 | 136 | // add contour to polygon 137 | ArrayList contour = contours.get(selectedContour); 138 | // if selectedPoint is bigger than zero (aka last point) then reverse the arrayList of points 139 | if (selectedPoint > 0) { Collections.reverse(contour); } 140 | // add all the points in the contour to the polygon 141 | for (PVector p : contour) { 142 | addPoint(int(p.x), int(p.y)); 143 | } 144 | // remove this contour from the list of contours 145 | contours.remove(selectedContour); 146 | // the while loop above makes all of this code loop until the number of contours is zero 147 | // at that time all the points in all the contours have been added to the polygon... in the correct order (hopefully) 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /socialflow/README.md: -------------------------------------------------------------------------------- 1 | LUX Installation - Social Kinect 2 | ================================= 3 | 4 | A Kinect based light visualization of people for interactive fun installations 5 | 6 | What it can do 7 | -------------- 8 | This application visualizes people as glowing light particles and allows them to post images of them in action on Facebook 9 | 10 | What you need to do 11 | -------------------- 12 | 13 | - Upload the server script to a PHP web server 14 | - Upload the Facebook PHP SDK ( Get it here : https://github.com/facebook/facebook-php-sdk/tree/master/src ) 15 | - Generate the necessary Facebook Page tokens for permissions to post the site 16 | - Setup your Kinect and start your processing Sketch (tested working on 2.0b7 , some display issues on 2.0b6) 17 | 18 | 19 | Requirements 20 | ------------- 21 | 22 | - Microsoft Kinect 23 | - Processing IDE 24 | - JRE 25 | - ControlP5 Processing library 26 | - SimpleOpenNI library 27 | 28 | Thanks 29 | ------ 30 | 31 | Many thanks to 32 | 33 | - Amnon Owed for his tutorials 34 | - philho for his ImageUpload code 35 | - jobLeonard for his shiftBlur 36 | - Greg Borenstein for his book Making Things See 37 | - flight404 for the opengl glow code though I did not use it 38 | 39 | License 40 | ------- 41 | All the technologies used here are free. You are subject to the licensing agreements of those providers. 42 | 43 | You are free to generally use this code as long as you give due credit. Me and a lot of other developers before me have spent hours of caffeineited sleepness to make things work. 44 | -------------------------------------------------------------------------------- /socialflow/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitdaf/kinect/1f5179b01e69844af4e8b143c24b144e755c67b9/socialflow/bg.jpg -------------------------------------------------------------------------------- /socialflow/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitdaf/kinect/1f5179b01e69844af4e8b143c24b144e755c67b9/socialflow/facebook.png -------------------------------------------------------------------------------- /socialflow/particle.pde: -------------------------------------------------------------------------------- 1 | // a basic noise-based moving particle courtesy Amnon Owed 2 | // Modified this a little to reduce randomness, make them slower and appear a little different 3 | // Ankit Daftery 29 Dec 2012 4 | 5 | class Particle { 6 | // unique id, (previous) position, speed 7 | float id, x, y, xp, yp, s, d; 8 | color col; // color 9 | 10 | Particle(float id) { 11 | this.id = id; 12 | s = random(2, 3); // speed 13 | } 14 | 15 | void updateAndDisplay() { 16 | // let it flow, end with a new x and y position 17 | id += 0.01; 18 | d = (noise(id, x/globalY, y/globalY)-0.5)*globalX; 19 | x += cos(radians(d))*0.2; 20 | y += sin(radians(d))*0.2; 21 | 22 | // constrain to boundaries 23 | if (x<-10) x=xp=kinectWidth+10; 24 | if (x>kinectWidth+10) x=xp=-10; 25 | if (y<-10) y=yp=kinectHeight+10; 26 | if (y>kinectHeight+10) y=yp=-10; 27 | 28 | // if there is a polygon (more than 0 points) 29 | if (poly.npoints > 0) { 30 | // if this particle is outside the polygon 31 | if (!poly.contains(x, y)) { 32 | // while it is outside the polygon 33 | while(!poly.contains(x, y)) { 34 | // randomize x and y 35 | x = random(kinectWidth); 36 | y = random(kinectHeight); 37 | }; 38 | // set previous x and y, to this x and y 39 | xp=x; 40 | yp=y; 41 | } 42 | } 43 | 44 | // individual particle color 45 | stroke(col); 46 | strokeWeight(2); 47 | // Draw a point where the particle is. I used a point instead of a line because I wanted less randomness. 48 | point(xp,yp); 49 | // line from previous to current position 50 | //line(xp, yp, x, y); 51 | 52 | // set previous to current position 53 | xp=x; 54 | yp=y; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /socialflow/script.php: -------------------------------------------------------------------------------- 1 | \n"; 61 | $debugData .= "Other info given by browser (size, type): {$_FILES[$fieldName]['size']}, {$_FILES[$fieldName]['type']}
\n"; 62 | $fileSize = filesize($tempLocation); 63 | $debugData .= "Real file size: $fileSize
\n"; 64 | // Strangely enough, if IE is given a path leading to nowhere, it just sends a 0 byte file! 65 | if ($fileSize == 0) // Might test a minimum size (smallest header size for graphics...) 66 | { 67 | return array('', 'File is empty!'); 68 | } 69 | // Get original file name 70 | $file = $_FILES[$fieldName]['name']; 71 | $debugData .= "Original file name: $file
\n"; // No HTML escape! :( 72 | // Strip out the path (given by IE, perhaps other browsers -- Firefox and Opera just give the name) 73 | // Most samples I saw use basename() but I found out that it fails to strip a Windows path on a Unix server 74 | // I could have used a str_replace, but I like REs... 75 | // (I gobble anything up to the last sequence of characters not having slash or anti-slash in it) 76 | // Note: ensure magic quotes are disabled or neutralized 77 | $file = preg_replace('!.*?([^\\/]+)$!', '$1', $file); 78 | $debugData .= "Filter 1: $file
\n"; 79 | // Filter out all characters that are not alphanumerical, dot and dash 80 | // as they can be troublesome in some OSes. 81 | // A sequence of such chars is replaced by a unique underscore. 82 | $file = preg_replace('/[^a-zA-Z0-9.-]+/', '_', $file); 83 | $debugData .= "Filter 2: $file
\n"; 84 | // Split name and extension: gobble everything up to the last dot (file name), then dot and remainder (extension) 85 | // Note that .htaccess has no extension and is a pure filename 86 | if (preg_match('/^(.+)\.([^.]+)$/', $file, $m) == 0) 87 | { 88 | // No match => No dot or nothing before the dot 89 | $extension = ''; 90 | } 91 | else 92 | { 93 | $file = $m[1]; 94 | $extension = $m[2]; 95 | } 96 | $debugData .= "Split: $file $extension
\n"; 97 | // Ensure name isn't too long: just truncate it 98 | $file = substr($file, 0, MAX_FILE_NAME_LENGTH); 99 | $debugData .= "Truncated: $file
\n"; 100 | // If extension not allowed (could be a CGI file...), discard it 101 | if ($extension != '' && !in_array($extension, $allowedExtensions)) 102 | { 103 | return array('', 'File format not allowed.'); 104 | } 105 | 106 | // Add trailing slash to dest dir, supposed in Unix format 107 | // (if not slash at end, replace last char by itself followed by a slash) 108 | $destinationDir = preg_replace('!([^/])$!', '$1/', $destinationDir); 109 | 110 | // Create a time stamp used as prefix to make the file name unique. 111 | // Might have a conflict if you have millions of users, ie. a probability of having two users 112 | // uploading a file in the same second, supposing they go in the same dir. 113 | // If so, you are able to improve this code! ;-) 114 | // One might want to pass a prefix (or suffix) parameter to this function, eg. to tag the file name 115 | // with the name of the author. Just do it! 116 | $prefix = date('Ymd-His-'); // Date time prefix. Not using it to avoid hogging up space on my web server 117 | $destinationFile = $file . ($extension != '' ? '.' . $extension : ''); 118 | $debugData .= "Destination file: $destinationFile
\n"; 119 | $destinationPath = $destinationDir . $destinationFile; 120 | $debugData .= "Destination path: $destinationPath
\n"; 121 | // Move uploaded file from temporary folder to destination 122 | // Overwrite a file of same name there, if any (unless, perhaps server is on Windows, might just fail to move). 123 | if (!move_uploaded_file($tempLocation, $destinationPath)) 124 | { 125 | return array('', 'Failed to move upload file.' . " (dest.: $destinationPath)"); 126 | } 127 | return array($destinationFile, $destinationPath); 128 | return array($tempLocation,'OK'); 129 | } 130 | } 131 | 132 | // Just call the function! 133 | $debugData = ''; 134 | $destDir = '../your_directory/images'; 135 | $maxFiles = 5; // Max number of files accepted in the dest dir 136 | 137 | // First, check if we don't have too much files. Not a bad test: since we fix the max size of files, the max total size of the files 138 | // is under control. 139 | $files = scandir($destDir); 140 | $files = array_slice($files, 2); // Shift out . and .. 141 | while (count($files) > $maxFiles) 142 | { 143 | $file = array_shift($files); 144 | echo $file . " to remove\n"; 145 | unlink($destDir . '/' . $file); 146 | } 147 | 148 | $result = HandleUploadedFile('image', $destDir, array('png', 'gif', 'jpg', 'jpeg')); 149 | if ($debugData != '') 150 | { 151 | echo "

Debug data:
$debugData

\n"; 152 | } 153 | if ($result[0] == '') 154 | { 155 | echo "

Error: {$result[1]}

\n"; 156 | } 157 | else 158 | { 159 | $imageURL = 'http://' . $_SERVER['SERVER_NAME'] . '/your_directory/images/' . $result[0]; 160 | echo "

Received image: {$result[0]}

\n"; 161 | echo "

Posting to Facebook now

\n"; 162 | $photo_details = array(); 163 | $photo_details['url'] = $imageURL; 164 | $postdata = http_build_query($photo_details); 165 | $opts = array('http' => array('method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'content' => $postdata)); 166 | $context = stream_context_create($opts); 167 | $post_url = "https://graph.facebook.com/" . $album_id . "/photos?access_token=" . $access_token; 168 | $response = file_get_contents($post_url,false,$context); 169 | echo $response; 170 | } 171 | 172 | ?> -------------------------------------------------------------------------------- /socialflow/shiftBlur3.pde: -------------------------------------------------------------------------------- 1 | // 00 51 00 2 | // 51 52 00 3 | // 00 51 00 4 | // 256 in total, >>8 5 | 6 | void shiftBlur3(int[] s, int[] t){ // source & target buffer int yOffset; 7 | int yOffset; 8 | for (int i = 1; i < (width-1); ++i){ 9 | 10 | yOffset = width*(height-1); 11 | // top edge (minus corner pixels) 12 | t[i] = (((((s[i] & 0xFF) * 52) + 13 | ((s[i+1] & 0xFF) + 14 | (s[i-1] & 0xFF) + 15 | (s[i + width] & 0xFF) + 16 | (s[i + yOffset] & 0xFF)) * 51) >>> 8) & 0xFF) + 17 | (((((s[i] & 0xFF00) * 52) + 18 | ((s[i+1] & 0xFF00) + 19 | (s[i-1] & 0xFF00) + 20 | (s[i + width] & 0xFF00) + 21 | (s[i + yOffset] & 0xFF00)) * 51) >>> 8) & 0xFF00) + 22 | (((((s[i] & 0xFF0000) * 52) + 23 | ((s[i+1] & 0xFF0000) + 24 | (s[i-1] & 0xFF0000) + 25 | (s[i + width] & 0xFF0000) + 26 | (s[i + yOffset] & 0xFF0000)) * 51) >>> 8) & 0xFF0000) + 27 | 0xFF000000; //ignores transparency 28 | 29 | // bottom edge (minus corner pixels) 30 | t[i + yOffset] = (((((s[i + yOffset] & 0xFF) * 52) + 31 | ((s[i - 1 + yOffset] & 0xFF) + 32 | (s[i + 1 + yOffset] & 0xFF) + 33 | (s[i + yOffset - width] & 0xFF) + 34 | (s[i] & 0xFF)) * 51) >>> 8) & 0xFF) + 35 | (((((s[i + yOffset] & 0xFF00) * 52) + 36 | ((s[i - 1 + yOffset] & 0xFF00) + 37 | (s[i + 1 + yOffset] & 0xFF00) + 38 | (s[i + yOffset - width] & 0xFF00) + 39 | (s[i] & 0xFF00)) * 51) >>> 8) & 0xFF00) + 40 | (((((s[i + yOffset] & 0xFF0000) * 52) + 41 | ((s[i - 1 + yOffset] & 0xFF0000) + 42 | (s[i + 1 + yOffset] & 0xFF0000) + 43 | (s[i + yOffset - width] & 0xFF0000) + 44 | (s[i] & 0xFF0000)) * 51) >>> 8) & 0xFF0000) + 45 | 0xFF000000; 46 | 47 | // central square 48 | for (int j = 1; j < (height-1); ++j){ 49 | yOffset = j*width; 50 | t[i + yOffset] = (((((s[i + yOffset] & 0xFF) * 52) + 51 | ((s[i + 1 + yOffset] & 0xFF) + 52 | (s[i - 1 + yOffset] & 0xFF) + 53 | (s[i + yOffset + width] & 0xFF) + 54 | (s[i + yOffset - width] & 0xFF)) * 51) >>> 8) & 0xFF) + 55 | (((((s[i + yOffset] & 0xFF00) * 52) + 56 | ((s[i + 1 + yOffset] & 0xFF00) + 57 | (s[i - 1 + yOffset] & 0xFF00) + 58 | (s[i + yOffset + width] & 0xFF00) + 59 | (s[i + yOffset - width] & 0xFF00)) * 51) >>> 8) & 0xFF00) + 60 | (((((s[i + yOffset] & 0xFF0000) * 52) + 61 | ((s[i + 1 + yOffset] & 0xFF0000) + 62 | (s[i - 1 + yOffset] & 0xFF0000) + 63 | (s[i + yOffset + width] & 0xFF0000) + 64 | (s[i + yOffset - width] & 0xFF0000)) * 51) >>> 8) & 0xFF0000) + 65 | 0xFF000000; 66 | } 67 | } 68 | 69 | // left and right edge (minus corner pixels) 70 | for (int j = 1; j < (height-1); ++j){ 71 | yOffset = j*width; 72 | t[yOffset] = (((((s[yOffset] & 0xFF) * 52) + 73 | ((s[yOffset + 1] & 0xFF) + 74 | (s[yOffset + width - 1] & 0xFF) + 75 | (s[yOffset + width] & 0xFF) + 76 | (s[yOffset - width] & 0xFF) ) * 51) >>> 8) & 0xFF) + 77 | (((((s[yOffset] & 0xFF00) * 52) + 78 | ((s[yOffset + 1] & 0xFF00) + 79 | (s[yOffset + width - 1] & 0xFF00) + 80 | (s[yOffset + width] & 0xFF00) + 81 | (s[yOffset - width] & 0xFF00) ) * 51) >>> 8) & 0xFF00) + 82 | (((((s[yOffset] & 0xFF0000) * 52) + 83 | ((s[yOffset + 1] & 0xFF0000) + 84 | (s[yOffset + width - 1] & 0xFF0000) + 85 | (s[yOffset + width] & 0xFF0000) + 86 | (s[yOffset - width] & 0xFF0000) ) * 51) >>> 8) & 0xFF0000) + 87 | 0xFF000000; 88 | 89 | t[yOffset + width - 1] = (((((s[yOffset + width - 1] & 0xFF) * 52) + 90 | ((s[yOffset] & 0xFF) + 91 | (s[yOffset + width - 2] & 0xFF) + 92 | (s[yOffset + (width<<1) - 1] & 0xFF) + 93 | (s[yOffset - 1] & 0xFF)) * 51) >>> 8) & 0xFF) + 94 | (((((s[yOffset + width - 1] & 0xFF00) * 52) + 95 | ((s[yOffset] & 0xFF00) + 96 | (s[yOffset + width - 2] & 0xFF00) + 97 | (s[yOffset + (width<<1) - 1] & 0xFF00) + 98 | (s[yOffset - 1] & 0xFF00)) * 51) >>> 8) & 0xFF00) + 99 | (((((s[yOffset + width - 1] & 0xFF0000) * 52) + 100 | ((s[yOffset] & 0xFF0000) + 101 | (s[yOffset + width - 2] & 0xFF0000) + 102 | (s[yOffset + (width<<1) - 1] & 0xFF0000) + 103 | (s[yOffset - 1] & 0xFF0000)) * 51) >>> 8) & 0xFF0000) + 104 | 0xFF000000; 105 | } 106 | 107 | // corner pixels 108 | t[0] = (((((s[0] & 0xFF) * 52) + 109 | ((s[1] & 0xFF) + 110 | (s[width-1] & 0xFF) + 111 | (s[width] & 0xFF) + 112 | (s[width*(height-1)] & 0xFF)) * 51) >>> 8) & 0xFF) + 113 | (((((s[0] & 0xFF00) * 52) + 114 | ((s[1] & 0xFF00) + 115 | (s[width-1] & 0xFF00) + 116 | (s[width] & 0xFF00) + 117 | (s[width*(height-1)] & 0xFF00)) * 51) >>> 8) & 0xFF00) + 118 | (((((s[0] & 0xFF0000) * 52) + 119 | ((s[1] & 0xFF0000) + 120 | (s[width-1] & 0xFF0000) + 121 | (s[width] & 0xFF0000) + 122 | (s[width*(height-1)] & 0xFF0000)) * 51) >>> 8) & 0xFF0000) + 123 | 0xFF000000; 124 | 125 | t[width - 1 ] = (((((s[width-1] & 0xFF) * 52) + 126 | ((s[width-2] & 0xFF) + 127 | (s[0] & 0xFF) + 128 | (s[(width<<1) - 1] & 0xFF) + 129 | (s[width*height-1] & 0xFF) ) * 51) >>> 8) & 0xFF) + 130 | (((((s[width-1] & 0xFF00) * 52) + 131 | ((s[width-2] & 0xFF00) + 132 | (s[0] & 0xFF00) + 133 | (s[(width<<1) - 1] & 0xFF00) + 134 | (s[width*height-1] & 0xFF00) ) * 51) >>> 8) & 0xFF00) + 135 | (((((s[width-1] & 0xFF0000) * 52) + 136 | ((s[width-2] & 0xFF0000) + 137 | (s[0] & 0xFF0000) + 138 | (s[(width<<1) - 1] & 0xFF0000) + 139 | (s[width*height-1] & 0xFF0000) ) * 51) >>> 8) & 0xFF0000) + 140 | 0xFF000000; 141 | 142 | t[width * height - 1] = (((((s[width*height-1] & 0xFF) * 52) + 143 | ((s[width-1] & 0xFF) + 144 | (s[width*(height-1)-1] & 0xFF) + 145 | (s[width*height-2] & 0xFF) + 146 | (s[width*(height-1)] & 0xFF) ) * 51) >>> 8) & 0xFF) + 147 | (((((s[width*height-1] & 0xFF00) * 52) + 148 | ((s[width-1] & 0xFF00) + 149 | (s[width*(height-1)-1] & 0xFF00) + 150 | (s[width*height-2] & 0xFF00) + 151 | (s[width*(height-1)] & 0xFF00) ) * 51) >>> 8) & 0xFF00) + 152 | (((((s[width*height-1] & 0xFF0000) * 52) + 153 | ((s[width-1] & 0xFF0000) + 154 | (s[width*(height-1)-1] & 0xFF0000) + 155 | (s[width*height-2] & 0xFF0000) + 156 | (s[width*(height-1)] & 0xFF0000) ) * 51) >>> 8) & 0xFF0000) + 157 | 0xFF000000; 158 | 159 | t[width *(height-1)] = (((((s[width*(height-1)] & 0xFF) * 52) + 160 | ((s[width*(height-1) + 1] & 0xFF) + 161 | (s[width*height-1] & 0xFF) + 162 | (s[width*(height-2)] & 0xFF) + 163 | (s[0] & 0xFF) ) * 51) >>> 8) & 0xFF) + 164 | (((((s[width*(height-1)] & 0xFF00) * 52) + 165 | ((s[width*(height-1) + 1] & 0xFF00) + 166 | (s[width*height-1] & 0xFF00) + 167 | (s[width*(height-2)] & 0xFF00) + 168 | (s[0] & 0xFF00) ) * 51) >>> 8) & 0xFF00) + 169 | (((((s[width*(height-1)] & 0xFF0000) * 52) + 170 | ((s[width*(height-1) + 1] & 0xFF0000) + 171 | (s[width*height-1] & 0xFF0000) + 172 | (s[width*(height-2)] & 0xFF0000) + 173 | (s[0] & 0xFF0000) ) * 51) >>> 8) & 0xFF0000) + 174 | 0xFF000000; 175 | } 176 | 177 | -------------------------------------------------------------------------------- /socialflow/snapshot.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitdaf/kinect/1f5179b01e69844af4e8b143c24b144e755c67b9/socialflow/snapshot.jpeg -------------------------------------------------------------------------------- /socialflow/socialflow.pde: -------------------------------------------------------------------------------- 1 | // Kinect Flow-Example by Amnon Owed (15/09/12) 2 | // Modified by Ankit Daftery 29/12/12 3 | 4 | // import libraries 5 | 6 | // The graphic libraries 7 | import processing.opengl.*; // opengl 8 | import javax.media.opengl.*; 9 | import java.awt.Polygon; // this is a regular java import so we can use and extend the polygon class (see PolygonBlob) 10 | import java.awt.Graphics2D; 11 | import controlP5.*; // For visual controls 12 | 13 | // The Image processing libraries 14 | import SimpleOpenNI.*; // kinect 15 | import blobDetection.*; // blobs 16 | 17 | // Other Java Imports 18 | 19 | import java.net.*; 20 | import java.util.Collections; // For collections in Processing 2.0b7 21 | import java.awt.image.BufferedImage; 22 | import java.util.Timer; 23 | import java.util.TimerTask; 24 | import java.io.*; 25 | import javax.imageio.*; 26 | 27 | // Initiate the global objects used from the libraries 28 | 29 | ControlP5 cp5; // Visual control panel 30 | PGraphicsOpenGL pgl; 31 | GL gl; 32 | SimpleOpenNI kinect; // declare SimpleOpenNI object 33 | BlobDetection theBlobDetection; // declare BlobDetection object 34 | PolygonBlob poly = new PolygonBlob(); // declare custom PolygonBlob object (see class for more info) 35 | 36 | // Global variables 37 | PImage dispImg; // For manipulation of the screen for the glowy effect 38 | PImage cam,blobs; // PImage to hold incoming imagery for blob detection 39 | int kinectWidth = 640; // the kinect's dimensions to be used later on for calculations 40 | int kinectHeight = 480; 41 | float reScale; // to center and rescale from 640x480 to higher custom resolutions 42 | int nou_old=0; //old number of users to keep track, since the Kinect is being a royal pain in the arse 43 | int[] screenBuf; // To be used for for a shiftBlur 44 | color bgColor; // background color 45 | PImage bgImg; // Background Image 46 | Boolean canUpload=true; // To keep track of Posting to Facebook so we don't flood 47 | Particle[] flow = new Particle[3500]; // an array to hold 3500 Particle objects to begin with 48 | float globalX, globalY; // global variables to influence the movement of all particles 49 | 50 | String[] palettes = { 51 | "-1117720,-13683658,-8410437,-9998215,-1849945,-5517090,-4250587,-14178341,-5804972,-3498634", 52 | "-67879,-9633503,-8858441,-144382,-4996094,-16604779,-588031", 53 | "-16711663,-13888933,-9029017,-5213092,-1787063,-11375744,-2167516,-15713402,-5389468,-2064585" 54 | }; // three color palettes courtest Amnon Owed 55 | 56 | 57 | void setup() { 58 | size(1600, 900, P2D); // setup size and graphics mode, I am using P2D 59 | screenBuf = new int[1600*900]; // A pixel array to be used later for ShiftBlur 60 | cp5 = new ControlP5(this); // Visual control panel onscreen 61 | PImage[] imgs = {loadImage("facebook.png"),loadImage("facebook.png"),loadImage("facebook.png")}; // Images for the button in different states 62 | cp5.addButton("Facebook") // adding the facebook share button 63 | .setValue(128) 64 | .setPosition(width-150,0) 65 | .setImages(imgs) 66 | .updateSize() 67 | ; 68 | kinect = new SimpleOpenNI(this); // initialize SimpleOpenNI object 69 | if (!kinect.enableScene()) { // Check to see if the Kinect is available, quit otherwise 70 | println("No scene image"); 71 | exit(); 72 | } 73 | else { 74 | setupKinect(kinect); // Setup parameters for the kinect 75 | reScale = (float) width / kinectWidth; // calculate the reScale value 76 | blobs = createImage(kinectWidth/3, kinectHeight/3, RGB); // create a smaller blob image for speed and efficiency 77 | theBlobDetection = new BlobDetection(blobs.width, blobs.height); // initialize blob detection object to the blob image dimensions 78 | theBlobDetection.setThreshold(0.3f); // A threshold of 0.2 or 0.3 is best, I don't know why 79 | dispImg = createImage(1600,900,RGB); 80 | bgImg = loadImage("bg.jpg"); 81 | } 82 | } 83 | 84 | void draw() { 85 | setupgl(); // Setup the graphics layer 86 | blendMode(REPLACE); // Replace the previous image, we have new things to draw 87 | image(bgImg,0,0); // Giving our effect a background to match 88 | kinect.update(); // Get fresh information from the Kinect 89 | cam = kinect.sceneImage().get(); // Get information from the scene 90 | blobs.copy(cam, 0, 0, cam.width, cam.height, 0, 0, blobs.width, blobs.height); // copy the image into the smaller blob image for faster processing 91 | blobs.filter(BLUR); // blur the blob image 92 | theBlobDetection.computeBlobs(blobs.pixels); // detect the blobs 93 | poly.reset(); // clear the polygon (original functionality) 94 | poly.createPolygon(); // create the polygon from the blobs (custom functionality, see class) 95 | if (kinect.getNumberOfUsers() < nou_old) nou_old = kinect.getNumberOfUsers(); // Generally doesn't happen, but just in case. We don't want ghosts of users past ;) 96 | if(nou_old>0) // We DO have some user staring at us 97 | { 98 | int n = 3500*nou_old; // 3500 particles for each user. Experiment and set the number according to your taste and requirement 99 | flow = new Particle[n]; 100 | setupFlowfield(); // Initialize the particles for the flow 101 | drawFlowfield(); // Draw the flow particles 102 | } 103 | } 104 | 105 | 106 | // A function to easily setup features required from the Kinect OpenNI interface 107 | 108 | void setupKinect(SimpleOpenNI kinect) 109 | { 110 | kinect.setMirror(true); // mirror the image to make the display more intuitive 111 | kinect.enableUser(SimpleOpenNI.SKEL_PROFILE_NONE); // We need to enable user tracking but do not use skeleton joints here 112 | kinect.enableGesture(); // Enable Gesture tracking to be used to find the hand the first time 113 | kinect.enableHands(); // Enable hand tracking to be used for "Share on Facebook" feature 114 | kinect.addGesture("RaiseHand"); // Tell the kinect that we would like it to report a Raised Hand gesture 115 | } 116 | 117 | // Create and initialize the particles that we need to represent users 118 | 119 | void setupFlowfield() { 120 | strokeWeight(2.5); // set stroke weight (for particle display) to 2.5, set it here to avoid multi-thousand calls 121 | for(int i=0; i0) nou_old -=1; // Shouldn't be needing this but there do seem to be some glitches, so necessary not to kill people unnecessarily 182 | println("Number of users " + nou_old); 183 | } 184 | 185 | // SimpleOpenNI callback method when the Kinect finds a user it was tracking is gone for more than 10 seconds, counts as an exit 186 | 187 | void onExitUser(int userId) 188 | { 189 | println("User exit " + userId); 190 | kinect.stopTrackingSkeleton(userId); // Stop reminiscing, it hurts 191 | if(nou_old>0) nou_old -= 1; 192 | print("Number of users " + nou_old); 193 | } 194 | 195 | // SimpleOpenNI callback method when the Kinect finds a new user 196 | 197 | void onNewUser(int userId) { 198 | println("start pose detection for" + userId); 199 | nou_old += 1; // We have a newcomer in our midst 200 | println("Number of users " + nou_old); 201 | //kinect.requestCalibrationSkeleton(userId,true); // Still unable to decide whether this is necessary to make tracking more reliable 202 | } 203 | 204 | 205 | // ----------------------------------------------------------------- 206 | // hand events 5 207 | 208 | void onCreateHands(int handId, PVector position, float time) { 209 | kinect.convertRealWorldToProjective(position, position); 210 | println("Found hands"); 211 | } 212 | 213 | void onUpdateHands(int handId, PVector position, float time) { 214 | PVector p = new PVector(); 215 | if(position.x>kinectWidth*0.8 && position.y>0.75*kinectHeight) 216 | { 217 | if(canUpload) // Wouldn't want to flood Facebook with another post when this one hasn't gone through, would we ? 218 | { 219 | canUpload = false; 220 | println("Clicking"); 221 | new Poster(4); 222 | } 223 | } 224 | } 225 | 226 | void onDestroyHands(int handId, float time) { 227 | kinect.addGesture("RaiseHand"); 228 | } 229 | 230 | // ----------------------------------------------------------------- 231 | // gesture events 6 232 | void onRecognizeGesture(String strGesture,PVector idPosition,PVector endPosition) 233 | { 234 | println("Recognized Gesture"); 235 | kinect.startTrackingHands(endPosition); 236 | kinect.removeGesture("RaiseHand"); 237 | } 238 | 239 | // A method to post the image to your own server. Thanks a ton to philho for this 240 | // Use this method to post the image to your own server. You can then use a script there to store, manipulate and post images to Facebook, twitter etc 241 | 242 | void post(Timer tim) 243 | { 244 | DataUpload du = new DataUpload(); 245 | boolean bOK = false; 246 | // Upload the currently displayed image with a fixed name, and the chosen format 247 | // We need a new buffered image without the alpha channel 248 | BufferedImage imageNoAlpha = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 249 | loadPixels(); 250 | imageNoAlpha.setRGB(0, 0, width, height, g.pixels, 0, width); 251 | // Ideally shouldn't be needing this section but something I have done posts all the images inverted, so flipping them back 252 | BufferedImage dimg = new BufferedImage(width,height,imageNoAlpha.getColorModel().getTransparency()); 253 | Graphics2D g2 = dimg.createGraphics(); 254 | g2.drawImage(imageNoAlpha,0,0,width,height,0,height,width,0,null); 255 | g2.dispose(); 256 | // end of inversion section 257 | bOK = du.UploadImage("snapshot.jpeg", dimg); 258 | if (!bOK) 259 | return; // Some problem on Java side. Do nothing 260 | 261 | // Get the answer of the PHP script 262 | int rc = du.GetResponseCode(); 263 | String feedback = du.GetServerFeedback(); 264 | println("----- " + rc + " -----\n" + feedback + "---------------"); 265 | // A flash of white light, like that at the moment of creation. Of a snapshot. 266 | void onLostUser(int userId) 267 | { 268 | print("Lost the user "); 269 | println(userId); 270 | kinect.stopTrackingSkeleton(userId); 271 | if (nou_old>0) nou_old -=1; 272 | print("Number of users "); 273 | println(nou_old); 274 | } 275 | 276 | 277 | void onExitUser(int userId) 278 | { 279 | print("User exit "); 280 | println(userId); 281 | kinect.stopTrackingSkeleton(userId); 282 | if (nou_old>0) nou_old -= 1; 283 | print("Number of users "); 284 | println(nou_old); 285 | } 286 | 287 | // user-tracking callbacks! 288 | void onNewUser(int userId) { 289 | print("start pose detection for"); 290 | println(userId); 291 | nou_old += 1; 292 | print("Number of users "); 293 | println(nou_old); 294 | //kinect.requestCalibrationSkeleton(userId,true); 295 | } 296 | 297 | // ----------------------------------------------------------------- 298 | // hand events 5 299 | void onCreateHands(int handId, PVector position, float time) { 300 | kinect.convertRealWorldToProjective(position, position); 301 | println("Found hands");//handPositions.add(position); 302 | } 303 | 304 | void onUpdateHands(int handId, PVector position, float time) { 305 | //kinect.convertRealWorldToProjective(position, position); 306 | PVector p = new PVector(); 307 | if (position.x>kinectWidth*0.8 && position.y>0.75*kinectHeight) 308 | { 309 | if (canUpload) 310 | { 311 | canUpload = false; 312 | println("Clicking"); 313 | fill(255); 314 | rect(0,0,width,height); 315 | new Poster(1); 316 | } 317 | } 318 | //handPositions.add(position); 319 | } 320 | void onDestroyHands(int handId, float time) { 321 | //handPositions.clear(); 322 | kinect.addGesture("RaiseHand"); 323 | } 324 | // ----------------------------------------------------------------- 325 | // gesture events 6 326 | void onRecognizeGesture(String strGesture, PVector idPosition, PVector endPosition) 327 | { 328 | println("Recognized Gesture"); 329 | kinect.startTrackingHands(endPosition); 330 | kinect.removeGesture("RaiseHand"); 331 | } 332 | 333 | 334 | void post(Timer tim) 335 | { 336 | DataUpload du = new DataUpload(); 337 | boolean bOK = false; 338 | // Upload the currently displayed image with a fixed name, and the chosen format 339 | // We need a new buffered image without the alpha channel 340 | fill(255); 341 | rect(0, 0, width, height); 342 | BufferedImage imageNoAlpha = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 343 | loadPixels(); 344 | imageNoAlpha.setRGB(0, 0, width, height, g.pixels, 0, width); 345 | // BufferedImage dimg = new BufferedImage(width,height,imageNoAlpha.getColorModel().getTransparency()); 346 | // Graphics2D g2 = dimg.createGraphics(); 347 | // g2.drawImage(imageNoAlpha,0,0,width,height,0,height,width,0,null); 348 | // g2.dispose(); 349 | 350 | bOK = du.UploadImage("snapshot.jpeg", imageNoAlpha); 351 | if (!bOK) 352 | return; // Some problem on Java side. Do nothing 353 | 354 | // Get the answer of the PHP script 355 | int rc = du.GetResponseCode(); 356 | String feedback = du.GetServerFeedback(); 357 | println("----- " + rc + " -----\n" + feedback + "---------------"); 358 | fill(255); 359 | rect(0, 0, width, height); 360 | tim.cancel(); 361 | } 362 | 363 | public class Poster { 364 | Timer tim; 365 | 366 | public Poster(int seconds) 367 | { 368 | tim = new Timer(); 369 | tim.schedule(new PostTask(), seconds*1000); 370 | } 371 | class PostTask extends TimerTask { 372 | 373 | public void run() { 374 | println("Time's up!"); 375 | post(tim); 376 | //tim.cancel(); //Not necessary because we call System.exit 377 | canUpload = true; 378 | } 379 | } 380 | } 381 | 382 | 383 | fill(255); 384 | rect(0,0,width,height); 385 | tim.cancel(); // Cancel the timer, we are done with it 386 | } 387 | 388 | // A class to handle posting data to a web server 389 | 390 | public class Poster{ 391 | Timer tim; 392 | 393 | public Poster(int seconds) 394 | { 395 | tim = new Timer(); 396 | tim.schedule(new PostTask(),seconds*1000); 397 | } 398 | class PostTask extends TimerTask { 399 | 400 | public void run() { 401 | println("Time's up!"); 402 | post(tim); // Passing the timer as a parameter to cancel the timer in the posting method. Was getting system crashes otherwise 403 | canUpload = true; // Re-enable upload, we just posted one 404 | } 405 | } 406 | } 407 | 408 | --------------------------------------------------------------------------------