0

I have a series of small, 20x20 BufferedImages, each one with a white background and black text on it, each BufferedImage containing a single digit, from 0 - 9.

What I want to do, is to simply compare the images to each other, and determine if they are duplicates (equal) or not. E.g, if I compare two images which both say '2', then I want that to be marked as a duplicate. If one of them says 5, then I want that to be marked as not a duplicate.

I'm thinking of simply looping over all the pixels of the image, getting their RBG values and storing them in a string, then compare the RBG strings of each image to determine if they are duplicates or not. Is that the best method, or is there any better / faster method?

The images are stored in memory and not on the file system, so I can't really md5 them, however if md5-ing them will give accurate results and will be fast, then I can have them stored.

Ali
  • 261,656
  • 265
  • 575
  • 769

4 Answers4

2

For an OCR-based solution, you can use Tesseract (via Tess4J Java wrapper). The process can be as follows:

  1. scale buffered image to 300 DPI
  2. set to recognize digits only (SetVariable("tessedit_char_whitelist", "0123456789"))
  3. set page segmentation modes (PSM) to 10 (for single character)
  4. get recognized text
nguyenq
  • 8,212
  • 1
  • 16
  • 16
1

I'm thinking of simply looping over all the pixels of the image, getting their RBG values and storing them in a string, then compare the RBG strings of each image to determine if they are duplicates or not. Is that the best method, or is there any better / faster method?

Encoding and storing the pixels in a String and comparing Strings is unnecessary. If a the images can be compared pixel by pixel, then compare them as arrays of pixel values. Checksumming or hashing would be a good accelerator, provided that the costs van be amortized. (You can calculate an MD5 or equivalent digest in Java of anything that you can represent as bytes!)

However, the real question is whether simple pixel-wise comparison will even work. There are all sorts of things that can interfere with this. You say:

The images are coming from Robot.createScreenCapture() from different parts of the screen.

That means:

  • the digits could be in different fonts,
  • the digits could be in that same fonts but different styles, or font sizes,
  • the digit images could be misaligned or rotated within the 20x20 images
  • the digit rendering could use anti-aliasing which could be affected by screen resolution, etcetera,
  • there could be extraneous stuff; e.g. if the image clipping is not correct,
  • there could be background stuff; e.g. if it is not "pure" white.

If any one of those happens, then simple pixel-by-pixel comparison won't work, and you may need to do proper OCR ... with all of its cost and complexity.

Recommendations for Java OCR software are off-topic. Google is your friend.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • thanks for the advice. I do know that the images will be rendered in the same font, style, size, and background. However, the user is going to drag his mouse around the numbers to select the areas which will be captured via `Robot.createScreenCapture`, so it -is- possible that a few pixels might be off. What would you suggest in this case? just ignoring 3-4 pixels if they're different? – Ali Sep 15 '13 at 02:28
  • Well, it seems that even a 1-2 pixel difference causes the rbg of the entire image to move off.. – Ali Sep 15 '13 at 02:47
  • RBG is working fine for me now (previously when I shifted it one pixel, I was introducing a lot more colors which changed the whole composition of the image). Since the images have just two colors, I calculate the background color and ignore the pixels with that color. Then, I only compare those pixels which are in the foreground. Its very accurate so far! – Ali Sep 15 '13 at 03:22
0

You're best off creating a Single BufferedImage for each number (or ImageIcon, if this is what is needed), and then comparing for equality, ==. Otherwise you may need to do OCR to try to find a match that is close enough.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Its not possible to just do object equality. The images are coming from `Robot.createScreenCapture()` from different parts of the screen. I'd like to get some pointers on how to do the OCR, please. – Ali Sep 15 '13 at 01:08
  • @ClickUpvote: I have not yet delved into the OCR waters, and so cannot give you specific advice. – Hovercraft Full Of Eels Sep 15 '13 at 01:10
  • do you know if its possible to loop through the pixels of the image and get the rbg value of each pixel as i thought? – Ali Sep 15 '13 at 01:11
  • @ClickUpvote: sure you can loop through the data held by the raster, but to what effect? If you can't extract the information from the image, you're stuck. – Hovercraft Full Of Eels Sep 15 '13 at 01:13
  • Is it not possible to extract the rbg or any other info? – Ali Sep 15 '13 at 01:18
0

I got my answer from this question

import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import javax.imageio.ImageIO;

protected boolean areEqual(BufferedImage img1, BufferedImage img2)
{
    String str1 = imgToStr(img1);
    String str2 = imgToStr(img2);        
    return str1.equals(str2);
}

protected String imgToStr(BufferedImage img)
{
    ByteArrayOutputStream os = new ByteArrayOutputStream();        
    try
    {
        OutputStream b64 = new Base64.OutputStream(os);
         ImageIO.write(img, "png", b64);
        String result = os.toString("UTF-8");   
        return result;
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }        
    return "";

}

Base64 class downloaded from here

Community
  • 1
  • 1
Ali
  • 261,656
  • 265
  • 575
  • 769
  • While this works if every pixel is exactly identical, I needed something less sensitive, so I've gone with the RGB solution as I described above. – Ali Sep 15 '13 at 03:31