opencv本身有二维码识别功能,但是识别效果不是很好,它对二维码图片要求较高,虽然可以有图片处理,甚至抠出二维码区域,但是整体识别与微信二维码识别功能还是有差距的。

    微信二维码识别,采用了机器学习算法,加入了CNN模型的概念,它作为三方库,开源给了opencv,而且支持java语言,不过要使用,需要结合操作系统做编译,需要下载opencv源码,还需要下载opencv_contrib代码,这里面就是一些三方库,微信二维码就在这里面。

opencv java 读取二维码 opencv解析二维码_opencv java 读取二维码

    源码编译需要生成opencv_java45x.dll,opencv-java45x.so,opencv_java45x.jar等文件,不仅需要动态库,还需要jar包。

    在windows系统下,动态库是dll类型,在linux下动态库就是so类型。如果你能下载到opencv-4.x版本的jar,dll,so文件,可以直接使用,如果没有,就需要手动编译(编译过程非常复杂,建议不要轻易尝试)。

    有了jar,动态库,就很方便了,无需很复杂的设置,做过java开发的肯定知道jar需要加入类路径下,动态库只需要指定一个位置,java能像读文件一样加载到就可以了。

    这里以windows系统为例,所需的文件就是opencv-453.jar和opencv_java453.dll动态库,如果是maven项目,我们可以将opencv-453.jar作为本地jar加入项目中。

<dependency>
      <groupId>org</groupId>
      <artifactId>opencv</artifactId>
      <version>453</version>
      <scope>system</scope>
      <systemPath>${project.basedir}\src\main\resources\opencvlib\opencv-453.jar</systemPath>
    </dependency>

   java代码如下:

package org.example;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.wechat_qrcode.WeChatQRCode;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.net.URL;
import java.util.List;

public class WeChatQRCodeTool {

    private static volatile  WeChatQRCodeTool instance;
    private static volatile WeChatQRCode detector;

    private WeChatQRCodeTool(){
        System.setProperty("java.awt.headless","true");
        URL url = null;
        String os = System.getProperty("os.name");
        if(os.startsWith("Linux")){
            url = ClassLoader.getSystemResource("opencvlib/libopencv_java452.so");
            System.load(url.getPath());
            detector = new org.opencv.wechat_qrcode.WeChatQRCode();
        }else{
            url = ClassLoader.getSystemResource("opencvlib/opencv_java452.dll");
            System.load(url.getPath());
            ClassLoader cl = WeChatQRCodeTool.class.getClassLoader();
            URL detectprototxt = cl.getResource("opencvlib/detect.prototxt");
            URL detectcaffemodel =cl.getResource("opencvlib/detect.caffemodel");
            URL srprototxt=cl.getResource("opencvlib/sr.prototxt");
            URL srcaffemodel =cl.getResource("opencvlib/sr.caffemodel");
            detector = new org.opencv.wechat_qrcode.WeChatQRCode(
                    detectprototxt.getPath().substring(1),
                    detectcaffemodel.getPath().substring(1),
                    srprototxt.getPath().substring(1),
                    srcaffemodel.getPath().substring(1));
        }
    }

    public static WeChatQRCodeTool getInstance(){
        if(instance==null){
            synchronized (WeChatQRCodeTool.class){
                if(instance==null){
                    instance = new WeChatQRCodeTool();
                }
            }
        }
        return instance;
    }

    public static Mat bufImg2Mat(BufferedImage original, int imgType, int matType) {
        if (original == null) {
            throw new IllegalArgumentException("original == null");
        }
        byte[] pixels = null;
        // Don't convert if it already has correct type
        if (original.getType() != imgType) {

            // Create a buffered image
            BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), imgType);

            // Draw the image onto the new buffer
            Graphics2D g = image.createGraphics();
            try {
                g.setComposite(AlphaComposite.Src);
                g.drawImage(original, 0, 0, null);
                pixels = ((DataBufferByte)image.getRaster().getDataBuffer()).getData();
            } finally {
                g.dispose();
            }
        } else {
            pixels = ((DataBufferByte) original.getRaster().getDataBuffer()).getData();
        }
        Mat mat = Mat.eye(original.getHeight(), original.getWidth(), matType);
        mat.put(0, 0, pixels);
        return mat;
    }

    public String decode(BufferedImage srcImage) {

        int cvtype = CvType.CV_8UC3;
        if(srcImage.getType() == BufferedImage.TYPE_BYTE_GRAY) {
            cvtype = CvType.CV_8UC1;
        }
        Mat image = bufImg2Mat(srcImage, srcImage.getType(), cvtype);
        List<String> result2 = detector.detectAndDecode(image);
        if (result2 != null && result2.size() > 0) {
            return result2.get(0);
        }
        return null;
    }

    public String decodeQRCodeByPath(String qrCodePath){
        String qrCodeText = null;
        try {
            BufferedImage image = ImageIO.read(new File(qrCodePath));
            qrCodeText = decode(image);
        }catch (Exception e){
            e.printStackTrace();
        }
        return qrCodeText;
    }


    public static void main(String[] args) {
        String img = "C:\\Users\\86159\\Pictures\\qr-3.jpg";
        String result = getInstance().decodeQRCodeByPath(img);
        System.out.println(result);
    }
}

    示例图片如下:

    这个二维码图片,在一个大图中,人眼可以看,但是普通的二维码识别基本不可能识别,但是使用微信二维码识别,没有任何问题。识别结果如下所示:

opencv java 读取二维码 opencv解析二维码_opencv_02

    本人尝试过很多二维码图片,使用微信识别,基本一试一个准,果断放弃com.google.zxing提供的二维码识别功能。

    不得不佩服微信二维码识别功能的强大,再回过头来想想,这里面使用了深度学习的内容,比如CNN模型,虽然不懂,感觉已经很厉害了。

    本人在尝试过程中,发现,很多文章提到,Java使用微信二维码识别功能,需要下载模型文件,也就是代码中提到的:detect.prototxt,detect.caffemodel,sr.prototxt,sr.caffemodel文件,其实,不用这几个文件,也是可以的。

URL detectprototxt = cl.getResource("opencvlib/detect.prototxt");
URL detectcaffemodel =cl.getResource("opencvlib/detect.caffemodel");
URL srprototxt=cl.getResource("opencvlib/sr.prototxt");
URL srcaffemodel =cl.getResource("opencvlib/sr.caffemodel");
/*
detector = new org.opencv.wechat_qrcode.WeChatQRCode(
		detectprototxt.getPath().substring(1),
		detectcaffemodel.getPath().substring(1),
		srprototxt.getPath().substring(1),
		srcaffemodel.getPath().substring(1));*/
detector = new org.opencv.wechat_qrcode.WeChatQRCode();

    本文写到这里,其实也差不多了,但是这种识别只能在windows下,java开发,尤其是服务端,最后的代码99%都是部署在linux服务器下的,那么,本文介绍的办法无需改动,但是linux环境要求比较特殊。不仅需要opencv-453.jar,opencv_java453.so 文件,动态库在调用的时候,还需要一些其他的opencv依赖,这么一来,还是需要编译opencv+opencv_contrib。

   我在尝试部署到linux下发现,如果一个机器编译了so文件,可以复制到其他机器使用,其他机器就不需要编译了,但是这些动态库太多了,还是很麻烦的。