HƯỚNG DẪN Image Comparison in JAVA

Joe

Thành viên VIP
21/1/13
3,075
1,342
113
JavaDude.gif

This tutorial is a summary of the two last tutorials
  1. OCR: Optical Character Recognition
  2. JAVA: Image and Pixel Processing
With the knowledge we have about the way how to process an Image and how to recognize an Image I show you how to compare the contents of two different images. Image Comparison is very tricky. First, the different physical size. Then, the logical differences which are the hue of colors, the blurring pixels, the similarity, etc.

A File-Comparison focuses on the logical content of the files without checking for the art of characters (i.e. the fonts) whether the letters are cursive or bold or colored. The only little problem is the watch-out for Upper and Lower cases.

An Image-Comparison is, as mentioned above, more complex than File-Comparison because it focuses on the contents which are in form of colors, appearances and the viewing perspective of the images. Further, there is no any logical connection between the images. Example: two images have different size, but both show the same content appearance (same picture). Such a difference does not exist on file: either the contents of the files are the same, or they are different.

I have showed you how to analyze an image and how to parse the pixels into three basic colors RED, GREEN and BLUE (RGB) and the control attribute ALPHA (for the rate of Opaqueness). Together they build an ARGB pixel. Today I show you how to use the mentioned knowledge in order to implement an API for Image Comparison. It's relatively simple. The comparing images can be EQUAL or SIMILAR.
  • Equal is here not the equalness of the physical size, but the IDENTICAL appearances. Meaning: all pixels of both images have the same ARGB.
  • Similar is here the similar appearance of the comparing image contents. The similarity could be 100% (IDENTICAL or EQUAL) or 0% (fully different)
If two images show the same appearance, but different physical sizes the larger image should be reduced to the size of the smaller. An enlargement is disadvantageous because the appearance of the smaller has to be inflated and that could falsify the comparison more than a compression.

The API ImageCompare.java. NOTE: this API is NOT thread-safe.
Java:
import java.io.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
// Joe Nartca (C)
public class ImageCompare {
  /**
  @param tolerant float, the tolerant color between 2 pixels (max 0.1f or 10%).
  */
  public ImageCompare(float tolerant) {
    bTol = (int)(((tolerant > 0.1f || tolerant < 0f) ? 0.1f:tolerant)*256);
    rTol = bTol << 16;
    gTol = bTol << 8;
  }
  /**
  Default tolerant: 2% or 0.02f
  */
  public ImageCompare( ) {
    bTol = 5;
    gTol = bTol << 8;
    rTol = bTol << 16;
  }
  //
  public boolean isEqual() {
    if (result == null || ratio < 0.999f) return false;
    return true;
  }
  /**
  @return float gives the percent of similarity
  */
  public float isSimilar( ) {
    return ratio;
  }
  /**
  @return BufferedImage
  */
  public BufferedImage getFirstImage( ) {
    return img_1;
  }
  /**
  @return BufferedImage
  */
  public BufferedImage getSecondImage( ) {
    return img_2;
  }
  /**
  Compare 2 images (file) for Dissimilariry
  @param imgFile_1 String, Image FileName 1
  @param imgFile_2 String, Image FileName 2
  @return float gives the percent of similarity
  @Exception Exception thrown by JAVA
  */
  public float onCompare(String imgFile_1, String  imgFile_2) throws Exception {
    BufferedImage aImg, bImg;
    img_1 = ImageIO.read(new File(imgFile_1));
    img_2 = ImageIO.read(new File(imgFile_2));
    int w = img_1.getWidth(), h = img_1.getHeight();
    int W = img_2.getWidth(), H = img_2.getHeight();
    // adjusting the size for an easy comparison
    if (W > w || H > h) { // resizing to aImg/bImg
      aImg = img_1;
      bImg = resize(img_2, w, h);
    } else if (W < w || H < h) {
      bImg = img_2;
      aImg = resize(img_1, W, H);
    } else {
      aImg = img_1;
      bImg = img_2;
    }
    w = aImg.getWidth(); h = aImg.getHeight();
    float matched = 0f, cont = (float)(w * h);
    //
    result = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    result_1 = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    result_2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    int rgb1, rgb2, red1, green1, blue1, red2, green2, blue2, X, red, green, blue;
    for (int y = 0; y < h; ++y) for (int x = 0; x < w; ++x) {
      rgb1 = aImg.getRGB(x, y); rgb2 = bImg.getRGB(x, y);
      if (rgb1 == rgb2) ++matched;
      else { // +/- tolerant
        red = 0; blue = 0; green = 0;
        red1 = rgb1 & RED; red2 = rgb2 & RED;
        blue1 = rgb1 & BLUE; blue2 = rgb2 & BLUE;
        green1 = rgb1 & GREEN; green2 = rgb2 & GREEN;
        //
        if (red1 != red2) {
          X = red2 > red1? red2 - red1 : red1 - red2;
          if (X > rTol) red = X;
        }
        if (green1 != green2) {
          X = green2 > green1? green2 - green1 : green1 - green2;
          if (X > gTol) green = X;
        }
        if (blue1 != blue2) {
          X = blue2 > blue1? blue2 - blue1 : blue1 - blue2;
          if (X > bTol) blue = X;
        }
        if (red == 0 && green == 0 && blue == 0) ++matched;
        else {
          result.setRGB(x, y, ALPHA | red | green | blue);
          result_1.setRGB(x, y, rgb1);
          result_2.setRGB(x, y, rgb2);
        }
      }
    }
    ratio = matched/cont;
    return ratio;
  }
  //
  public BufferedImage getDissimilarity() {
    return result;
  }
  //
  public BufferedImage getDifferenceOfFirstImage() {
    return result_1;
  }
  //
  public BufferedImage getDifferenceOfSecondImage() {
    return result_2;
  }
  //--------------------------------------------------------------------------------
  private BufferedImage resize(BufferedImage image, int width, int height) {
    try {
      BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
      ((Graphics2D)img.createGraphics()).drawImage(image, 0, 0, width, height, null);
      return img;
    } catch (Exception ex) { }
    return image;
  }
  private float ratio = 0f;
  private BufferedImage img_1, img_2;
  private BufferedImage result_1, result_2, result;
  private int rTol, gTol, bTol, ALPHA = 0xFF000000, RED = 0xFF0000, GREEN = 0xFF00, BLUE = 0xFF;
}
And the Verification app TestIC.java
Java:
import java.io.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
import javax.swing.*;
//
import joeapp.color.ImageCompare;
// Joe Nartca (C)
public class TestIC extends JFrame {
  public TestIC(String... a) throws Exception {
    setTitle("Image Comparison");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JLabel lab1 = new JLabel("First Image");
    JLabel msg = new JLabel("Image Comparison");
    String dir = System.getProperty("user.dir");
    JTextField txt1 = new JTextField(a.length == 2?a[0]:dir+"\\images\\Bitcoin.png", 24);
    JLabel lab2 = new JLabel("Second Image");
    JTextField txt2 = new JTextField(a.length == 2?a[1]:dir+"\\images\\FataMorgana.png", 24);
    JComboBox<String> cBox = new JComboBox<>(new String[] { "Compare", "isEqual", "isSimilar", "Diff.Of 1st Image",
                                                            "Diff.Of 2nd Image", "The Dissimilarity", "1st Image", "2nd Image"
                                                          }
                                             );
    JButton but = new JButton("No Image");
    but.addActionListener(e -> {
      cBox.grabFocus();
    });
    final ImageCompare ic = new ImageCompare(0.1f);
    JLabel labc = new JLabel("What Function?");
    cBox.addActionListener(e -> {
      try {
        String f1 = txt1.getText();
        String f2 = txt2.getText();
        if (f1 == null || f1.length() == 0 || f2 == null || f2.length() == 0) {
          msg.setText("Require 2 Image files");
          return;
        }
        f1 = f1.replace("/", File.separator);
        f2 = f2.replace("/", File.separator);
        boolean b = false;
        but.setText(null); but.setIcon(null);
        int i1 = f1.lastIndexOf(File.separator);
        int i2 = f1.lastIndexOf(File.separator);
        i1 = i1 < 0? 0:i1+1; i2 = i2 < 0? 0:i2+1;
        BufferedImage img = ic.getDissimilarity();
        //
        String n1 = f1.substring(i1);
        String n2 = f2.substring(i2);
        switch (cBox.getSelectedIndex()) {
        case 0: ratio = ic.onCompare(f1, f2);
                msg.setText("The Similarity Ratio:"+String.format("%3d %%", (int)(ratio*100)));
                but.setText(String.format("%3d %%", (int)(ratio*100))+" similar");
                break;
        case 1: if (img != null) {
                  b = ic.isEqual();
                  msg.setText(n1+" and "+n2+" is "+(b?"Equal":"NOT Equal"));
                  but.setText((b?"Equal":"NOT Equal"));
                } else msg.setText("MUST be compared before using isEqual()");
                break;
        case 2: if (img != null) {
                  ratio = ic.isSimilar( );
                  msg.setText(n1+" and "+n2+" share "+String.format("%3d %%", (int)(ratio*100))+" Simiarity");
                  but.setText(String.format("%3d %%", (int)(ratio*100))+"Similarity");
                } else msg.setText("MUST be compared before using isSimilar()");
                break;
        case 3: if (img != null) {
                  msg.setText("The Dissimilarities of 1st.Image");
                  but.setIcon(new ImageIcon(ic.getDifferenceOfFirstImage()));
                } else msg.setText("MUST be compared before using this function");
                break;
        case 4: if (img != null) {
                  msg.setText("The Dissimilarities of 2nd.Image");
                  but.setIcon(new ImageIcon(ic.getDifferenceOfSecondImage()));
                } else msg.setText("MUST be compared before using this function");
                break;
        case 5: if (img != null) {
                  msg.setText("The Dissimilarity between two Images");
                  but.setIcon(new ImageIcon(img));
                } else msg.setText("MUST be compared before using this function");
                break;
        case 6: if (img != null) {
                  msg.setText("The First Image");
                  but.setIcon(new ImageIcon(ic.getFirstImage()));
                } else msg.setText("MUST be compared before using this function");
                break;
        default:if (img != null) {
                  msg.setText("The Second Image");
                  but.setIcon(new ImageIcon(ic.getSecondImage()));
                } else msg.setText("MUST be compared before using this function");
                break;
        }
        pack();
      } catch (Exception ex) {
        msg.setText("Exception:"+ex.toString());
      }
    });
    msg.setHorizontalAlignment(JLabel.CENTER);
    JPanel north = new JPanel();
    north.add(lab1); north.add(txt1); north.add(lab2); north.add(txt2);
    JPanel center = new JPanel();
    center.add(labc); center.add(cBox);
    JPanel south = new JPanel();
    south.add(msg); south.add(but);

    add("North", north);
    add("Center", center);
    add("South", south);
    pack();
    setVisible(true);
  }
  //
  public static void main(String... a) throws Exception {
    UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
    new TestIC(a);
  }
  private float ratio = 0f;
}
TestIC_1.png

TestIC_3.png

TestIC_5.png

TestIC_6.png

TestIC_7.png

and the comparing Image files
  • Bitcoin.png / FataMorgana.png
  • BigOnion / Small_BigOnion.png
for downloading.


Joe
 

Attachments

Sửa lần cuối:

Joe

Thành viên VIP
21/1/13
3,075
1,342
113
This API ImageCompare allows you to compare the image contents of different Compressing Technologies: PNG, JPEG, etc. The following Screenshots show you the comparison of a bigger JPG Image (JPG_BigOnion.jpg) to a smaller PNG Image (Small_BigOnion.png).
TestIC_8.png

TestIC_9.png

and the JPG_BigOnion.jpg for downloading

PS. ocr.jar is the package that contains the following APIs:
  • ImageTools - the Imaging tools such as filterColor, extract, focus, etc.
  • ImageCompare - see the Source above
  • JIOCR - the frame of OCR
  • OCR - the interface
  • DynamicOCR - the dynamic OCR
  • StaticOCR - the static OCR
How to use? include ocr.jar in your global CLASSPATH and import joeapp.color.*
 

Attachments

Sửa lần cuối: