2

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

Andrews
  • 895
  • 3
  • 15
  • 30

2 Answers2

5

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.

Andrews
  • 895
  • 3
  • 15
  • 30
  • Thanks for sharing this. How bad have you tested for decoding? When comparing with libdmtx with ZXing (coupled with your changes), have you noticed difference in decoding rate and speed? – banerjk Jan 25 '21 at 15:10
2

I was having similar problems using ZXing to decode DataMatrix barcodes. From what I can see, ZXing doesn't traverse the entire image you send it, but rather starts from the middle and expands out until it has found a barcode. So, if the DataMatrix barcode isn't centered in the image, ZXing will not be able to reliably find it. I implemented (a rather slow) workaround that fixes this problem, by creating different cropped versions of the image:

My core decode method is similar to that of the original post. My image traversal logic is as follows:

    // Read the original image
    final BufferedImage image = ImageIO.read(...);
    
    final int width = image.getWidth();
    final int height = image.getHeight();
    
    // Try detect codes using different sections of the image.
    //
    // +------+------+
    // |    ##|##    |
    // |    ##|##    |
    // |    ##|##    |
    // +------+------+
    // |      |      |
    // |      |      |
    // |      |      |
    // +------+------+
    // 
    // We create 9 cropped versions of the image, with each cropped
    // version being 1/4 of the original image. We traverse the
    // original image from left-to-right, top-to-bottom, and create
    // 9 sub-images that we try to decode in turn.
    for (int i=0; i<3; i++) {
        for (int j=0; j<3; j++) {
            final int x = i * width / 4;
            final int y = j * height / 4;
            final BufferedImage crop = image.getSubimage(x, y, width / 2, height / 2);

            decoded(crop);
        }
    }