Search code examples
javabarcodezxingdatamatrix

Reading DataMatrix/QR code zxing java


The below data matrix is being read well using Barcode Scanner, zxing mobile app. However, the same is not being read by zxing java library.

I have some image transformation code commented. Even transforming the image, rotation or scaling doesn't help.

Ideally, I would like to perform all possible image pre-processing programatically until decoded.

What is the logic the mobile app using, since am scanning the same image from the computer screen and it is working.

Please find below, the code am using for decoding.

public class BarcodeReader {

    private static Map<DecodeHintType,Object> hintsMap;

    public static void main(String...args){

         BufferedImage before = null;
         hintsMap = new EnumMap<DecodeHintType, Object>(DecodeHintType.class);
         hintsMap.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
         hintsMap.put(DecodeHintType.POSSIBLE_FORMATS, EnumSet.allOf(BarcodeFormat.class));
         //hintsMap.put(DecodeHintType.PURE_BARCODE, Boolean.FALSE);
         try 
         {
             before = ImageIO.read(new File("C:/ocr.jpg"));
             decode(before);
            /* for(int i=1; i < 1000;i++){
                 AffineTransform transform = new AffineTransform();
                 double rad = (double)i/100;
                 double scale = (double)i/100;
                 System.out.println("rad "+scale);
                 //transform.rotate(rad, before.getWidth()/2, before.getHeight()/2);
                 transform.scale(scale, scale);
                 BufferedImage after = new BufferedImage(before.getWidth(), before.getHeight(), BufferedImage.TYPE_INT_ARGB);
                 AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
                 after = op.filter(before, after);
                 decode(after);
             }*/


             //tmpBfrImage = tmpBfrImage.getSubimage(200, 100, 800, 800);
         } 
         catch (IOException tmpIoe) 
         {
             tmpIoe.printStackTrace();
         }


    }

    public static void decode(BufferedImage tmpBfrImage){
        if (tmpBfrImage == null)
            throw new IllegalArgumentException("Could not decode image.");
        LuminanceSource tmpSource = new BufferedImageLuminanceSource(tmpBfrImage);
        BinaryBitmap tmpBitmap = new BinaryBitmap(new HybridBinarizer(tmpSource));
        MultiFormatReader tmpBarcodeReader = new MultiFormatReader();

        Result tmpResult;
        String tmpFinalResult;
        try 
        {
            if (hintsMap != null && ! hintsMap.isEmpty())
                tmpResult = tmpBarcodeReader.decode(tmpBitmap, hintsMap);
            else
                tmpResult = tmpBarcodeReader.decode(tmpBitmap);
            // setting results.
            tmpFinalResult = String.valueOf(tmpResult.getText());
            System.out.println(tmpFinalResult);
            System.exit(0);;
        } 
        catch (Exception tmpExcpt) 
        {
         tmpExcpt.printStackTrace();
        }
    }

}

Sample Barcode


Solution

  • I had problems at multiple levels. I downloaded zxing source from github and debugged it.

    1. The first problem was adding the below line as hints screws up the recognition hintsMap.put(DecodeHintType.PURE_BARCODE, Boolean.FALSE);

      Looking at their source code for DataMatrixReader, there was a line doing this

      if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE))

      So, irrespective of setting PURE_BARCODE true or false, it considers it as true. Ideally hints should not contain the key.

    2. The second problem was with the way the detector for DataMatrix works. Detector detecting L

      The detector was identifying the 'L' by looking at the number of black and white transitions from each vertices. Ideally, the transitions from Top-Left to Bottom-Left and Bottom-Left to Bottom-Right should have 0 transitions.

      However, since the line was drawn closer towards the outer edge of the box, the transitions were not becoming 0. I made changes to move it closer to the center of the Left and Bottom Black Lines. This means moving the vertical red line to the right and the bottom red line a bit upwards. I added a new method Correct Points, that makes the necessary correction. This correction works for me, ideally one should be making the correction a bit more smarter.

      ResultPoint pointA = correctPoints(cornerPoints[0], Vertices.TOPLEFT);
      ResultPoint pointB = correctPoints(cornerPoints[1], Vertices.BOTTOMLEFT);
      ResultPoint pointC = correctPoints(cornerPoints[2], Vertices.TOPRIGHT);
      ResultPoint pointD = correctPoints(cornerPoints[3], Vertices.BOTTOMRIGHT);
      
      ---
      ---
      
      private ResultPoint correctPoints(ResultPoint point, Vertices vertice){
        if(vertice.equals(Vertices.TOPLEFT))
            return new ResultPoint(point.getX()+10, point.getY()+5);
        else if(vertice.equals(Vertices.BOTTOMLEFT)){
            return new ResultPoint(point.getX()+10, point.getY()-5);
        }else if(vertice.equals(Vertices.TOPRIGHT)){
            return new ResultPoint(point.getX(), point.getY()+10);
        }else{
            return new ResultPoint(point.getX()-10, point.getY()-5);
        }
      
      }
      

    After making these changes, data matrix detection was working for images that were as bad as or even poorer than these.