1

I am new to programming and have programmed a java program that calculates the voronoi diagram for random points and draws it on a picture - 1080 x 1920 pixels. Here you can see the result

The problem is that it takes nearly four minutes to compile the code. Please help me with code optimization. Below you can find the code:

class Voronoi {
private static Random r;
private static KDTree<Integer> kd;
private static double[][] keys;
private static int counter;
private static int x;
public static void main(String[] args) throws IOException,
 KeySizeException, KeyDuplicateException {
 BufferedImage img = null;
 Scanner sc = new Scanner(System.in);
 x = sc.nextInt();
 // input an image
 img = ImageIO.read(new File("input.jpg"));
 // get image Height and Width
 int w = img.getWidth();
 int h = img.getHeight();
 // make array with equal size like the image and populate it with the 0
 // make an empty array and populate it
 int[][] empty = new int[h][w];
 for (int row = 0; row < h; row++) {
 for (int col = 0; col < w; col++) {
 empty[row][col] = 0;
 }
 }
 // make a D-dimensional KD-tree
 keys = new double[x][2];
 kd = new KDTree<Integer>(2);
 ArrayList<Color> b = new ArrayList<Color>();
 int[] totalBlue = new int[x];
 int[] totalGreen = new int[x];
 int[] totalRed = new int[x];
 int[] counter_sec = new int[x];
 // Generate random centers and populate them with 1
 for (int i = 0; i < x; i++) {
 generateCentre(empty, h, w);
 // b.add(new Color(r.nextInt(256),r.nextInt(256),r.nextInt(256)));
 }
 for (int i = 0; i < h; i++) {
 for (int j = 0; j < w; j++) {
 double[] array = new double[2];
 array[0] = i;
 array[1] = j;
 Color c = new Color(img.getRGB(j, i));
 totalBlue[kd.nearest(array)] = totalBlue[kd.nearest(array)]
 + c.getBlue();
 totalRed[kd.nearest(array)] = totalRed[kd.nearest(array)]
 + c.getRed();
 totalGreen[kd.nearest(array)] = totalGreen[kd.nearest(array)]
 + c.getGreen();
 // img.setRGB(j, i, b.get(kd.nearest(array)).getRGB());
 counter_sec[kd.nearest(array)] = counter_sec[kd.nearest(array)] + 1;
 }
 }
 for (int i = 0; i < h; i++) {
 for (int j = 0; j < w; j++) {
 double[] array = new double[2];
 array[0] = i;
 array[1] = j;
 Color c = new Color(img.getRGB(j, i));
 // img.setRGB(j, i, b.get(kd.nearest(array)).getRGB());
 Color color = new Color(totalRed[kd.nearest(array)]/counter_sec[kd.nearest(array)], totalGreen[kd.nearest(array)]/counter_sec[kd.nearest(array)],totalBlue[kd.nearest(array)]/counter_sec[kd.nearest(array)]);
 img.setRGB(j, i, color.getRGB());
 }
 }
 File outputfile = new File("image.jpg");
 ImageIO.write(img, "jpg", outputfile);
 System.out.println(totalRed[0]/counter_sec[0]+" "+totalGreen[0]/counter_sec[0]+" "+totalBlue[0]/counter_sec[0]);
}
public static void generateCentre(int[][] empty, int h, int w)
 throws KeySizeException, KeyDuplicateException {
 r = new Random();
 int height = r.nextInt(h);
 int width = r.nextInt(w);
 for (int row = 0; row < h; row++) {
 for (int col = 0; col < w; col++) {
 if (row == height && col == width && empty[height][width] == 0) {
 empty[height][width] = 1;
 keys[counter][0] = row;
 keys[counter][2] = col;
 kd.insert(keys[counter], counter);
 }/*else if (row == height && col == width
 && empty[height][width] != 0) {
 generateCentre(empty, h, w);
 }*/
 }
 }
 System.out.println(kd.search(keys[counter]));
 if (counter < x) {
 counter++;
 }
}
}
Marko Topolnik
201k31 gold badges336 silver badges454 bronze badges
asked Sep 15, 2014 at 17:39
4
  • 11
    It takes four minutes to compile that? Are you sure you don't mean it takes four minutes to run it? Commented Sep 15, 2014 at 17:42
  • 3
    BTW no need to populate a Java array with zeros, it comes pre-initialized. Commented Sep 15, 2014 at 17:45
  • 1
    I sincerely doubt that it takes four minutes to compile. It looks terribly inefficient to me. You do that m*n looping four times. I'd wonder if you could combine it into a single loop and reduce the runtime by a factor of 4. Commented Sep 15, 2014 at 17:46
  • Given that the code works, you may want to flag this for migration to CodeReview.SE where issues of performance and quality of working, correct code can be addressed. Commented Sep 15, 2014 at 18:02

1 Answer 1

2

Although I don't claim a full understanding of your code, I see one potentially large cause of slowdown:

 totalBlue[kd.nearest(array)] = totalBlue[kd.nearest(array)]
 + c.getBlue();
 totalRed[kd.nearest(array)] = totalRed[kd.nearest(array)]
 + c.getRed();
 totalGreen[kd.nearest(array)] = totalGreen[kd.nearest(array)]
 + c.getGreen();
 // img.setRGB(j, i, b.get(kd.nearest(array)).getRGB());
 counter_sec[kd.nearest(array)] = counter_sec[kd.nearest(array)] + 1;

You seem to be repeatedly calling kd.nearest(array), which is probably not a very cheap operation. You should call that once and store it in a local variable. I would rewrite your key loop as follows:

double[] coords = new double[2];
for (int i = 0; i < h; i++) {
 for (int j = 0; j < w; j++) {
 coords[0] = i;
 coords[1] = j;
 final int nearest = kd.nearest(coords);
 final Color c = new Color(img.getRGB(j, i));
 totalBlue[nearest] += c.getBlue();
 totalRed[nearest] += c.getRed();
 totalGreen[nearest] += c.getGreen();
 counter_sec[nearest]++;
 }
}

Here I have utilized the += and ++ operators to make things shorter and more readable. I have also hoisted your coordinates array outside the loop because it can be safely reused at each iteration. This reduces GC time, but more importantly it helps CPU caches.

Apply the same changes to your second loop, where you generate the output image.

answered Sep 15, 2014 at 17:49

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.