How to rotate a 2D array of pixels about it's "center" - java

I've been given a 2D array of pixels and I am suppose to rotate this image based off of the pixel array about it's centermost point. I've tried to implement code which rotates the image based off of the rotation matrix, but I haven't been successful so far.
Current 2D Array Rotation Code:
int width = originalImage.length;
int height = originalImage[0].length;
final double angle = 90;
int[][] array = new int[width][height];
double c = Math.cos(Math.toRadians(angle));
double s = Math.sin(Math.toRadians(angle));
int x = width / 2;
int y = height / 2;
for (int xx = 0; xx < width; xx++) {
for (int yy = 0; yy < height; yy++) {
int xp = xx - x;
int yp = yy - y;
int xa = (int)((float)((float)xp * c - (float)yp * s));
int ya = (int)((float)((float)xp * s + (float)yp * c));
xa += x;
ya += y;
xp += x;
yp += y;
if(xa < width && ya < height) array[xa][ya] = originalImage[xp][yp];
//System.out.print("\n"+xa+" "+ya);
}
}
I've also tried this:
int[][] array = new int[originalImage.length][originalImage[0].length];
int xx = 0, yy = 0;
for (int x = originalImage.length - 1; x >= 0; x--) {
xx = 0;
for (int y = 0; y < originalImage[x].length; y++) {
//System.out.println(array[yy][xx]);
//System.out.println(originalImage[y][x]);
if (y < originalImage.length && x < originalImage[x].length) {
array[yy][xx] = originalImage[y][x];
//System.out.print(array[yy][xx]);
}
xx++;
}
yy++;
}
Are there any suggestion to how I can improve my code, or how this should be done?

since you are rotating by 90 degrees you can skip a bit
// origin to center point
int xp = xx - x;
int yp = yy - y;
// rotation and origin back to (0,0)
int x_rotated = -yp + y;
int y_rotated = xp + x;
//
array[x_rotated][y_rotated] = originalImage[xx][yy];
If you want to rotate in a different direction change
int x_rotated = yp + y;
int y_rotated = -xp + x;

Related

Rotate 2D array by alpha degrees

I wrote a function which takes two parameters:
JPG image as 3D array
rotation degrees given by alpha
My approach was:
public static int[][] rotate(int[][] img, double alpha) {
double rad = Math.toRadians(alpha);
double sin = Math.sin(rad);
double cos = Math.cos(rad);
int height = img.length;
int width = img[0].length;
int[][] rotate = new int[height][width];
for(int i = 0; i < height; i++) {
for(int j = height - i - 1; j < width; j++) {
if(j < height && i < width) {
double i_new = Math.floor(cos * (img[i].length - i) - sin * (img[j].length - j)) + i;
double j_new = Math.floor(sin * (img[i].length - i) + cos * (img[j].length - j)) + j;
rotate[i][j] = img[(int)j_new][(int)i_new];
}
}
}
return rotate;
}
While fixing the index range, the output is a black image. What am I missing?
After a while I got to a solution.
Caution: Its not using any special pre-defined libraries.
The global function which run`s over the matrice:
public static int[][] rotate(int[][] img, double alpha) {
double rad = Math.toRadians(alpha); //construct of the relevant angles
double sin = Math.sin(rad);
double cos = Math.cos(rad);
int height = img.length;
int width = img[0].length;
int[][] rotate = new int[height][width];
int a = height / 2; //we will use the area of a and b to compare coordinates by the formula given
int b = width / 2;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
double i_new = Math.floor(cos * (i - a) - sin * (j - b)) + a; // following the conversion function
double j_new = Math.floor(sin * (i - a) + cos * (j - b)) + b;
if (i_new >= rotate.length || i_new < 0 || j_new >= rotate[0].length || j_new < rotate[0][0]) { // if out of scope of the conversion --> print none
System.out.print(""); //mainly cause 'continue' statements are not necessary in java and JS
} else {
rotate[(int) i_new][(int) j_new] = img[i][j]; //convert
}
}
}
return rotate;
}
The global function which rotates each 2D matrice:
public static int[][][] rotate_alpha(int[][][] img, double alpha) {
int height = img[0].length;
int width = img[0][0].length;
int[][][] rotated = new int[3][height][width];
for (int k = 0; k < 3; k++) {
rotated[k] = rotate(img[k], alpha);
}
return rotated;
}
Hope this topic is solved by now, and stands by all the standards of the clean code.

What's wrong with image rotation code?

The image rotates with code below, but wrong, some black dots appears on original image. I believe it's something with rotation code. Any solution? Thanks. The image dimensions is 32x32 pixels loaded on center of screen (320x240).
public class RendPanel extends JPanel {
private static final long serialVersionUID = 1L;
int widthe = 320;
int heighte = 240;
double angle = Math.toRadians(220);
double sin = Math.sin(angle);
double cos = Math.cos(angle);
double x0 = 0.5 * (widthe - 1); // point to rotate about
double y0 = 0.5 * (heighte - 1); // center of image
public static BufferedImage fbuffer;
public RendPanel(int width, int height) {
fbuffer = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
BufferedImage in = null;
try { in = ImageIO.read(new File("square.png")); } //32x32 square .png
catch (IOException e) { e.printStackTrace(); }
for (int i = 0; i < in.getWidth(); i++) {
for (int j = 0; j < in.getHeight(); j++) {
fbuffer.setRGB(i + (320 / 2) - 16, j + (240 / 2) - 16, in.getRGB(i, j));
}
}
BufferedImage neww = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < widthe; x++) {
for (int y = 0; y < heighte; y++) {
if(x >= x0 - 32 && x <= x0 + 32 && y >= y0 - 32 && y <= y0 + 32){
double a = x - x0;
double b = y - y0;
int xx = (int) (+a * cos - b * sin + x0);
int yy = (int) (+a * sin + b * cos + y0);
// plot pixel (x, y) the same color as (xx, yy) if it's in bounds
if (xx >= 0 && xx < width && yy >= 0 && yy < height) {
neww.setRGB(xx, yy, fbuffer.getRGB(x, y));
}
}
}
}
fbuffer = neww;
repaint();
setPreferredSize(new Dimension(width, height));
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(fbuffer, 0, 0, null);
}
}
A beginner's mistake (sorry).
Taking every source pixel in turn, transforming the coordinates to the destination and copying the pixel value is not the right way.because the regular input grid will not map to a regular grid, and there will be voids (and overlaps).
The correct way is to scan the destination image (so that every destination pixel is reached) and counter-transform the coordinates to fetch the pixel value from the source.
As a refinement, you can use the four neighboring pixel from where you land in the source and perform bilinear interpolation, to reduce aliasing.
Man, it's strange, because in this code it works properly!
Heres a working code:
public class RendPanel extends JPanel {
private static final long serialVersionUID = 1L;
int widthe = 320;
int heighte = 240;
int ang = 0;
double x0 = 0.5 * (widthe - 1); // point to rotate about
double y0 = 0.5 * (heighte - 1); // center of image
public static BufferedImage fbuffer;
public RendPanel(int width, int height) {
fbuffer = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
BufferedImage in = null;
try { in = ImageIO.read(new File("square.png")); } //32x32 square .png
catch (IOException e) { e.printStackTrace(); }
for (int i = 0; i < in.getWidth(); i++) {
for (int j = 0; j < in.getHeight(); j++) {
fbuffer.setRGB(i + (320 / 2) - 16, j + (240 / 2) - 16, in.getRGB(i, j));
}
}
setPreferredSize(new Dimension(width, height));
}
BufferedImage neww;
public void r(){
neww = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
double angle = Math.toRadians(ang);
double sin = Math.sin(angle);
double cos = Math.cos(angle);
for (int x = 0; x < widthe; x++) {
for (int y = 0; y < heighte; y++) {
if(x >= x0 - 32 && x <= x0 + 32 && y >= y0 - 32 && y <= y0 + 32){
double a = x - x0;
double b = y - y0;
int xx = (int) (+a * cos - b * sin + x0);
int yy = (int) (+a * sin + b * cos + y0);
// plot pixel (x, y) the same color as (xx, yy) if it's in bounds
if (xx >= 0 && xx < widthe && yy >= 0 && yy < heighte) {
neww.setRGB(x, y, fbuffer.getRGB(xx, yy));
}
}
}
}
ang++;
repaint();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(neww, 0, 0, null);
}
}
Thanks for:
https://introcs.cs.princeton.edu/java/31datatype/Rotation.java.html
EDIT:
You have to invert the vars on bf2.setRGB(x, y, fbuffer.getRGB(xx, yy)); to the rotated coordinate.

For loops creating black screen without reading integer array

Whenever I tried to get the pixel array in the ori class, which extends the Screen class, to render to the canvas (that, in turn, is written in the canvas in the main class) it just generated a black image over the canvas in the JFrame. This only happened when the 'for' loops are written as "x = 0" as opposed to "int x = 0". When the loops are written as "int x = 0" or "int y= 0", it did not generate anything, including the black screen.
This is from the origin method in the ori class:
if(x <= ax && y <= ay){
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
pixels[x + y * width] = 0xff00ff;
}
}
}
screen class:
void waves(){
rand = new Random();
for(y = 0; y < height; y++){
int r = 24 + 1;
int s = 10;
waves++;
if(waves == 75){
sel = rand.nextInt(2);
waves = 0;
}
if(sel == 0){
if(blue > 0){
float f = ray - r;
double uir = f * 0.0001;
u = blue *= uir;
}
else{
}
}
if(sel == 1){
if(blue > 0){
float f = ray + r;
double uir = f * 0.0001;
u = blue *= uir;
}
else{
}
}
for(x = 0; x < width; x++){
Color wacol = new Color(0, 0, u);
int water = wacol.getRGB();
pixels[x + y * width] = water;
orga.origin();
}
}
}
It appears that the 'for' loops written as "x = 0" are called upon and then, somehow, render without reading the pixel array. The same happens when "int x = 0" but with the opposite result. What is causing this and what is causing the code to ignore the pixel array?

What is happening in this pixel-rendering algorithm?

I just went across some code on how to draw a pixel array on top of another pixel array that looks like this:
public class Bitmap {
private int[] pixels;
private int w, h;
public void draw(Bitmap b, int xp, int yp) {
int x0 = xp;
int x1 = xp+b.w;
int y0 = yp;
int y1 = yp+b.h;
if(x0 < 0) x0 = 0;
if(x1 > w) x1 = w;
if(y0 < 0) y0 = 0;
if(y1 > h) y1 = h;
for (int y = y0; y < y1; y++) {
int sp = (y - yp) * b.w - xp;
int dp = (y) * w;
for (int x = x0; x < x1; x++) {
int c = b.pixels[sp + x];
if (c < 0) pixels[dp + x] = b.pixels[sp + x];
}
}
}
}
As You can see, one is able to draw a Bitmap object on specific coordinates on top of another Bitmap.
The thing I don't get are the two for loops. I know, that the outer for loop is the y axis of the Bitmap drawn, and starts the inner for loop to draw the x axis of the Bitmap.
Now I came over this:
int sp = (y - yp) * b.w - xp;
int dp = (y) * w;
What exactly do sp and dp stand for? And what does 'c' mean later on in
int c = b.pixels[sp + x];
if (c < 0) pixels[dp + x] = b.pixels[sp + x];
?
Thanks in advance, best regards
Given the algorithm, we can guess what the original author was thinking:
sp is "source position": the start of the row in the source bitmap
dp is "destination position": the start of the row in the destination bitmap
c is "color": the source pixel value (where negative values are transparent).

Perlin Noise Generation not functioning properly

I have been working with perlin noise recently and when implementing it into a tile engine I am using, I have noticed that the perlin noise function produced "blocks" as seen in the picture below. Each pixel is another different location in a 500 by 500 array that is returned from the perlin noise function.
in this example the persistence is 0.5 with an octave count of 5
When playing with it further, the more octaves I have, the larger the block chunks.
Here is the Code that I am using to call the perlin noise function:
PerlinNoise p = new PerlinNoise();
//returns a float[][] array of 500 by 500
p.GeneratePerlinNoise(p.genWhiteNoise(500, 500), 5, (float) 0.1);
PerlinNoise class
import java.util.Random;
public class PerlinNoise {
Random r;
public PerlinNoise() {
r = new Random();
}
public void setSeed(long seed) {
r.setSeed(seed);
}
public void printOutArray(float[][] arr) {
for(int i = 0; i < arr.length; i++) {
for(int n = 0; n < arr[0].length; n++) {
System.out.print(arr[i][n] + ", ");
}
System.out.print("\n");
}
}
public void printOutTerrain(float[][] arr) {
for(int i = 0; i < arr.length; i++) {
for(int n = 0; n < arr[0].length; n++) {
float a = arr[i][n];
if(a < 0.4) {
System.out.print("W");
} else {
System.out.print("L");
}
}
System.out.print("\n");
}
}
//-------------------------------------------------------------//
float[][] genWhiteNoise(int width, int height) {
float[][] noise = new float[height][width];
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
noise[y][x] = r.nextFloat();
}
}
return noise;
}
float[][] genSmoothNoise(float[][] baseNoise, int octave) {
int height = baseNoise.length;
int width = baseNoise[0].length;
float[][] smoothNoise = new float[height][width];
int samplePeriod = 1 << octave; //calculates 2^k
float sampleFrequency = (float) (1.0/samplePeriod);
for(int i = 0; i < height; i++) {
int sample_i0 = (i / samplePeriod) * samplePeriod;
int sample_i1 = (sample_i0 + samplePeriod) % height; //wrap around
float vertical_blend = (i - sample_i0) * sampleFrequency;
for(int n = 0; n < width; n++) {
int sample_n0 = (n / samplePeriod) * samplePeriod;
int sample_n1 = (sample_n0 + samplePeriod) % width; //wrap around
float horizontal_blend = (n - sample_n0) * sampleFrequency;
//blend the top two corners
float top = Interpolate(baseNoise[sample_i0][sample_n0],
baseNoise[sample_i1][sample_n0], horizontal_blend);
//blend the bottom two corners
float bottom = Interpolate(baseNoise[sample_i0][sample_n1],
baseNoise[sample_i1][sample_n1], horizontal_blend);
//final blend
smoothNoise[i][n] = Interpolate(top, bottom, vertical_blend);
}
}
return smoothNoise;
}
float[][] GeneratePerlinNoise(float[][] baseNoise, int octaveCount, float persistance)
{
int height = baseNoise.length;
int width = baseNoise[0].length;
float[][][] smoothNoise = new float[octaveCount][][]; //an array of 2D arrays containing
//generate smooth noise
for (int i = 0; i < octaveCount; i++)
{
smoothNoise[i] = genSmoothNoise(baseNoise, i);
}
float[][] perlinNoise = new float[height][width];
float amplitude = 1.0f;
float totalAmplitude = 0.0f;
//blend noise together
for (int octave = octaveCount - 1; octave >= 0; octave--)
{
amplitude *= persistance;
totalAmplitude += amplitude;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude;
}
}
}
//normalisation
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
perlinNoise[i][j] /= totalAmplitude;
}
}
return perlinNoise;
}
//linear average between two points
float Interpolate(float x0, float x1, float alpha)
{
return Cosine_Interpolate(x0, x1, alpha);
}
//Linear Interpolation
float Linear_Interpolate(float x0, float x1, float alpha)
{
return x0 * (1 - alpha) + alpha * x1;
}
//Cosine interpolation (much smoother)
float Cosine_Interpolate(float x0, float x1, float alpha)
{
float ft = (float) (alpha * 3.141592653589);
float f = (float) ((1 - Math.cos(ft)) * 0.5);
return x0*(1-f) + x1*f;
}
}
So to reiterate my question: Why is my perlin noise function behaving the way it does, as in only generating space in chunks?
So to fix this, all I had to do is swap the vertical_blend and horizontal_blend variables in the genSmoothNoise() method. It's amazing what you notice after a break

Categories