I've made two functions for image processing:
- First one is for rotating an image
- 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.
-
\$\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– Phrancis2016年11月09日 21:09:31 +00:00Commented 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– J Dub2016年11月09日 21:23:43 +00:00Commented 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– J.D2016年11月09日 21:50:55 +00:00Commented 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– Octopus2016年11月09日 22:55:28 +00:00Commented 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– J.D2016年11月09日 23:21:03 +00:00Commented Nov 9, 2016 at 23:21
1 Answer 1
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
.
-
\$\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– J.D2016年11月10日 05:28:58 +00:00Commented Nov 10, 2016 at 5:28
-
\$\begingroup\$ Math is unbeatable. \$\endgroup\$t3chb0t– t3chb0t2016年11月10日 20:57:32 +00:00Commented 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– J.D2016年11月10日 22:06:14 +00:00Commented Nov 10, 2016 at 22:06