Capricia Six
Capricia Six

Reputation: 25

Rotation of pixel array

I'm a beginner in java.

I want to do rotation on my own, I know how to use Graphics class, but I try to understand how It works within those class.

I have an image selected randomly from google Image. I turned it into a pixel's array, with that function and it works well :

BufferedImage immeuble = ImageIO.read( new File( "....jpg" ) );
int width = immeuble.getWidth();
int height = immeuble.getHeight();
int[] pixArray;
pixArray = Util.convertToPixels( immeuble );



public static int[] convertToPixels(Image img) {
            int width = img.getWidth(null);
            int height = img.getHeight(null);
            int[] pixel = new int[width * height];

        PixelGrabber pg = new PixelGrabber(img, 0, 0, width, height, pixel, 0, width);
        try {
            pg.grabPixels();
        } catch (InterruptedException e) {
            throw new IllegalStateException("Error: Interrupted Waiting for Pixels");
        }
        if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
            throw new IllegalStateException("Error: Image Fetch Aborted");
        }
        return pixel;
    }

Then, I rotated this pixel array according to a given angle.

pixArray = util.rotate( angle, pixArray, width, height );

public int[] rotate( double angle, int[] pixels, int width, int height ) {
        final double radians = Math.toRadians( angle );
        final double cos = Math.cos( radians );
        final double sin = Math.sin( radians );
        final int[ ] pixels2 = new int[ pixels.length ];
        for( int pixel = 0; pixel < pixels2.length; pixel++ ) {
            pixels2[pixel] = 0xFFFFFF;
        }
        for( int x = 0; x < width; x++ ) {
            for( int y = 0; y < height; y++ ) {
                final int centerx = width / 2;
                final int centery = height / 2;
                final int m = x - centerx;
                final int n = y - centery;
                final int j = ( ( int ) ( m * cos + n * sin ) ) + centerx;
                final int k = ( ( int ) ( n * cos - m * sin ) ) + centery;
                if( j >= 0 && j < width && k >= 0 && k < height ){
                    pixels2[ ( y * width + x ) ] = pixels[ ( k * width + j ) ];
                }
            }
        }
        return pixels2;
    }

And then, I display the new image with the rotation, with the array (pixArray) transform into a 2d array :

array2d = util.monoToMulti( pixArray, height, width );
public int[][] monoToMulti(final int[] array, final int height, final int width) {
        if (array.length != (height * width))
            throw new IllegalArgumentException("Invalid array length");

        int[][] multi = new int[height][width];

        for ( int i = 0; i < height; i++ )
            System.arraycopy(array, (i*width), multi[i], 0, width);

        return multi;
    }

And creates a new BufferedImage :

BufferedImage bufferedImage = new BufferedImage( array2d.length, array2d[0].length,
                                                         BufferedImage.TYPE_INT_ARGB );
        for ( int x = 0; x < array2d.length; x++ )
        {
            for ( int y = 0; y < array2d[x].length; y++ )
            {
                bufferedImage.setRGB( x, y, array2d[x][y] );
            }
        }


        util.saveToPNG( bufferedImage, "rotation_test" );

I have several problems :

Thanks for the help !

Edit : For example with an angle of 18°

double angle = 18;


public void saveToPNG(Image image, String name) throws IOException {
        File f = new File (name + ".png");
        try {
            ImageIO.write((RenderedImage) image, "png", f);
        } catch (IOException e) {
            e.printStackTrace();
        }

Upvotes: 0

Views: 1537

Answers (1)

Thomas
Thomas

Reputation: 88707

First I'd like to say that I agree with ControlAltDel that you'd probably be better off using the built-in functionality.

However, I'll try to answer your questions:

First one is : my image even if I don't make the rotation part (util.rotate). The image in output is rotated by an angle of 90°C. And I don't know why, I can't see where the problem comes from.

That's probably from your code that creates a new image from the 2d pixel array:

Your array's 1st dimension is height, i.e. each element is an array of a line's pixels. But when setting the pixels to the new array you're wapping y and x, i.e. you iterate over the lines and assign the line index to x. That's the swapping error you get.

Example: you correctly applied the 1D array layout, i.e. it is all pixels of line 1 then all of line 2 etc., also indicated in your code here: pixels2[ ( y * width + x ) ]

When you split that array into a 2D array, you declare it as follows:

int[][] multi = new int[height][width];

As you can see, the first dimension is height, however, when looping over that array again you access the values like this:

bufferedImage.setRGB( x, y, array2d[x][y] );

So you basically pass the x coordinate as the index of the height and vice versa, hence the 90 degree rotation (you basically swap x and y).

To fix that just change the loops to start with y first.

Then, if I apply the rotation part, the image is cut. Because I keep the width and height of the input image, And I would like to calculate the extent of the output image, so I can have the full image in output. I think there is some sinus ou cosinus calculation in that, but I'm not good at all in trigonometry.

You could calculate the rotated position of the corners first (you'd actually need only two of them) and calculate their distance to the center on both x and y and use the highest values to create your bounding box for the rotated image.

Since you're rotating around the center you'll only need two corners and their maximum distances to the center on x and y. The final dimensions would then double those distances.

Example:

You only need two corners, so you could use the first and last pixel of the first line. Those would have the coordinates (x/y) 0/0 and width/0.

Now feed those to your rotation code (btw, calculate the center of the rectangle outside the loop) and get the rotated coordinates, e.g. x1/y1 and x2/y2.

Get the maximum distance of those coordinates from the center on x and y, i.e. use Math.max( Math.abs(x1-center_x), Math.abs(x2-center_x)) etc.

Double that maximum distance and you have your dimensions for the new array.

Now rotate your original image around its center but add an offset to the coordinates before putting them into the new array. The offset would be half the difference in width and height between both arrays.

Upvotes: 1

Related Questions