Skip to content

Instantly share code, notes, and snippets.

@brianfay
Created December 16, 2013 00:32
Show Gist options
  • Save brianfay/7980510 to your computer and use it in GitHub Desktop.
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…
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