/** * **************************************************************************************************************************************** */ private static List<List<PointIndex_I32>> getCandidates( BufferedImage image, int blurRadius, float threshLow, float threshHigh, double toleranceDist, double toleranceAngle, boolean dynamicThreshold) { List<List<PointIndex_I32>> candidates = new ArrayList<List<PointIndex_I32>>(); ImageFloat32 input = ConvertBufferedImage.convertFromSingle(image, null, ImageFloat32.class); ImageUInt8 binary = new ImageUInt8(input.width, input.height); // Finds edges inside the image CannyEdge<ImageFloat32, ImageFloat32> canny = FactoryEdgeDetectors.canny( blurRadius, false, dynamicThreshold, ImageFloat32.class, ImageFloat32.class); canny.process(input, threshLow, threshHigh, binary); List<Contour> contours = BinaryImageOps.contour(binary, rule, null); for (Contour c : contours) { // Only the external contours are relevant. List<PointIndex_I32> vertices = ShapeFittingOps.fitPolygon(c.external, true, toleranceDist, toleranceAngle, 100); candidates.add(vertices); } return candidates; }
/** * Detects contours inside the binary image generated by canny. Only the external contour is * relevant. Often easier to deal with than working with Canny edges directly. */ public static void fitCannyBinary(ImageFloat32 input) { BufferedImage displayImage = new BufferedImage(input.width, input.height, BufferedImage.TYPE_INT_RGB); ImageUInt8 binary = new ImageUInt8(input.width, input.height); // Finds edges inside the image CannyEdge<ImageFloat32, ImageFloat32> canny = FactoryEdgeDetectors.canny(2, false, true, ImageFloat32.class, ImageFloat32.class); canny.process(input, 0.1f, 0.3f, binary); List<Contour> contours = BinaryImageOps.contour(binary, 8, null); Graphics2D g2 = displayImage.createGraphics(); g2.setStroke(new BasicStroke(2)); // used to select colors for each line Random rand = new Random(234); for (Contour c : contours) { // Only the external contours are relevant. List<PointIndex_I32> vertexes = ShapeFittingOps.fitPolygon(c.external, true, toleranceDist, toleranceAngle, 100); g2.setColor(new Color(rand.nextInt())); VisualizeShapes.drawPolygon(vertexes, true, g2); } ShowImages.showWindow(displayImage, "Canny Contour"); }
/** * Fits a sequence of line-segments into a sequence of points found using the Canny edge detector. * In this case the points are not connected in a loop. The canny detector produces a more complex * tree and the fitted points can be a bit noisy compared to the others. */ public static void fitCannyEdges(ImageFloat32 input) { BufferedImage displayImage = new BufferedImage(input.width, input.height, BufferedImage.TYPE_INT_RGB); // Finds edges inside the image CannyEdge<ImageFloat32, ImageFloat32> canny = FactoryEdgeDetectors.canny(2, true, true, ImageFloat32.class, ImageFloat32.class); canny.process(input, 0.1f, 0.3f, null); List<EdgeContour> contours = canny.getContours(); Graphics2D g2 = displayImage.createGraphics(); g2.setStroke(new BasicStroke(2)); // used to select colors for each line Random rand = new Random(234); for (EdgeContour e : contours) { g2.setColor(new Color(rand.nextInt())); for (EdgeSegment s : e.segments) { // fit line segments to the point sequence. Note that loop is false List<PointIndex_I32> vertexes = ShapeFittingOps.fitPolygon(s.points, false, toleranceDist, toleranceAngle, 100); VisualizeShapes.drawPolygon(vertexes, false, g2); } } ShowImages.showWindow(displayImage, "Canny Trace"); }
/** 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"); }
public static void main(String[] args) throws IOException { if (args.length == 0) { System.out.println( "usage: crop-objects [OPTION]... FILE [DIR]\n" + "crops detected objects from image FILE and writes their subimages to files. \n" + "Can specify the DIR in which to create the files, otherwise subimage files are " + "created in the same directory as FILE is in by default. \n" + "-t [n] [m] set the high and low threshold values. n and m are values between 0 and 1."); System.exit(1); } // interpret options and read arguments if (args[0].equals("-t")) { threshLow = Float.parseFloat(args[1]); threshHigh = Float.parseFloat(args[2]); filename = args[3]; if (args.length == 5) { dir = args[4]; } } else { filename = args[0]; if (args.length == 2) { dir = args[1]; } } // get path to directory file is in String name = FilenameUtils.removeExtension(filename); // get image BufferedImage image = UtilImageIO.loadImage(new File(filename).getAbsolutePath()); if (image == null) { System.out.println( "usage: crop-objects [OPTION]... FILE [DIR]\n" + "crops detected objects from image FILE and writes their subimages to files. \n" + "Can specify the DIR in which to create the files, otherwise subimage files are " + "created in the same directory as FILE is in by default. \n" + "-t [n] [m] set the high and low threshold values. n and m are values between 0 and 1."); System.exit(1); } minsize = (int) (0.1 * Math.min(image.getHeight(), image.getWidth())); // find objects in image // generate candidate contours ArrayList<List<PointIndex_I32>> objects = new ArrayList<List<PointIndex_I32>>(); List<BufferedImage> results = new ArrayList<BufferedImage>(); List<List<PointIndex_I32>> candidates = new ArrayList<List<PointIndex_I32>>(); ImageFloat32 input = ConvertBufferedImage.convertFromSingle(image, null, ImageFloat32.class); BufferedImage bw = ConvertBufferedImage.convertTo( input, new BufferedImage(image.getWidth(), image.getHeight(), image.getType())); File binaryfile = new File(name + "_" + "binary.png"); ImageIO.write(bw, "png", binaryfile); ImageUInt8 binary = new ImageUInt8(input.width, input.height); // Finds edges inside the image CannyEdge<ImageFloat32, ImageFloat32> canny = FactoryEdgeDetectors.canny( blurRadius, true, dynamicThreshold, ImageFloat32.class, ImageFloat32.class); canny.process(input, threshLow, threshHigh, binary); List<Contour> contours = BinaryImageOps.contour(binary, rule, null); BufferedImage visualBinary = VisualizeBinaryData.renderBinary(binary, null); File cannyfile = new File(name + "_" + "canny.png"); ImageIO.write(visualBinary, "png", cannyfile); BufferedImage cannyContour = VisualizeBinaryData.renderExternal(contours, null, binary.width, binary.height, null); File cannyContourfile = new File(name + "_" + "contour.png"); ImageIO.write(cannyContour, "png", cannyContourfile); for (Contour c : contours) { // Only the external contours are relevant. List<PointIndex_I32> vertices = ShapeFittingOps.fitPolygon(c.external, true, toleranceDist, toleranceAngle, 100); candidates.add(vertices); } for (List<PointIndex_I32> vertices : candidates) { try { Candidate c = new Candidate(vertices, image); if (c.size(minsize)) { c.rotate(); results.add(c.getImage()); objects.add(vertices); } } catch (Exception e) { System.out.println("Error creating candidate from contour " + e.getMessage()); } } // write subimages of objects to files int i = 0; for (BufferedImage obj : results) { // print images to file try { File outputfile = new File(name + "_" + i + ".png"); i++; ImageIO.write(obj, "png", outputfile); } catch (IOException e) { System.out.println("Error writing subimages" + e.getMessage()); } } // draw objects onto original image and save Draw.drawPolygons(objects, image); File outputfile = new File(name + "_" + "annotated.png"); ImageIO.write(image, "png", outputfile); }