liu
9 小时以前 514f372270b181f82e4deff98f8d0f38ace63183
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
 
import javax.imageio.ImageIO;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
 
/**
 * 解码失败样本的成因判定
 *
 * 用法:java DiagnoseQrFailures <标签目录> <文件名1> [文件名2 ...]
 *
 * 思路:把图片按整数倍最近邻放大后重新解码。二维码是纯黑白方块,
 * 最近邻放大不引入新信息,只是给解码器的采样网格更多余地。
 * - 放大后能解出 → 二维码数据本身是对的,只是模块密度/采样位置过于临界
 *   (属解码端问题,但印刷尺寸偏小时真实扫码枪也可能读不出,应缩短二维码内容或加大标签)
 * - 放大后仍解不出 → 该标签确有缺陷,需按废品处理
 */
public class DiagnoseQrFailures {
 
    public static void main(String[] args) throws Exception {
        File dir = new File(args[0]);
        for (int i = 1; i < args.length; i++) {
            File f = new File(dir, args[i]);
            if (!f.isFile()) {
                System.out.println(args[i] + " -> 文件不存在");
                continue;
            }
            BufferedImage img = ImageIO.read(f);
            String at1 = decode(img);
            System.out.println("\n=== " + args[i] + " ===");
            System.out.println("  原始尺寸        : " + img.getWidth() + " x " + img.getHeight());
            System.out.println("  1x 解码         : " + (at1 == null ? "失败" : "成功 -> " + at1));
            if (at1 != null) {
                continue;
            }
            boolean rescued = false;
            for (int scale : List.of(2, 3, 4, 6)) {
                BufferedImage up = scaleNearest(img, scale);
                String text = decode(up);
                System.out.println("  " + scale + "x 放大解码     : " + (text == null ? "失败" : "成功 -> " + text));
                if (text != null) {
                    rescued = true;
                    break;
                }
            }
            System.out.println("  结论            : " + (rescued
                    ? "二维码数据有效,属模块密度临界(解码端),非废码"
                    : "放大后仍不可识读,标签确有缺陷"));
        }
    }
 
    /** 整数倍最近邻放大:不插值、不引入新像素值,只放大采样空间 */
    private static BufferedImage scaleNearest(BufferedImage src, int scale) {
        BufferedImage out = new BufferedImage(
                src.getWidth() * scale, src.getHeight() * scale, BufferedImage.TYPE_INT_RGB);
        var g = out.createGraphics();
        try {
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
            g.drawImage(src, 0, 0, out.getWidth(), out.getHeight(), null);
        } finally {
            g.dispose();
        }
        return out;
    }
 
    private static String decode(BufferedImage image) {
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
        hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
        hints.put(DecodeHintType.POSSIBLE_FORMATS, List.of(BarcodeFormat.QR_CODE));
        try {
            Result r = new MultiFormatReader().decode(
                    new BinaryBitmap(new HybridBinarizer(source)), hints);
            return r == null ? null : r.getText();
        } catch (Exception e) {
            return null;
        }
    }
}