Search code examples
javazxing

Decoding colored image using ZXing library in Java


I am trying to decode a colored QR code in Java with ZXing library. From some research, I know that ZXing already has the ability to do this. But i have been doing this for 2 days and what i have done is trying to read a QR image from a file and calculating the darkest and brightest color in the image. Then change every foreground pixel to black and others to white. After this I will get a QR code just like the standard ones. Then use a function to read the QR code:

However, this only works for QR code with two different colors, if it gets to three colors, then it won't work in the most case. Unless, the new image, which converted from color to gray image, wouldn't exceed the error correction percentage.

And I had tried other methods but each method is working only for a specific kind of QR code (with logo, multi-colored, shaped Finder Patterns, etc.).

And what I am looking for is a way to decode all kinds of QR code. At least for multi-colored and shaped Finder Patterns.

For more detail:

1) This is the code I'm using to decode the Green QR on this page (second QR) and also used to decode the third QR (with a kind of logo thing above the alignment pattern) found on the same website which is definitely not working:

public class Decoder
{
  public static void main(String[] args)
  {
    // input image file
    File imageFile = new File("/Users/User/Desktop/QR/green.png");
    BufferedImage image = null;
    try
    {
      image = ImageIO.read(imageFile);
    }
    catch (IOException e)
    {
      System.out.println("io outch");
    }
    int imageWidth = image.getWidth();
    int imageHeight = image.getHeight();
    int total = 0;
    int dark = image.getRGB(0, 0);
    int light = image.getRGB(0, 0);
    int backgroundColor = 0;
    int foregroundColor = 0;
    FinderPattern topLeftFinder;
    for (int x = 0; x < imageWidth; x ++)
    {
      for (int y = 0; y <imageHeight; y ++)
      {
        total = total + image.getRGB(x, y);
      }
    }
    //int average = total / (imageWidth * imageHeight);
    //System.out.println("total" + total + " average " + average);
    for (int x = 0; x < imageWidth; x ++)
    {
      for (int y = 0; y <imageHeight; y ++)
      {
        if (image.getRGB(x, y) < dark)
        {
          dark = image.getRGB(x, y);
        }
        if (image.getRGB(x, y) > light)
        {
          light = image.getRGB(x, y);
        }
      }
    }
    for (int x = 0; x < imageWidth; x ++)
    {
      for (int y = 0; y <imageHeight; y ++)
      {
        if (image.getRGB(x, y) >= (dark - light) / 4)
        {
          image.setRGB(x, y, -1);
        }
        else if (image.getRGB(x, y) <= (dark - light) * 3 / 4)
        {
          image.setRGB(x, y, -16777216);
        }
        else
        {
          image.setRGB(x, y, -1);
        }
      }
    }
    System.out.println("total" + dark + " average " + light);
    File outputFile = new File("/Users/Desktop/QR/outputQR.png");
    //ImageIO.write(image, "png", file);
    try
    {
      ImageIO.write(image, "png", outputFile);
    }
    catch (IOException e)
    {
      System.out.println(e.getMessage());
    }
    // creating binary bitmap from source image
    LuminanceSource lumSource = new BufferedImageLuminanceSource(image);
    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(lumSource));
    Hashtable<DecodeHintType, Object> hint = new Hashtable<DecodeHintType, Object>();
    hint.put(DecodeHintType.TRY_HARDER, BarcodeFormat.QR_CODE);
    // decoding
    QRCodeReader QRreader = new QRCodeReader();
    Result result = null;
    try
    {
      result = QRreader.decode(bitmap, hint);
    }
    catch (ReaderException e)
    {
      System.out.println("error during reading");
    }
    // getting output text
    String decodedText = result.getText();
    System.out.println(decodedText);
  }
}

2) And this is the code used for decoding this QR which is working fine originally but don't know why it's not working right now. And this code won't decode the QRs mentioned above.

public class Decoder
{
    public static void main(String[] args)
    {
        // input image file
        File imageFile = new File("/Users/User/Desktop/QR/bigfinderQR.png");
        BufferedImage image = null;
        try
        {
            image = ImageIO.read(imageFile);
        }
        catch (IOException e)
        {
            System.out.println("io outch");
        }
        // creating binary bitmap from source image
        LuminanceSource lumSource = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(lumSource));
        Hashtable<DecodeHintType, Object> hint = new Hashtable<DecodeHintType, Object>();
        hint.put(DecodeHintType.TRY_HARDER, BarcodeFormat.QR_CODE);
        // decoding
        QRCodeReader QRreader = new QRCodeReader();
        Result result = null;
        try
        {
            result = QRreader.decode(bitmap, hint);
        }
        catch (ReaderException e)
        {
            System.out.println("error during reading");
        }
        // getting output text
        String decodedText = result.getText();
        System.out.println(decodedText);
    }
}

Solution

  • It just binarizes based on the computed luminance of each pixel. Anything reasonably like dark-on-light should be fine. A thousand colors are fine. If it's light-on-dark (inverted), then you do need to invert the image or modify the code to invert the image. This is almost surely not the issue.

    Distorting finder patterns is a trickier business since it's easier to make it invalid this way. You will need to preserve pretty nearly the 1:1:3:1:1 dark-light-dark-light-dark ratios scanning across the pattern horizontally and vertically. Rounding the corners a bit is fine. You couldn't for example put white space between black modules.

    Can you post the original image you're trying to decode? I could tell you very quickly what's right and wrong.