public Segmenter(ImageUInt8 gray) { // Downsample image to smaller size int sampleWidth = 300; this.scale = (1.0 * gray.getWidth()) / sampleWidth; int sampleHeight = (int) (gray.getHeight() / scale); ImageUInt8 sample = new ImageUInt8(sampleWidth, sampleHeight); boofcv.alg.distort.DistortImageOps.scale( gray, sample, boofcv.alg.interpolate.TypeInterpolate.NEAREST_NEIGHBOR); this.sample = sample; // Calculate mean this.mean = GPixelMath.sum(gray) / (gray.width * gray.height); this.threshold = this.calculateThreshold(); Log.i( "Setalyzer", "brightness histogram: " + Arrays.toString(this.brightnessHistogram.histogram)); // Blur image and store blurred image this.blurred = boofcv.alg.filter.blur.BlurImageOps.mean(sample, null, BLUR_RADIUS, null); // Create binary image and label blobs for threshold-based segmentation // Log.i("Setalyzer", "THreshold value is " + this.mean); // Threshold to generate binary image ImageUInt8 binary = boofcv.alg.filter.binary.ThresholdImageOps.threshold( this.blurred, null, (int) this.mean, false); ImageSInt32 blobImage = FactoryImage.create(ImageSInt32.class, sample.getWidth(), sample.getHeight()); this.numBlobs = boofcv.alg.filter.binary.BinaryImageOps.labelBlobs4(binary, blobImage); this.blobQuads = quadsFromBlobs(blobImage, numBlobs); // Canny detect edges and store them // Dynamic canny edge, which sets the threshold as a function of the image's edge intensity DetectEdgeContour<ImageUInt8> cannyD = FactoryDetectEdgeContour.canny(0.05, 0.15, true, ImageUInt8.class, ImageSInt16.class); cannyD.process(blurred); List<List<Point2D_I32>> edges = cannyD.getContours(); // Prune edges which are obviously not cards List<List<Point2D_F64>> prunedEdgeList = new ArrayList<List<Point2D_F64>>(); for (List<Point2D_I32> edge : edges) { // Disregard tiny contours if (edge.size() < EDGE_SIZE_THRESHOLD) { continue; } // Disregard shapes with blatantly non-card shapes // if (!isCardShaped(edge)) { // continue; // } List<Point2D_F64> f64Edge = new ArrayList<Point2D_F64>(); for (Point2D_I32 p : edge) { f64Edge.add(new Point2D_F64(p.x, p.y)); } prunedEdgeList.add(f64Edge); } this.prunedCannyEdgeList = prunedEdgeList; }
/** Fits polygons to found contours around binary blobs. */ public static void fitBinaryImage(ImageFloat32 input) { ImageUInt8 binary = new ImageUInt8(input.width, input.height); BufferedImage polygon = new BufferedImage(input.width, input.height, BufferedImage.TYPE_INT_RGB); // the mean pixel value is often a reasonable threshold when creating a binary image double mean = ImageStatistics.mean(input); // create a binary image by thresholding ThresholdImageOps.threshold(input, binary, (float) mean, true); // reduce noise with some filtering ImageUInt8 filtered = BinaryImageOps.erode8(binary, null); filtered = BinaryImageOps.dilate8(filtered, null); // Find the contour around the shapes List<Contour> contours = BinaryImageOps.contour(filtered, 8, null); // Fit a polygon to each shape and draw the results Graphics2D g2 = polygon.createGraphics(); g2.setStroke(new BasicStroke(2)); for (Contour c : contours) { // Fit the polygon to the found external contour. Note loop = true List<PointIndex_I32> vertexes = ShapeFittingOps.fitPolygon(c.external, true, toleranceDist, toleranceAngle, 100); g2.setColor(Color.RED); VisualizeShapes.drawPolygon(vertexes, true, g2); // handle internal contours now g2.setColor(Color.BLUE); for (List<Point2D_I32> internal : c.internal) { vertexes = ShapeFittingOps.fitPolygon(internal, true, toleranceDist, toleranceAngle, 100); VisualizeShapes.drawPolygon(vertexes, true, g2); } } ShowImages.showWindow(polygon, "Binary Blob Contours"); }