Created
December 16, 2013 00:32
-
-
Save brianfay/7980510 to your computer and use it in GitHub Desktop.
Takes a live video feed and detects 10x10 black and white pattern on a piece of paper (the edges of this pattern should all be black) Borrows very heavily from atduskgreg's Marker Detection example: https://github.com/atduskgreg/opencv-processing/tree/master/examples/MarkerDetection May require some tweaking to use, depending on lighting conditi…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import gab.opencv.*; | |
import org.opencv.imgproc.Imgproc; | |
import org.opencv.core.Core; | |
import org.opencv.core.MatOfPoint; | |
import org.opencv.core.MatOfPoint2f; | |
import org.opencv.core.Point; | |
import org.opencv.core.Size; | |
import org.opencv.core.Mat; | |
import org.opencv.core.CvType; | |
import processing.video.*; | |
PImage openCVImage; | |
Capture capture; | |
OpenCV opencv; | |
ArrayList<MatOfPoint> contours; | |
ArrayList<MatOfPoint2f> approximations, markers; | |
Mat thresholdMat; | |
int detectionFrequency; //the nuber of frames to pass before we detect markers | |
void setup(){ | |
//TODO: make size bigger, add image of detected markers on right side of screen | |
size(640, 480); | |
openCVImage = new PImage(320,240); | |
capture = new Capture(this,320,240); | |
opencv = new OpenCV(this,openCVImage); | |
contours = new ArrayList<MatOfPoint>(); | |
approximations = new ArrayList<MatOfPoint2f>(); | |
markers = new ArrayList<MatOfPoint2f>(); | |
thresholdMat = OpenCV.imitate(opencv.getGray()); | |
opencv.blur(1); | |
detectionFrequency = 50; | |
capture.start(); | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | |
void draw(){ | |
background(224); | |
scale(2); | |
//this is where opencv gets video feed: | |
opencv.loadImage(capture); | |
createThresholdMat(); | |
image(opencv.getOutput(),0,0); //drawing frame before the detection prevents visual gaps | |
drawContours(); //we call this function every frame - if there are no contours, it does nothing, otherwise it draws the last contours that were detected. | |
drawApproximations(); | |
drawMarkers(); | |
if(frameCount % detectionFrequency == 0){ | |
println("checking for markers..."); | |
findContours(); | |
createApproximations(); | |
selectMarkers(); | |
warpMarkers(); | |
} | |
if(frameCount % detectionFrequency == (detectionFrequency - 1)){ | |
cleanUp(); | |
} | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | |
void createThresholdMat(){ | |
//mouseX controls this threshold | |
Imgproc.adaptiveThreshold(opencv.getGray(), thresholdMat, mouseX, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, 451, -65); | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | |
void findContours(){ | |
try{ | |
Imgproc.findContours(thresholdMat, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_NONE); | |
}catch(Exception e){ | |
System.out.println("did not find contours"); | |
} | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | |
void createApproximations(){ | |
try{ | |
if(contours.size() > 0){ | |
for(MatOfPoint cntr : contours){ | |
double epsilon = cntr.size().height * 0.03; | |
MatOfPoint2f approx = new MatOfPoint2f(); | |
Imgproc.approxPolyDP(new MatOfPoint2f(cntr.toArray()),approx,epsilon,true); | |
approximations.add(approx); | |
} | |
} | |
}catch(Exception e){ | |
System.out.println("Creating polygon approximations somehow failed."); | |
} | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | |
void selectMarkers(){ | |
float minAllowedContourSide = 50; // we won't allow an edge smaller than this to count towards detection | |
float minArea = minAllowedContourSide * minAllowedContourSide; | |
for(MatOfPoint2f candidate : approximations){ | |
//we only want four-sided shapes; if we aren't finding any, mess with epsilon in polyApproxDP | |
if(candidate.size().height * candidate.size().width != 4){ | |
continue; | |
} | |
//we only want convex shapes (you'd have to be folding the paper pretty funky to get a concave box) | |
if(!Imgproc.isContourConvex(new MatOfPoint(candidate.toArray()))){ | |
continue; | |
} | |
//we don't want to detect really small shapes - we're looking for a big black box | |
float minDist = openCVImage.width * openCVImage.width; | |
Point[] points = candidate.toArray(); | |
for(int i =0; i < points.length; i++){ | |
//I'm not math smart, but I do think this is detecting smallest area, not smallest distance. | |
//Misleading variable names, IMO | |
Point side = new Point(points[i].x - points[(i+1)%4].x, points[i].y - points[(i+1)%4].y); | |
float squaredLength = (float)side.dot(side); | |
minDist = min(minDist, squaredLength); | |
} | |
if(minDist < minArea) { | |
continue; | |
} | |
//if the candidate polygon passed our tests, we consider it an actual marker. | |
markers.add(candidate); | |
} | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | |
void warpMarkers(){ | |
//TODO: Detect all markers in the ArrayList; not just the first one | |
if(markers.size() == 0){ | |
println("No markers to warp"); | |
return; | |
} | |
MatOfPoint2f canonicalMarker = new MatOfPoint2f(); | |
Point[] canonicalPoints = new Point[4]; | |
canonicalPoints[0] = new Point(0, 350); | |
canonicalPoints[1] = new Point(0, 0); | |
canonicalPoints[2] = new Point(350, 0); | |
canonicalPoints[3] = new Point(350, 350); | |
canonicalMarker.fromArray(canonicalPoints); | |
try{ | |
for(MatOfPoint2f marker : markers){ | |
Mat transform = Imgproc.getPerspectiveTransform(markers, canonicalMarker); | |
Mat unWarpedMarker = new Mat(50, 50, CvType.CV_8UC1); | |
Imgproc.warpPerspective(opencv.getGray(), unWarpedMarker, transform, new Size(350,350)); | |
Imgproc.threshold(unWarpedMarker,unWarpedMarker, 125, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU); | |
float cellSize = 350/10.0; | |
boolean[][] markerCells = new boolean[10][10]; | |
for(int row = 0; row < 10; row++){ | |
for(int col = 0; col < 10; col++){ | |
int cellX = int(col*cellSize); | |
int cellY = int(row*cellSize); | |
//count the cell if more than half of the points of its matrix are white | |
Mat cell = unWarpedMarker.submat(cellX, cellX + (int)cellSize, cellY, cellY + (int)cellSize); | |
markerCells[row][col] = (Core.countNonZero(cell) > (cellSize*cellSize)/2); | |
} | |
} | |
for(int col = 0; col < 10; col++){ | |
for(int row = 0; row < 10; row++){ | |
if(markerCells[row][col]){ | |
print(1); | |
} | |
else{ | |
print(0); | |
} | |
} | |
println(); | |
} | |
} | |
}catch(Exception e){ | |
println("Unable to warp markers"); | |
} | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | |
void cleanUp(){ | |
//we don't want to look for markers in past frames, so we clear the arraylists before we detect new ones | |
markers.clear(); | |
approximations.clear(); | |
contours.clear(); | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | |
void drawContours(){ | |
pushStyle(); | |
noFill(); | |
stroke(0,255,0); | |
strokeWeight(4); | |
for (MatOfPoint cntr : contours) { | |
beginShape(); | |
Point[] points = cntr.toArray(); | |
for (int i = 0; i < points.length; i++) { | |
vertex((float)points[i].x, (float)points[i].y); | |
} | |
endShape(CLOSE); | |
} | |
popStyle(); | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | |
void drawApproximations(){ | |
pushStyle(); | |
noFill(); | |
stroke(255,0,0); | |
strokeWeight(4); | |
for (MatOfPoint2f cntr : approximations) { | |
beginShape(); | |
Point[] points = cntr.toArray(); | |
for (int i = 0; i < points.length; i++) { | |
vertex((float)points[i].x, (float)points[i].y); | |
} | |
endShape(CLOSE); | |
} | |
popStyle(); | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | |
void drawMarkers(){ | |
pushStyle(); | |
noFill(); | |
stroke(0,0,255); | |
strokeWeight(4); | |
for (MatOfPoint2f cntr : markers) { | |
beginShape(); | |
Point[] points = cntr.toArray(); | |
for (int i = 0; i < points.length; i++) { | |
vertex((float)points[i].x, (float)points[i].y); | |
} | |
endShape(CLOSE); | |
} | |
popStyle(); | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | |
void captureEvent(Capture c){ | |
c.read(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment