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++;
}
}
}
-
11It takes four minutes to compile that? Are you sure you don't mean it takes four minutes to run it?Ideasthete– Ideasthete2014年09月15日 17:42:18 +00:00Commented Sep 15, 2014 at 17:42
-
3BTW no need to populate a Java array with zeros, it comes pre-initialized.Marko Topolnik– Marko Topolnik2014年09月15日 17:45:20 +00:00Commented Sep 15, 2014 at 17:45
-
1I 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.duffymo– duffymo2014年09月15日 17:46:34 +00:00Commented 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.user289086– user2890862014年09月15日 18:02:27 +00:00Commented Sep 15, 2014 at 18:02
1 Answer 1
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.