Java: Comparing two Bufferedimages using loops - java

When i want to check if two Bufferedimages are the same, i can simply compare them pixel by pixel using these two loops:
boolean bufferedImagesEqual(BufferedImage img1, BufferedImage img2) {
if (img1.getWidth() == img2.getWidth() && img1.getHeight() == img2.getHeight()) {
for (int x = 0; x < img1.getWidth(); x++) {
for (int y = 0; y < img1.getHeight(); y++) {
if (img1.getRGB(x, y) != img2.getRGB(x, y))
return false;
}
}
} else {
return false;
}
return true;
}
I took this from : Here
But I also want this to work if img2 is rotated by 90 or 270 degrees. I tried swtiching x and y , using combinations like
img2.getWidth()-x
in the second .getRGB() but none of it seems to work.
I know it may not be the hardest problem in the world but i cannot seem to figure it out.
Any help would be greatly appreciated.

I think it is unavoidable to perform the "2-loops" 3 times:
one for 0 degree rotation, the second 90 and the third 270:
For 90 degrees: (assert img1.getWidth()==img2.getHeight() && img1.getHeight()==img2.getWidth())
for (int x = 0; x < img1.getWidth(); x++) {
for (int y = 0; y < img1.getHeight(); y++) {
if (img1.getRGB(x, y) != img2.getRGB(img1.getHeight() - 1 - y, x))
return false;
}
}
For 270 degrees: (assert img1.getWidth()==img2.getHeight() && img1.getHeight()==img2.getWidth())
for (int x = 0; x < img1.getWidth(); x++) {
for (int y = 0; y < img1.getHeight(); y++) {
if (img1.getRGB(x, y) != img2.getRGB(y, img1.getWidth() - 1 - x))
return false;
}
}

Related

Repeated lines/stripes using for loops in Java

i want to paint black and white stripes on the image, switching every 20th column both horizontally and vertically on top of an image while staying inside it's borders. so far i can get a black square with 1 pixel wide vertical stripes. i've tried to at least get skinny white stripes on my horizontal lines by switching things around but it's still vertical.
public void zebraStripes() {
Image img = ImageViewer.getImage();
double numPixelsWide = img.getWidth();
int numPixelsHigh = img.getHeight();
Color c = Color.WHITE;
Color b = Color.BLACK;
double i = numPixelsWide;
if (i % 20 == 0) {
for (int x = 0; x < numPixelsHigh; x++) {
for (int y = 0; y < i; y++) {
img.setPixelColor(y, x, b);
}
for (int z = 19; z < i; z = z + 20) {
img.setPixelColor(z, x, c);
}
}
}
}
// paint black and white stripes (left to right) on the image, switching
// every 20th row
public void jailBird() {
Image img = ImageViewer.getImage();
double numPixelsWide = img.getWidth();
double numPixelsHigh = img.getHeight();
Color c = Color.WHITE;
Color b = Color.BLACK;
double i = numPixelsHigh;
if (i % 20 == 0) {
for (int x = 0; x < numPixelsHigh; x++) {
for (int y = 0; y < i; y++) {
img.setPixelColor(y, x, b);
}
for (int z = 19; z < i; z = z + 20) {
img.setPixelColor(z, x, c);
}
}
}
}
}
how do i get the white stripes to be 20 pixels wide and horizontal?
Not tested! Hope it gets you going.
// paint a 20 pixels wide horizontal line for every 40 pixels
for (int y = 0; y < numPixelsHigh; y += 40) {
// paint a stripe
for (int ys = y; ys < y + 20; ys++) {
for (int x = 0; x < numPixelsWide; x++) {
img.setPixelColor(x, ys, Color.BLACK);
}
}
}

Why does this recursive image comparison take less then primitive iterative one? (JAVA 8)

I have run into a little situation that puzzles me. Would someone be able to explain it?
I have written an algorithm in Java that compares two images by first comparing the two middle columns of the two images, then comparing the two middle rows of the two images, and then recursively comparing the four remaining quadrants in the two images.
Now, for some reason my test tells me that my algorithm is quicker then the primitive comparison which simply iterates over each row and each column.
I would have thought that the iterative approach would be faster as the number of comparisons is the same, while in the recursive approach we're doing additional calculations to work out the four quadrants and the middle row and column.
I have done a separate test ( which I won't include) to check that the algorithm does in fact go over every pixel in the image and it does.
Is there something I'm missing in my implementation or is there something about the Java compiler that's producing this result?
Here's the code:
Algo:
public boolean areIdentical(BufferedImage image1, BufferedImage image2) {
int height = image1.getHeight();
int width = image2.getWidth();
for(int y : new int[]{0, height-1}){
for (int x = 0; x < width; x++) {
if(!compareColors(image1.getRGB(x,y), image2.getRGB(x,y))) return false;
}
}
for(int x : new int[]{0,width-1}){
for(int y = 0; y < height; y++){
if(!compareColors(image1.getRGB(x,y), image2.getRGB(x,y))) return false;
}
}
return areIdenticalRecursiveCross(image1, image2, new BoxCoordinates(1,1, width-2,height-2));
}
public boolean areIdenticalRecursiveCross(BufferedImage image1, BufferedImage image2, BoxCoordinates boxToCheck){
int height = boxToCheck.getHeight();
int width = boxToCheck.getWidth();
int startX= boxToCheck.getX();
int startY=boxToCheck.getY();
if(height == 1 || height == 2 || width == 1 || width == 2){
for (int x = startX; x < startX+width; x++) {
for (int y = startY; y < startY+height; y++) {
if(!compareColors(image1.getRGB(x,y),image2.getRGB(x,y))) return false;
}
}
return true;
} else{
int newWidth = width/2 - 1 +width%2;
for(int x = startX+newWidth; x <= startX+width/2; x++){
for(int y = startY; y < startY+height; y++){
if(!compareColors(image1.getRGB(x,y),image2.getRGB(x, y))) return false;
}
}
int newHeight = height/2 - 1 + height%2;
for(int y = startY + newHeight; y <= startY + height/2; y++){
for(int x = startX; x < width+startX; x++){
if(!compareColors(image1.getRGB(x,y),image2.getRGB(x, y))) return false;
}
}
return areIdenticalRecursiveCross(image1, image2,new BoxCoordinates(startX, startY, newWidth, newHeight))
&& areIdenticalRecursiveCross(image1, image2,new BoxCoordinates(startX + width/2+1, startY, newWidth, newHeight))
&& areIdenticalRecursiveCross(image1, image2,new BoxCoordinates(startX, startY + height/2+1, newWidth, newHeight))
&& areIdenticalRecursiveCross(image1, image2,new BoxCoordinates(startX+ width/2+1, startY+height/2+1, newWidth, newHeight));
}
}
public boolean compareColors(int c1, int c2){
return c1 == c2;
}
Test:
public void testAreIdentical() throws Exception {
BufferedImage screen = takeScreenshot();
long start = System.currentTimeMillis();
boolean primitive = false;
for(int i = 0; i<1;i++) {
primitive = primitive(screen, screen);
}
long end =System.currentTimeMillis();
System.out.println("Primitive took: " + (end-start)/1000.0 + " returning " + primitive);
start = System.currentTimeMillis();
boolean recursive = false;
for (int i =0; i< 1;i++) {
recursive = matcher.areIdentical(screen, screen);
}
end = System.currentTimeMillis();
System.out.println("Failgorithm took: " + (end-start)/1000.0 + " returning " + recursive);
}
public boolean primitive(BufferedImage image1, BufferedImage image2){
for (int x = 0; x < image1.getWidth(); x++) {
for (int y = 0; y < image1.getHeight(); y++) {
if(image1.getRGB(x,y)!=image2.getRGB(x,y)) return false;
}
}
return true;
}
Test Result:
Primitive took: 0.161 returning true
Failgorithm took: 0.127 returning true
Failgorithm 2 took: 0.168 returning true
Interestingly if I change the algorithm to only check a single middle row and column on each recursive step, I get a worse result(Failgorithm 3).

Won't Draw Tiles?

Well, I'm making a relatively simple platformer game with java and I have a method that generates the level, but the tiles won't draw.
public void generateDungeon() {
int y = 30;
for(int x = 0; x < block[0].length; x++) {
block[x][y] = new Block(new Rectangle(x * Tile.tileSize, y * Tile.tileSize, Tile.tileSize, Tile.tileSize), Tile.basic);
}
}
I want the Y to stay the same so that's why it has a set value.
That is where I think the problem lies, but here's the console message
Exception in thread "Thread-2" java.lang.NullPointerException
at mineandbuild.Player.isCollidingWithBlock(Player.java:82)
at mineandbuild.Player.tick(Player.java:22)
at mineandbuild.Component.tick(Component.java:89)
at mineandbuild.Component.run(Component.java:110)
at java.lang.Thread.run(Unknown Source)
The console says the problem is at this line of code:
public boolean isCollidingWithBlock(Point pt1, Point pt2) {
for(int x = (int) (this.x / Tile.tileSize); x < (int) (this.x / Tile.tileSize + 3); x++) {
for(int y = (int) (this.y / Tile.tileSize); y < (int) (this.y / Tile.tileSize + 3); y++) {
if(x >= 0 && y >= 0 && x < Component.dungeon.block.length && y < Component.dungeon.block[0].length)
This line ---> if(Component.dungeon.block[x][y].id != Tile.air) {
if(Component.dungeon.block[x][y].contains(pt1) || Component.dungeon.block[x][y].contains(pt2)) {
return true;
}
}
}
}
return false;
}
Thanks!
Try printing out the x and y values for
if(Component.dungeon.block[x][y].id != Tile.air) {
You say that in the code before the value of y should stay the same, which means for every other index of y in the 2-d array they're set to null, but in the forloop you have this
for(int y = (int) (this.y / Tile.tileSize); y < (int) (this.y / Tile.tileSize + 3); y++) {
which leads me to believe that you're trying to access indices that are null, then trying to access the "id" field, except that that object doesn't exist.
block[x][y] might not have been initialized for all y values... as in genrateDunegeon() method you are initializing block[x][30] for all values of x.
So question is what for other values of y... did you initialized it in your not shown code snippet.
Until you will not initialize, exception will generate.
I think this may work.....(although not sure..)
public void generateDungeon()
{
for(int x = 0; x < block[0].length; x++)
{
for (int y=0; /*your termination condition like y<n */; y++)
if(y == 30)
block[x][y] = new Block(new Rectangle(x * Tile.tileSize, y * Tile.tileSize, Tile.tileSize, Tile.tileSize), Tile.basic);
else
block[x][y] = new Block(null);
}
}
and handle null value in Block constructor.

How can i find out where a BufferedImage has Alpha in Java?

I've got a BuferredImage and a boolean[][] array.
I want to set the array to true where the image is completely transparant.
Something like:
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
alphaArray[x][y] = bufferedImage.getAlpha(x, y) == 0;
}
}
But the getAlpha(x, y) method does not exist, and I did not find anything else I can use.
There is a getRGB(x, y) method, but I'm not sure if it contains the alpha value or how to extract it.
Can anyone help me?
Thank you!
public static boolean isAlpha(BufferedImage image, int x, int y)
{
return image.getRBG(x, y) & 0xFF000000 == 0xFF000000;
}
for(int x = 0; x < width; x++)
{
for(int y = 0; y < height; y++)
{
alphaArray[x][y] = isAlpha(bufferedImage, x, y);
}
}
Try this:
Raster raster = bufferedImage.getAlphaRaster();
if (raster != null) {
int[] alphaPixel = new int[raster.getNumBands()];
for (int x = 0; x < raster.getWidth(); x++) {
for (int y = 0; y < raster.getHeight(); y++) {
raster.getPixel(x, y, alphaPixel);
alphaArray[x][y] = alphaPixel[0] == 0x00;
}
}
}
public boolean isAlpha(BufferedImage image, int x, int y) {
Color pixel = new Color(image.getRGB(x, y), true);
return pixel.getAlpha() > 0; //or "== 255" if you prefer
}

Crop image to smallest size by removing transparent pixels in java

I have a sprite sheet which has each image centered in a 32x32 cell. The actual images are not 32x32, but slightly smaller. What I'd like to do is take a cell and crop the transparent pixels so the image is as small as it can be.
How would I do that in Java (JDK 6)?
Here is an example of how I'm currently breaking up the tile sheet into cells:
BufferedImage tilesheet = ImageIO.read(getClass().getResourceAsStream("/sheet.png");
for (int i = 0; i < 15; i++) {
Image img = tilesheet.getSubimage(i * 32, 0, 32, 32);
// crop here..
}
My current idea was to test each pixel from the center working my way out to see if it is transparent, but I was wondering if there would be a faster/cleaner way of doing this.
There's a trivial solution – to scan every pixel. The algorithm bellow has a constant performance of O(w•h).
private static BufferedImage trimImage(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
int top = height / 2;
int bottom = top;
int left = width / 2 ;
int right = left;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (image.getRGB(x, y) != 0){
top = Math.min(top, y);
bottom = Math.max(bottom, y);
left = Math.min(left, x);
right = Math.max(right, x);
}
}
}
return image.getSubimage(left, top, right - left + 1, bottom - top + 1);
}
But this is much more effective:
private static BufferedImage trimImage(BufferedImage image) {
WritableRaster raster = image.getAlphaRaster();
int width = raster.getWidth();
int height = raster.getHeight();
int left = 0;
int top = 0;
int right = width - 1;
int bottom = height - 1;
int minRight = width - 1;
int minBottom = height - 1;
top:
for (;top <= bottom; top++){
for (int x = 0; x < width; x++){
if (raster.getSample(x, top, 0) != 0){
minRight = x;
minBottom = top;
break top;
}
}
}
left:
for (;left < minRight; left++){
for (int y = height - 1; y > top; y--){
if (raster.getSample(left, y, 0) != 0){
minBottom = y;
break left;
}
}
}
bottom:
for (;bottom > minBottom; bottom--){
for (int x = width - 1; x >= left; x--){
if (raster.getSample(x, bottom, 0) != 0){
minRight = x;
break bottom;
}
}
}
right:
for (;right > minRight; right--){
for (int y = bottom; y >= top; y--){
if (raster.getSample(right, y, 0) != 0){
break right;
}
}
}
return image.getSubimage(left, top, right - left + 1, bottom - top + 1);
}
This algorithm follows the idea from pepan's answer (see above) and is 2 to 4 times more effective. The difference is: it never scans any pixel twice and tries to contract search range on each stage.
The worst case of the method's performance is O(w•h–a•b)
This code works for me. The algorithm is simple, it iterates from left/top/right/bottom of the picture and finds the very first pixel in the column/row which is not transparent. It then remembers the new corner of the trimmed picture and finally it returns the sub image of the original image.
There are things which could be improved.
The algorithm expects, there is the alpha byte in the data. It will fail on an index out of array exception if there is not.
The algorithm expects, there is at least one non-transparent pixel in the picture. It will fail if the picture is completely transparent.
private static BufferedImage trimImage(BufferedImage img) {
final byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
int width = img.getWidth();
int height = img.getHeight();
int x0, y0, x1, y1; // the new corners of the trimmed image
int i, j; // i - horizontal iterator; j - vertical iterator
leftLoop:
for (i = 0; i < width; i++) {
for (j = 0; j < height; j++) {
if (pixels[(j*width+i)*4] != 0) { // alpha is the very first byte and then every fourth one
break leftLoop;
}
}
}
x0 = i;
topLoop:
for (j = 0; j < height; j++) {
for (i = 0; i < width; i++) {
if (pixels[(j*width+i)*4] != 0) {
break topLoop;
}
}
}
y0 = j;
rightLoop:
for (i = width-1; i >= 0; i--) {
for (j = 0; j < height; j++) {
if (pixels[(j*width+i)*4] != 0) {
break rightLoop;
}
}
}
x1 = i+1;
bottomLoop:
for (j = height-1; j >= 0; j--) {
for (i = 0; i < width; i++) {
if (pixels[(j*width+i)*4] != 0) {
break bottomLoop;
}
}
}
y1 = j+1;
return img.getSubimage(x0, y0, x1-x0, y1-y0);
}
I think this is exactly what you should do, loop through the array of pixels, check for alpha and then discard. Although when you for example would have a star shape it will not resize the image to be smaller be aware of this.
A simple fix for code above. I used the median for RGB and fixed the min() function of x and y:
private static BufferedImage trim(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();
int top = height / 2;
int bottom = top;
int left = width / 2 ;
int right = left;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (isFg(img.getRGB(x, y))){
top = Math.min(top, y);
bottom = Math.max(bottom, y);
left = Math.min(left, x);
right = Math.max(right, x);
}
}
}
return img.getSubimage(left, top, right - left, bottom - top);
}
private static boolean isFg(int v) {
Color c = new Color(v);
return(isColor((c.getRed() + c.getGreen() + c.getBlue())/2));
}
private static boolean isColor(int c) {
return c > 0 && c < 255;
}
[Hi I tried the following. In the images file idle1.png is the image with a big transparent box while testing.png is the same image with minimum bounding box
'BufferedImage tempImg = (ImageIO.read(new File(fileNPath)));
WritableRaster tempRaster = tempImg.getAlphaRaster();
int x1 = getX1(tempRaster);
int y1 = getY1(tempRaster);
int x2 = getX2(tempRaster);
int y2 = getY2(tempRaster);
System.out.println("x1:"+x1+" y1:"+y1+" x2:"+x2+" y2:"+y2);
BufferedImage temp = tempImg.getSubimage(x1, y1, x2 - x1, y2 - y1);
//for idle1.png
String filePath = fileChooser.getCurrentDirectory() + "\\"+"testing.png";
System.out.println("filePath:"+filePath);
ImageIO.write(temp,"png",new File(filePath));
where the get functions are
public int getY1(WritableRaster raster) {
//top of character
for (int y = 0; y < raster.getHeight(); y++) {
for (int x = 0; x < raster.getWidth(); x++) {
if (raster.getSample(x, y,0) != 0) {
if(y>0) {
return y - 1;
}else{
return y;
}
}
}
}
return 0;
}
public int getY2(WritableRaster raster) {
//ground plane of character
for (int y = raster.getHeight()-1; y > 0; y--) {
for (int x = 0; x < raster.getWidth(); x++) {
if (raster.getSample(x, y,0) != 0) {
return y + 1;
}
}
}
return 0;
}
public int getX1(WritableRaster raster) {
//left side of character
for (int x = 0; x < raster.getWidth(); x++) {
for (int y = 0; y < raster.getHeight(); y++) {
if (raster.getSample(x, y,0) != 0) {
if(x > 0){
return x - 1;
}else{
return x;
}
}
}
}
return 0;
}
public int getX2(WritableRaster raster) {
//right side of character
for (int x = raster.getWidth()-1; x > 0; x--) {
for (int y = 0; y < raster.getHeight(); y++) {
if (raster.getSample(x, y,0) != 0) {
return x + 1;
}
}
}
return 0;
}'[Look at Idle1.png and the minimum bounding box idle = testing.png][1]
Thank you for your help regards Michael.Look at Idle1.png and the minimum bounding box idle = testing.png]images here
If your sheet already has transparent pixels, the BufferedImage returned by getSubimage() will, too. The default Graphics2D composite rule is AlphaComposite.SRC_OVER, which should suffice for drawImage().
If the sub-images have a distinct background color, use a LookupOp with a four-component LookupTable that sets the alpha component to zero for colors that match the background.
I'd traverse the pixel raster only as a last resort.
Addendum: Extra transparent pixels may interfere with collision detection, etc. Cropping them will require working with a WritableRaster directly. Rather than working from the center out, I'd start with the borders, using a pair of getPixels()/setPixels() methods that can modify a row or column at a time. If a whole row or column has zero alpha, mark it for elimination when you later get a sub-image.

Categories