Java program to distort image by applying Voronoi Diagram filter to it (also called stained glass filter).


Main.java
import javax.imageio.ImageIO; import java.io.File; // 2018 TheFlyingKeyboard and released under MIT License // theflyingkeyboard.net public class Main { public static void main(String[] args) { Voronoi voronoi = new Voronoi(2, 50000, false); try{ voronoi.generate(ImageIO.read(new File("tfk.png"))); }catch (Exception e){ e.printStackTrace(); } } }
Point2D.java
// 2018 TheFlyingKeyboard and released under MIT License // theflyingkeyboard.net public class Point2D { private int x; private int y; public Point2D(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } }
Voronoi.java
import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.util.concurrent.ThreadLocalRandom; // 2018 TheFlyingKeyboard and released under MIT License // theflyingkeyboard.net public class Voronoi { private int width; private int height; private final double distanceOrder; private final int numberOfPoints; private final Point2D[] points; private final int[] colors; private final int pointSize = 5; private final boolean drawPoints; private BufferedImage bufferedImage; public Voronoi(double distanceOrder, int numberOfPoints, boolean drawPoints) { this.distanceOrder = distanceOrder; this.numberOfPoints = numberOfPoints; this.drawPoints = drawPoints; points = new Point2D[numberOfPoints]; colors = new int[numberOfPoints]; } public void generate(BufferedImage bufferedImage) { this.bufferedImage = bufferedImage; this.width = bufferedImage.getWidth(); this.height = bufferedImage.getHeight(); for (int i = 0; i < numberOfPoints; ++i) { points[i] = new Point2D(ThreadLocalRandom.current().nextInt(width), ThreadLocalRandom.current().nextInt(height)); colors[i] = bufferedImage.getRGB(points[i].getX(), points[i].getY()); } generateVoronoi(); try { ImageIO.write(this.bufferedImage, "png", new File("Voronoi " + width + "X" + height + " " + " distance order " + distanceOrder + " " + numberOfPoints + " points.png")); } catch (Exception e) { e.printStackTrace(); } } private void generateVoronoi() { int counter = 0; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { System.out.println((counter / (double) (height * width)) * 100 + "%"); bufferedImage.setRGB(x, y, colors[findClosestPoint(new Point2D(x, y))]); ++counter; } } if (drawPoints) { drawPoints(); } } private void drawPoints() { for (int i = 0; i < numberOfPoints; ++i) { for (int y = -pointSize; y <= pointSize; ++y) { for (int x = -pointSize; x <= pointSize; ++x) { if (x * x + y * y <= pointSize * pointSize + pointSize && x + points[i].getX() >= 0 && x + points[i].getX() < width && y + points[i].getY() >= 0 && y + points[i].getY() < height) { bufferedImage.setRGB(x + points[i].getX(), y + points[i].getY(), 0); } } } } } private int findClosestPoint(Point2D point) { int index = 0; double minDistance = distance(point, points[index]); double currentDistance; for (int i = 1; i < numberOfPoints; ++i) { currentDistance = distance(point, points[i]); if (currentDistance < minDistance) { minDistance = currentDistance; index = i; } } return index; } private double distance(Point2D pointA, Point2D pointB) { if (distanceOrder == 1.0d) { return Math.abs(pointA.getX() - pointB.getX()) + Math.abs(pointA.getY() - pointB.getY()); } if (distanceOrder == 2.0d) { return Math.sqrt((pointA.getX() - pointB.getX()) * (pointA.getX() - pointB.getX()) + (pointA.getY() - pointB.getY()) * (pointA.getY() - pointB.getY())); } return Math.pow(Math.pow(Math.abs(pointA.getX() - pointB.getX()), distanceOrder) + Math.pow(Math.abs(pointA.getY() - pointB.getY()), distanceOrder), (1.0d / distanceOrder)); } }
Java Voronoi Diagram Of Image