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);
  }