2
\$\begingroup\$

I've made two functions for image processing:

  1. First one is for rotating an image
  2. Second one is to set all pixels of one color to another color

Rotate Image:

private static void roateImage(BufferedImage pic1) throws IOException {
 int width = pic1.getWidth(null);
 int height = pic1.getHeight(null);
 double angle = Math.toRadians(90);
 double sin = Math.sin(angle);
 double cos = Math.cos(angle);
 double x0 = 0.5 * (width - 1); // point to rotate about
 double y0 = 0.5 * (height - 1); // center of image
 WritableRaster inRaster = pic1.getRaster();
 BufferedImage pic2 = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
 WritableRaster outRaster = pic2.getRaster();
 int[] pixel = new int[3];
 // rotation
 for (int x = 0; x < width; x++) {
 for (int y = 0; y < height; y++) {
 double a = x - x0;
 double b = y - y0;
 int xx = (int) (+a * cos - b * sin + x0);
 int yy = (int) (+a * sin + b * cos + y0);
 if (xx >= 0 && xx < width && yy >= 0 && yy < height) {
 outRaster.setPixel(x, y, inRaster.getPixel(xx, yy, pixel));
 }
 }
 }
 ImageIO.write(pic2, "bmp", new File("Images/Output2.bmp"));
}

Please help me to optimize this code.

200_success
145k22 gold badges190 silver badges478 bronze badges
asked Nov 9, 2016 at 20:26
\$\endgroup\$
6
  • \$\begingroup\$ You could just leave this open and edit out one of the 2 and just open one more post. By the way looks like there's an error in the spelling of your rotation method, it's spelled "roateImage" instead of rotateImage \$\endgroup\$ Commented Nov 9, 2016 at 21:09
  • 1
    \$\begingroup\$ For the rotation, it looks like you are cutting off the corners. Is that a desirable effect? \$\endgroup\$ Commented Nov 9, 2016 at 21:23
  • \$\begingroup\$ Ok well... I've edit this question and now I have 2 posts, one just with rotation and one just with changing color of pixels. And no @JDub I don't have any kind of effects, or I didn't notice this. \$\endgroup\$ Commented Nov 9, 2016 at 21:50
  • 1
    \$\begingroup\$ Do you really want to rotate at arbitrary angles? Most image rotate functions (the basic ones anyway) will work for 90, 180 and 270 degrees only. Any other values really should be done with supersampling or they will look awful from aliasing effects and if you're not careful will contain many holes in the result. I would recommend you stick to just those angles. Now how will width and height be affected if we rotate 90 degrees? These aren't even optimization issues, these are what to watch out for to get it to work in the first place. \$\endgroup\$ Commented Nov 9, 2016 at 22:55
  • \$\begingroup\$ No, I want to rotate just with 90 degree or any kind of multiple of this, like 180 and 270. I will never need to rotate this images with any kind of other angle, just multiples of 90 \$\endgroup\$ Commented Nov 9, 2016 at 23:21

1 Answer 1

2
\$\begingroup\$

Math is fun; why waste it on the computer?

 double angle = Math.toRadians(90);
 double sin = Math.sin(angle);
 double cos = Math.cos(angle);

This is the same as

 int sin = 1;
 int cos = 0;

So

 int xx = (int) (+a * cos - b * sin + x0);
 int yy = (int) (+a * sin + b * cos + y0);

simplifies to

 int xx = (int) (- b + x0);
 int yy = (int) (+a + y0);

which simplifies to

 int xx = (int) (y0 + x0) - y;
 int yy = x + (int) (y0 - x0);

And you don't need a or b at all. But you could precalculate

 int sum = (int) (y0 + x0);
 int difference = (int) (y0 - x0);

right after you calculate y0 and x0 (i.e. before the loop). Then in the loop, you just need

 int xx = sum - y;
 int yy = difference + x;

Then you could rewrite

 if (xx >= 0 && xx < width && yy >= 0 && yy < height) {
 outRaster.setPixel(x, y, inRaster.getPixel(xx, yy, pixel));
 }

as

 if (sum >= y && sum - width < y && x >= -difference && x < height - difference) {
 outRaster.setPixel(x, y, inRaster.getPixel(xx, yy, pixel));
 }

But then we can change

 for (int x = 0; x < width; x++) {
 for (int y = 0; y < height; y++) {

to

 int right = Math.min(width, height - difference);
 int bottom = Math.min(height, sum + 1)
 for (int x = Math.max(0, -difference); x < right; x++) {
 for (int y = Math.max(0, sum - width + 1); y < bottom; y++) {

Now we don't have to check if xx and yy fit in the bounds. We only iterate over the pixels where they do. So we can drop the if and just say

 outRaster.setPixel(x, y, inRaster.getPixel(xx, yy, pixel));

TL;DR

private static void rotateImage(BufferedImage pic1) throws IOException {
 int width = pic1.getWidth(null);
 int height = pic1.getHeight(null);
 // point to rotate about center of image
 double x0 = 0.5 * (width - 1);
 double y0 = 0.5 * (height - 1);
 int sum = (int) (y0 + x0);
 int difference = (int) (y0 - x0);
 WritableRaster inRaster = pic1.getRaster();
 BufferedImage pic2 = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
 WritableRaster outRaster = pic2.getRaster();
 int[] pixel = new int[3];
 // rotation
 int right = Math.min(width, height - difference);
 int bottom = Math.min(height, sum + 1)
 for (int x = Math.max(0, -difference); x < right; x++) {
 for (int y = Math.max(0, sum - width + 1); y < bottom; y++) {
 int xx = sum - y;
 int yy = difference + x;
 outRaster.setPixel(x, y, inRaster.getPixel(xx, yy, pixel));
 }
 }
 ImageIO.write(pic2, "bmp", new File("Images/Output2.bmp"));
}

I also changed the spelling to rotateImage.

Now the only math done inside the loops is incrementing the index variables and simple integer subtraction and addition.

And we only increment over pixels that we're copying.

It's odd to take an image as input and then write a different image to a file. Why not return the image instead? Then the caller can save it.

Consider if you can make inRaster a Raster instead. I don't think that it needs to be writable, just outRaster.

answered Nov 10, 2016 at 4:27
\$\endgroup\$
3
  • \$\begingroup\$ Thanks a lot, that's really amazing. I've change every thing and now it's truly faster. I really appreciate your help with this, it's really useful :D. Could you help me to optimize and this function? I've post it here yesterday and I reallly need to optimize and that function. Thanks again for this one :). \$\endgroup\$ Commented Nov 10, 2016 at 5:28
  • \$\begingroup\$ Math is unbeatable. \$\endgroup\$ Commented Nov 10, 2016 at 20:57
  • \$\begingroup\$ Yes, I've try it and yes it's better. Now the single problem I have with performance it's about get/setRGB from this function because they are to slow.... Do you know what can I try to use there faster? I've try with get/setPixels but from some reason it's not working. Cand you help me with this please? \$\endgroup\$ Commented Nov 10, 2016 at 22:06

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.