2
\$\begingroup\$

I have an image which I know the number of rows and the number of columns. I would like to calculate the limits of the new image after a rotation.

   BufferedImage myImage = ImageIO.read( new File( "D:\\Users...jpg" ) );
    xmaxOrigine = myImage.getWidth() - 1;
    ymaxOrigine = myImage.getHeight() - 1;
    angle = 12;

    angleRadian = Math.toRadians( angle );
    cos = Math.cos( angleRadian );
    sin = Math.sin( angleRadian );

    p1X = (int) ( ( xmaxOrigine * cos ) - ( ymaxOrigine * sin ) );
    p1Y = (int) ( ( xmaxOrigine * sin ) + ( ymaxOrigine * cos ) );

    xmin_f = xmin_f < p1X ? xmin_f : p1X;
    xmax_f = xmax_f < p1X ? p1X : xmax_f;
    ymin_f = ymin_f < p1Y ? ymin_f : p1Y;
    ymax_f = ymax_f < p1Y ? p1Y : ymax_f;

    p2X = (int) ( ( 0  * cos ) - ( ymaxOrigine * sin ) );
    p2Y = (int) ( ( 0 * sin ) + ( ymaxOrigine * cos ) );

    xmin_f = xmin_f < p2X ? xmin_f : p2X;
    xmax_f = xmax_f < p2X ? p2X : xmax_f;
    ymin_f = ymin_f < p2Y ? ymin_f : p2Y;
    ymax_f = ymax_f < p2Y ? p2Y : ymax_f;

    p3X = (int) ( ( xmaxOrigine * cos ) - ( 0 * sin ) );
    p3Y = (int) ( ( xmaxOrigine * sin ) + ( 0 * cos ) );

    xmin_f = xmin_f < p3X ? xmin_f : p3X;
    xmax_f = xmax_f < p3X ? p3X : xmax_f;
    ymin_f = ymin_f < p3Y ? ymin_f : p3Y;
    ymax_f = ymax_f < p3Y ? p3Y : ymax_f;

    p4X = 0;
    p4Y = 0;

    xmin_f = xmin_f < p4X ? xmin_f : p4X;
    xmax_f = xmax_f < p4X ? p4X : xmax_f;
    ymin_f = ymin_f < p4Y ? ymin_f : p4Y;
    ymax_f = ymax_f < p4Y ? p4Y : ymax_f;

    widthFinal = xmax_f - xmin_f;
    heightFinal = ymax_f - ymin_f;

As you can see I look for the xmin, xmax, ymin, ymax for each points. I would like to make this operation once if it is possible. Also, how can I condense this code?

\$\endgroup\$
  • \$\begingroup\$ Where are the min and max variables declared and initialized? \$\endgroup\$ – JS1 Aug 19 '15 at 9:01
1
\$\begingroup\$
xmaxOrigine

So this looks like you are mixing a French word with English. Be careful of that. A native English reader will be mildly confused by this, as it looks similar to original which would make sense in that context. But the real problem is with someone for whom origine does not look like a word. For example, take native Mandarin speakers. Presumably they know how to look up English words that they don't understand in the English code. They won't however know that this is not an English word. So they won't find it in an English directory and won't know that they should look in a French dictionary.

Perhaps your context will have you overwhelmingly sharing this code with people who speak enough French to know what you mean. But if you do need to share with someone who doesn't speak French, that person may be confused by things like this.

    xmin_f = xmin_f < p1X ? xmin_f : p1X;
    xmax_f = xmax_f < p1X ? p1X : xmax_f;
    ymin_f = ymin_f < p1Y ? ymin_f : p1Y;
    ymax_f = ymax_f < p1Y ? p1Y : ymax_f;

This would be easier to read if you used the natural language constructs for this.

    xmin_f = Math.min(xmin_f, p1X);
    xmax_f = Math.max(p1X, xmax_f);
    ymin_f = Math.min(ymin_f, p1Y);
    ymax_f = Math.max(p1Y, ymax_f);

You also do this repeatedly. If these are object fields, you can make a method to do this for you.

private void expandBoundaries(int x, int y) {
    xmin_f = Math.min(xmin_f, x);
    xmax_f = Math.min(xmax_f, x);
    ymin_f = Math.min(ymin_f, y);
    ymax_f = Math.min(ymax_f, y);
}

If they are not object fields, you can make objects to track them. Then you'd use them like

    xRange.include(p1X);
    yRange.include(p1Y);

and at the end

    widthFinal = xRange.getMax() - xRange.getMin();
    heightFinal = yRange.getMax() - yRange.getMin();

or even

    widthFinal = xRange.getDifference();
    heightFinal = yRange.getDifference();

with include defined as

public void include(T value) {
    min = Math.min(min, value);
    max = Math.max(max, value);
}

You can also save some multiplications by storing the values for later use:

    float rotatedX2X = xmaxOrigine * cos;
    float rotatedY2X = ymaxOrigine * sin;
    float rotatedX2Y = xmaxOrigine * sin;
    float rotatedY2Y = ymaxOrigine * cos;

Then you could rewrite this code as

    BufferedImage myImage = ImageIO.read( new File( "D:\\Users...jpg" ) );
    xmaxOrigine = myImage.getWidth() - 1;
    ymaxOrigine = myImage.getHeight() - 1;

    angle = 12;
    angleRadian = Math.toRadians( angle );
    cos = Math.cos( angleRadian );
    sin = Math.sin( angleRadian );

    float rotatedX2X = xmaxOrigine * cos;
    float rotatedY2X = ymaxOrigine * sin;
    float rotatedX2Y = xmaxOrigine * sin;
    float rotatedY2Y = ymaxOrigine * cos;

    xRange.include((int) (rotatedX2X - rotatedY2X));
    yRange.include((int) (rotatedX2Y + rotatedY2Y));
    xRange.include((int)-rotatedY2X);
    yRange.include((int)rotatedY2Y);
    xRange.include((int)rotatedX2X);
    yRange.include((int)rotatedX2Y);
    xRange.include(0);
    yRange.include(0);

    widthFinal = xRange.getDifference();
    heightFinal = yRange.getDifference();

Note that this gets rid of p1X, p1Y, etc. They weren't used in the original code snippet. It's possible that they were used elsewhere. If so, you'd have to put them back.

Then your Range class:

public class Range<T extends Number> {
    private T max;
    private T min;

    public Range(T min, T max) {
        this.min = min;
        this.max = max;
    }

    public void include(T value) {
        min = Math.min(min, value);
        max = Math.max(max, value);
    }

    public T getMin() {
        return min;  
    }

    public T getMax() {
        return max;  
    }

    public T getDifference() {
        return max - min;
    }
}

Note that you have to initialize the Range before using it. You don't show the initialization in the original code snippet, so I didn't show it here. It would look something like

    Range xRange = new Range(small, large);
\$\endgroup\$
0
\$\begingroup\$

I would start with a rotation matrix and think about, who you can combine all points to one matrix, so you "only" have to calculate one matrix multiplication.

The rotation matrix is

new double[][] {
    { Math.cos(theta), -Math.sin(thera) },
    { Math.sin(theta), Math.cos(theta) }
} 

And your matrix for the coordinates could be:

new double[][] {
    { x1, x2, ... },
    { y1, y2, ... }
}

You can find many examples how to calculate a matrix multiplication with Google. But you have to check for the right traversing over the arrays!

The result will be an array of rotated coordinates. Then you have to find the min and max of both rows by two for loops and you are done.

If you choose good names and extract some of the functionality in other methods (like creating the rotation matrix or the matrix multiplication, and so on) the code would be very short and nice to read.

It seems like you are coming from a C background. I primarily know the type suffixes like _f or _i for float and integer (you did only use the first one) only from C code. In Java you didn't do this. Most of the time you should use an IDE which can tell you the type of a variable.

Last thing I noticed: you cast a float to an integer. But this can lead to an off by one error:

int f = 0.6;
int a = (int) f; // is 0. Should be 1. 
Int b = (int) f + 0.5; // is 1. 
\$\endgroup\$
0
\$\begingroup\$

Off by one?

At the top, you subtract off one from the width and height like this:

xmaxOrigine = myImage.getWidth() - 1;
ymaxOrigine = myImage.getHeight() - 1;

I think you did this because you are considering the rectangle to span between the center of pixels instead of the borders. But then at the end, you don't add one back in:

widthFinal = xmax_f - xmin_f;
heightFinal = ymax_f - ymin_f;

So if I had a 100x100 image, and rotated by 0 degrees, your function would return 99x99. I believe you should add one back in to return to the original units. You may also want to add another 0.5 to handle rounding:

widthFinal = xmax_f - xmin_f + 1.5f;
heightFinal = ymax_f - ymin_f + 1.5f;

Of course, I've made a few assumptions about what your program is intending to do. You should test your program yourself with various image sizes and angles to make sure your results match what you expect.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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