报表中含有单号二维码,纸质单据经扫描仪扫描为图片后,使用com.google.zxing解析二维码生成单号供程序使用。在实际使用过程中发现二维码解析时而有失败的情况,对扫描的单据图片进行分析后发现,解析失败得到都是扫描质量稍差者。

为了提高解析的成功率,首先想到的是报表单据打印的质量以及扫描为图片的质量都要好才能保证图片的质量,但实操的过程中受限于使用者的打印机和扫描仪,以及操作人员的水平,只好建议他们尽量使用较好的设备。其次,就是优化代码,看看是否能在代码的层面提高二维码识别率。

从代码的层面提高识别率,想到2个方案,其一对图片进行去除噪点的操作,生成高质量的图片;另外就是考虑到把整个图给Zxing进行识别,此时图片质量稍差就无法识,那么只是截取图片的中的二维码部分给ZXing识别是不是能提高识别率呢?

经实战发现对图片进行降噪,代价太大,在笔者的计算机上一张图降噪处理需要5秒钟左右,对于批量识别的情况不大适合。

所以,只要采取截取图片的中的二维码部分给ZXing识别这个方案试下了。

代码如下,其中 image = image.getSubimage(x,y,width,height);为核心代码。

public static String decode(BufferedImage image,int x,int y,double width,double height) {
        try {
            image = image.getSubimage(x,y,width,height);
            LuminanceSource luminanceSource = new BufferedImageLuminanceSource(image);
            Binarizer binarizer = new HybridBinarizer(luminanceSource);
            BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
            Map<DecodeHintType, Object> hints = new HashMap<>();
            // 解码设置编码方式为:utf-8,
            hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
             //优化精度
            hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
            Result result = new QRCodeReader().decode(binaryBitmap, hints);
            return result.getText();
        } catch (NotFoundException | ChecksumException | FormatException e) {
            log.error("图片中不存在二维码或者解析而二维码失败", e);
            throw new ServiceException("图片中不存在二维码或者解析而二维码失败", e);
        }
    }

经过测试,上面的代码大大提高了二维码的解析成功率。

备注,另外根据测试发现识别的精度还和二维码的大小有关系,如果二维码过大可以通过等比率缩放的方式将二维码缩小,代码如下:

public static BufferedImage getScale(BufferedImage image,double scale) {
        AffineTransformOp transformOp = new AffineTransformOp(AffineTransform.getScaleInstance(scale, scale), null);
        image = transformOp.filter(image, null);
        return image;
    }