Domanda

I'm trying to make a program to divide the map of an old GBA game into 16x16 tiles, save each tile, and then compare the raw image data of each grabbed tile to another list of saved tiles and automatically determine if it's the same image. So far I've managed to divide the map into 16x16 tiles, automatically save each 16x16 tile into a new image file, and then load all of these image files as BufferedImages into an array.

 public TileSorter()
 {
   for (int a = 0; a < 1269; a++)
   {
     try {
       img[a] = ImageIO.read(new File("img" + a + ".jpg"));
     } catch (IOException e) {}
     System.out.println("img" + a + ".jpg loaded into Array.");
   }
 }

What I want to do now, is analyze the raw data of each tile (a BufferedImage) in one array and determine if it's the same as any of the saved BufferedImage tiles from another array. I've looked around for answers and attempted both the getRGB() method and the getData() raster method:

    img[a].getRGB(0,0,16,16,rawImgData[a],0,0);

    rawImgData[a] = ((DataBufferByte) img[a].getRaster().getDataBuffer()).getData();

The problem is: from what I can see, the byte[] and int[] data that's returned from these methods is not the same for any two exact same pictures. I need a way to convert image data into raw int[] or byte[] data that can be used to compare two exact same pictures. (IE; if two pictures are both just a 16x16 array of black pixels, they should output the same getRGB() or getData() values.) Is there a method that I can use to convert these BufferedImages into primitive image data that can be easily compared to one another?

Any suggestions would be much appreciated, thanks.

È stato utile?

Soluzione

First of all: The JPG format is NOT a lossless compression. That means that for a sequence like

BufferedImage imageA = loadImage("image.jpg");
saveAs("saved.jpg");
BufferedImage imageB = loadImage("saved.jpg");

somehowCompare(imageA, imageB);

the somehowCompare method would most likely find the images to be not equal, because the JPG compression introduced artifacts.

In doubt, you should store the images as PNG, because it is lossless, and for a sequence as the one described above, the images would be considered as "equal" - meaning that they have exactly the same RGB pixel values.

But this is your actual question: How to compare these images?

The approach of obtaining the data buffer would be the most efficient one. But there are still some caveats: Whether or not the image contains a DataBufferInt or a DataBufferByte depends on subtle details. It may depend on the file type (JPG, GIF or PNG), or whether the image contains transparency (if it is a PNG or GIF), or on the compression method.

One option to solve this would be to paint each newly loaded image into one where you know that it contains a DataBufferInt. That means you could use a method like

public static BufferedImage convertToARGB(BufferedImage image)
{
    BufferedImage newImage = new BufferedImage(
        image.getWidth(), image.getHeight(),
        BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = newImage.createGraphics();
    g.drawImage(image, 0, 0, null);
    g.dispose();
    return newImage;
}

and then load your images with

img[a] = convertToARGB(ImageIO.read(new File("img" + a + ".png")));

The resulting images will have a DataBufferInt. From this buffer, you may obtain the data as an int[] array. And in the best case, you can simply compare the arrays of two such images using

DataBufferInt bufferA = (DataBufferInt)imageA.getRaster().getDataBuffer();
DataBufferInt bufferB = (DataBufferInt)imageB.getRaster().getDataBuffer();
int arrayA[] = bufferA.getData();
int arrayB[] = bufferB.getData();
if (Arrays.equal(arrayA, arrayB))
{
    // Images are equal!
}

The alternative would be to simply obtain individual pixels from the images:

// Assuming equal-sized images:
for (int y=0; y<imageA.getHeight(); y++)
{
    for (int x=0; x<imageA.getWidth(); x++)
    {
        int rgbA = imageA.getRGB(x,y);
        int rgbB = imageB.getRGB(x,y);
        if (rgbA != rgbB) 
        { 
            // Images are NOT equal!
        }
    }
}

The advantage with the latter approach could be that you may consider images as "equal" when they have "very similar" (but not perfectly equal) pixel values, by adjusting the if-statement appropriately.

Altri suggerimenti

The first thing that comes to mind is that JPG is a lossy format that shouldn't be used for precision pixel graphics, especially at 16x16 resolutions. You shouldn't expect to get the exact same RGB values that you originally wrote to the JPG when you read it back.

I've successfully used PNG for reference images in unit tests, to compare against generated BufferedImage.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top