/*
This code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
This code is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this code. If not, see .
*/
/* Code logic in short:
The code reads the webcam image
Detects contours
Filter contours based on size
Checks whether contour is new or a movement of an existing contour based on a threshold
Reoccuring contours are saved as blobs
The first blob is assigned as being pacman, you can also click on another blob to switch pacman
A matrix of dots is drawn
In case pacman is near a dot, it gets destroy and score + 1
*/
/* Most important values for tracking:
contourTreshold
sizeTreshold
/*
/* Load libraries */
import gab.opencv.*; // Load library https://github.com/atduskgreg/opencv-processing
import processing.video.*; // Load video camera library
/* Declare variables */
Capture video; // Camera stream
OpenCV opencv; // OpenCV
PImage now, diff; // PImage variables to store the current image and the difference between two images
PGraphics drawing; // PGraphics buffer
ArrayList contours; // ArrayList to hold all detected contours
ArrayList blobs = new ArrayList (); // ArrayList to hold all blobs
PFont font; // A new font object
int ContourThreshold = 20; // Contour detection sensitivity of the script
int SizeThreshold = 100; // Contour size threshold
int MovementMargin = 40; // Max difference in coordinates
int segmentSize = 5; // orig 5;
int segmentThreshold = segmentSize * segmentSize / 3;
/* Slider Bar */
HScrollbar hs1, hs2;
Boolean scroll_lock = false;
/* Setup function */
void setup() {
size(640, 960); // Create canvas window
// Select camera: print all available cameras in a list
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(i, cameras[i]);
}
}
// select the number of the webcam camera from the list
// video = new Capture(this, 640, 480, cameras[12], 30); // Define video size, with webcam plugged in
video = new Capture(this, 640, 480, cameras[16], 30);
opencv = new OpenCV(this, 640, 480); // Define opencv size
video.start(); // Start capturing video
font = loadFont("Serif-48.vlw"); // Load the font file into memory
textFont(font, 12); // Set font size
strokeWeight(3); // Set stroke size
noStroke();
hs1 = new HScrollbar(0, height/2-8, width, 16, 16);
hs2 = new HScrollbar(0, height/2+8, width, 16, 16);
}
/* Draw function */
void draw() {
// start buffer
drawing = createGraphics(640, 480);
drawing.beginDraw();
drawing.noStroke();
int[] segments = new int[int(640/segmentSize)*int((480)/segmentSize)]; // Set number segments
opencv.loadImage(video); // Capture video from camera in OpenCV
image(video, 0, 0); // Draw camera image to screen
opencv.gray(); // Convert into gray scale
//opencv.contrast(2); // Increase contrast
opencv.invert(); // Invert b/w
//opencv.blur(3); // Reduce camera noise
opencv.threshold(ContourThreshold); // Convert to Black and White
//opencv.flip(OpenCV.VERTICAL); // Flip image vertical
now = opencv.getOutput(); // Store image in PImage
image(now, 0, 480); // Show video (black and white)
// Analyze image
int segmentCounter = 0;
for( int x = 1; x < 640; x = x + segmentSize) {
for ( int y = 1; y < 480; y = y + segmentSize) {
int segmentBrightness = 0;
for ( int i = 0; i < segmentSize; i++) {
for ( int j = 0; j < segmentSize; j++) {
if (brightness(now.pixels[x+i+(y*width)+j]) > 127) {
segmentBrightness++;
}
}
}
if(segmentBrightness > segmentThreshold) { // segment is bright
segments[segmentCounter] = 1;
drawing.fill(255,255,255);
drawing.rect(x-1,y-1,12,12);
}
segmentCounter++;
}
}
drawing.endDraw();
// Draw the offscreen buffer to the screen with image()
image(drawing, 0, 0);
//opencv.loadImage(drawing);
// Analyze video
contours = opencv.findContours(); // Find contours
int evalc = 0; // Count number of evaluated contours
for (Contour contour : contours) { // Loop through all contours
int sumx = 0; // Sum of all x coordinates
int sections = 0; // # of sections
int sumy = 0; // Sum of all y coordinates
int area = 0; // Area of polygon
ArrayList Xcoors = new ArrayList(); // List of all X coordinates
ArrayList Ycoors = new ArrayList(); // List of all Y coordinates
for (PVector point : contour.getPolygonApproximation().getPoints()) { // Loop through all vertex X Y coordinates of polygon
sumx = sumx + int(point.x); // Sum up all X coordinates, needed for calculating the middle
sumy = sumy + int(point.y); // Sum up all Y coordinates, needed for calculating the middle
Xcoors.add(int(point.x)); // Store all X coordinates, needed for calculating the area
Ycoors.add(int(point.y)); // Store all Y coordinates, needed for calculating the area
sections++; // Count the number of sections
}
// Calculate the area of the polygon
int j = 0;
for (int i = 0; i < sections; i++) {
area = area + (Xcoors.get(j)+Xcoors.get(i)) * (Ycoors.get(j)-Ycoors.get(i));
j = i;
}
area = area / 2;
if(area > SizeThreshold && area < 100000) { // Check whether area is above threshold and not as big as the video itself
evalc++; // Up 1 for the evaluated contours counter
noFill(); // Disable filled shapes
stroke(0, 255, 0); // Set stroke color to green
contour.draw(); // Draw the contour
fill(255, 0, 0); // Set color to red
text((sumx/sections) + ", " + (sumy/sections), (sumx/sections), (sumy/sections)); // Print the coordinates on screen
if(blobs.size() > 0) { // Check whether this is the first blob or not
boolean withinmargin = false; // Flag whether it is close to a previous blob
int withinmarginID = 0; // Remember which one that was
for( int i = 0; i < blobs.size(); i++) { // Loop through all blobs
Blob _blob = (Blob) blobs.get(i); // Make a temp copy
int lastX = _blob.blobX.get(_blob.blobX.size()-1); // most recent X coordinate
int lastY = _blob.blobY.get(_blob.blobY.size()-1); // most recent Y coordinate
// Check whether the contour is within movement margin, otherwise it's a new blob
if(lastX < (sumx/sections + MovementMargin)){
if(lastX > (sumx/sections - MovementMargin)) {
if(lastY < (sumy/sections + MovementMargin)) {
if(lastY > (sumy/sections - MovementMargin)) {
withinmargin = true;
withinmarginID = i;
// println("hit X old: " + lastX + "; new:" + sumx/c + "; Y old: " + lastY + "; Y new: " +sumy/c);
}
}
}
}
_blob = null; // Reset temp copy
}
if(withinmargin) { // Within margin? True: add coordinates to ArrayList
Blob _blob = (Blob) blobs.get(withinmarginID); // Make temp copy of blob
_blob.addXYSA(sumx/sections, sumy/sections, sections, area); // Add the information to the ArrayLists of the blob object
blobs.set(withinmarginID, _blob); // Store the blob
} else { // else: it's a new blob
blobs.add(new Blob(sumx/sections, sumy/sections, sections, area, false)); // Add new blob to the blobs ArrayList
}
} else { // Add first blob, which becomes PacMan
blobs.add(new Blob(sumx/sections, sumy/sections, sections, area, true)); // Store blob and flag as PacMan
}
}
}
// Print some useful monitoring info to the console
println("Found " + contours.size() + " Contours; area > "+SizeThreshold+" px " + evalc + "; Total "+ blobs.size() + " blobs" + " mouseX: " + mouseX);
ContourThreshold = int(hs1.getPos()); // get scrollbar position
fill(0,0,255); // Set color to blue
textSize(20); // Increase text size
text("CountourThreshold: " + ContourThreshold, 20, 40);
int multiplySizeTreshold = 2; // variable for multiplying the Sizetreshold
SizeThreshold = int(hs2.getPos()) * multiplySizeTreshold; // NEW: multiply w/ 100 to get a bigger max SizeTreshold
// SizeThreshold = int(hs2.getPos());
text("SizeThreshold: " + SizeThreshold * 10, 30, 60);
scroll_lock = hs1.update(scroll_lock);
scroll_lock = hs2.update(scroll_lock);
hs1.display();
hs2.display();
// Wait a little before the next round to save processing power and memory
delay(40);
}
/* Capture function */
void captureEvent(Capture c) {
c.read();
}