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.

\$\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\$
    – Phrancis
    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\$
    – J Dub
    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\$
    – J.D
    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\$
    – Octopus
    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\$
    – J.D
    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.

\$\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\$
    – J.D
    Commented Nov 10, 2016 at 5:28
  • \$\begingroup\$ Math is unbeatable. \$\endgroup\$
    – t3chb0t
    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\$
    – J.D
    Commented Nov 10, 2016 at 22:06

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.