Android 接入微信扫码库,实现堪比微信的扫码效果

对于Android的扫码库,我们平时都会使用ZXing或者ZBar来实现。

但是实际情况是,对于一些环境恶劣的情况下,比如 眩光、昏暗、有污渍等情况下,很难被识别。

即使是在普通情况下,扫码的识别速度、识别率依旧不甚满意的。

为了满足业务需要,故我们需要找到更好的扫码库。

微信开源的扫码库,就是其中一个不错的选择。

微信的扫码库,基于开源引擎ZXing,结合计算机视觉和深度学习技术,深度优化改造的高性能扫码库。

拥有 基于CNN的二维码检测和增强,更鲁棒的定位点检测,更详细的特性可以看基于CNN的微信二维码引擎OpenCV开源!

接入微信扫码库

接入微信扫码库,需要接入OpenCV库,接入OpenCV库有以下几种方式

  • 接入OpenCV的Java SDK包,这样可以直接在Java调用OpenCV的大部分方法,这种方法最简单
  • 使用OpenCV sdk 提供的C++头文件与.so动态库、.a静态库,自己封装jni,这样可以100%使用openCV的接口
  • 通过openCV的源码,重新编译成Android sdk库,这样能只编译生成自己需要的功能,文件比较小,缺点是编译环境啥的比较难搞。

关于OpenCV不同接入方式,详情请看 Android 接入 OpenCV库的三种方式

这里,我直接用方法一 + 方法二的形式,首先我们需要下载OpenCV的SDK包,附上下载地址
在这里插入图片描述

这里选择的是OpenCV 4.5.2版本,选择android进行下载

下载完成解压后,我们得到了如下所示的文件
在这里插入图片描述

这里的sdk文件夹,就是我们需要的OpenCV的Java SDK包了。

接着,我们新建一个项目,选择Native C++
在这里插入图片描述

在这里插入图片描述

接着,我们导入这个Java SDK

在这里插入图片描述

接着,在app的gradle中,依赖这个library

implementation project(':sdk')

新建一个WeChatQRCode的扫码类,这里有三个jni接口

  • WeChatQrCode : 初始化
  • detectAndDecode : 发现并解析
  • delete : 销毁
class WeChatQRCode {
    
    val nativeObjAddr: Long

    private constructor(addr: Long) {
        nativeObjAddr = addr
    }

    constructor(
        detector_prototxt_path: String,
        detector_caffe_model_path: String,
        super_resolution_prototxt_path: String,
        super_resolution_caffe_model_path: String
    ) {
        nativeObjAddr = WeChatQRCode(
            detector_prototxt_path,
            detector_caffe_model_path,
            super_resolution_prototxt_path,
            super_resolution_caffe_model_path
        )
    }

    fun detectAndDecode(img: Mat, points: List<Mat>): List<String> {
        val points_mat = Mat()
        val retVal = detectAndDecode(
            nativeObjAddr, img.nativeObj, points_mat.nativeObjAddr
        )
        Converters.Mat_to_vector_Mat(points_mat, points)
        points_mat.release()
        return retVal
    }

    @Throws(Throwable::class)
    protected fun finalize() {
        delete(nativeObjAddr)
    }

    external fun WeChatQRCode(
        detector_prototxt_path: String,
        detector_caffe_model_path: String,
        super_resolution_prototxt_path: String,
        super_resolution_caffe_model_path: String
    ): Long


    external fun detectAndDecode(
        nativeObj: Long,
        img_nativeObj: Long,
        points_mat_nativeObj: Long
    ): List<String>


    external fun delete(nativeObj: Long)

}

在app模块的cpp文件夹中,添加头文件

同时,我们在native-lib.cpp中,实现jni方法

#include <jni.h>
#include <string>
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/wechat_qrcode.hpp>
#include <opencv2/imgcodecs.hpp>
#include <android/log.h>


#define CONSTRUCTOR(ENV, CLS) ENV->GetMethodID(CLS, "<init>", "(I)V")
#define ARRAYLIST(ENV) static_cast<jclass>(ENV->NewGlobalRef(ENV->FindClass("java/util/ArrayList")))
#define LIST_ADD(ENV, LIST) ENV->GetMethodID(LIST, "add", "(Ljava/lang/Object;)Z")

#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "NativeLib", __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , "NativeLib", __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO  , "NativeLib", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN  , "NativeLib", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR  , "NativeLib", __VA_ARGS__)


using namespace std;
using namespace cv;


/// throw java exception
#undef throwJavaException
#define throwJavaException throwJavaException_wechat_qrcode

static void throwJavaException(JNIEnv *env, const std::exception *e, const char *method) {
    std::string what = "unknown exception";
    jclass je = 0;

    if (e) {
        std::string exception_type = "std::exception";

        if (dynamic_cast<const cv::Exception *>(e)) {
            exception_type = "cv::Exception";
            je = env->FindClass("org/opencv/core/CvException");
        }

        what = exception_type + ": " + e->what();
    }

    if (!je) je = env->FindClass("java/lang/Exception");
    env->ThrowNew(je, what.c_str());

    LOGE("%s caught %s", method, what.c_str());
    (void) method;        // avoid "unused" warning
}


void vector_Mat_to_Mat(std::vector<cv::Mat> &v_mat, cv::Mat &mat) {
    int count = (int) v_mat.size();
    mat.create(count, 1, CV_32SC2);
    for (int i = 0; i < count; i++) {
        long long addr = (long long) new Mat(v_mat[i]);
        mat.at<Vec<int, 2> >(i, 0) = Vec<int, 2>(addr >> 32, addr & 0xffffffff);
    }
}


jobject vector_string_to_List(JNIEnv *env, std::vector<std::string> &vs) {

    static jclass juArrayList = ARRAYLIST(env);
    static jmethodID m_create = CONSTRUCTOR(env, juArrayList);
    jmethodID m_add = LIST_ADD(env, juArrayList);

    jobject result = env->NewObject(juArrayList, m_create, vs.size());
    for (std::vector<std::string>::iterator it = vs.begin(); it != vs.end(); ++it) {
        jstring element = env->NewStringUTF((*it).c_str());
        env->CallBooleanMethod(result, m_add, element);
        env->DeleteLocalRef(element);
    }
    return result;
}

extern "C"
JNIEXPORT jlong JNICALL
Java_com_heiko_opencvwxtest_WeChatQRCode_WeChatQRCode(JNIEnv *env, jobject thiz,
                                                      jstring detector_prototxt_path,
                                                      jstring detector_caffe_model_path,
                                                      jstring super_resolution_prototxt_path,
                                                      jstring super_resolution_caffe_model_path) {
    using namespace cv::wechat_qrcode;
    static const char method_name[] = "WeChatQRCode_WeChatQRCode";
    try {
        LOGD("%s", method_name);
        const char *utf_detector_prototxt_path = env->GetStringUTFChars(detector_prototxt_path, 0);
        std::string n_detector_prototxt_path(
                utf_detector_prototxt_path ? utf_detector_prototxt_path : "");
        env->ReleaseStringUTFChars(detector_prototxt_path, utf_detector_prototxt_path);
        const char *utf_detector_caffe_model_path = env->GetStringUTFChars(
                detector_caffe_model_path, 0);
        std::string n_detector_caffe_model_path(
                utf_detector_caffe_model_path ? utf_detector_caffe_model_path : "");
        env->ReleaseStringUTFChars(detector_caffe_model_path, utf_detector_caffe_model_path);
        const char *utf_super_resolution_prototxt_path = env->GetStringUTFChars(
                super_resolution_prototxt_path, 0);
        std::string n_super_resolution_prototxt_path(
                utf_super_resolution_prototxt_path ? utf_super_resolution_prototxt_path : "");
        env->ReleaseStringUTFChars(super_resolution_prototxt_path,
                                   utf_super_resolution_prototxt_path);
        const char *utf_super_resolution_caffe_model_path = env->GetStringUTFChars(
                super_resolution_caffe_model_path, 0);
        std::string n_super_resolution_caffe_model_path(
                utf_super_resolution_caffe_model_path ? utf_super_resolution_caffe_model_path : "");
        env->ReleaseStringUTFChars(super_resolution_caffe_model_path,
                                   utf_super_resolution_caffe_model_path);
        cv::wechat_qrcode::WeChatQRCode *_retval_ = new cv::wechat_qrcode::WeChatQRCode(
                n_detector_prototxt_path, n_detector_caffe_model_path,
                n_super_resolution_prototxt_path, n_super_resolution_caffe_model_path);
        return (jlong) _retval_;
    } catch (const std::exception &e) {
        throwJavaException(env, &e, method_name);
    } catch (...) {
        throwJavaException(env, 0, method_name);
    }
    return 0;
}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_heiko_opencvwxtest_WeChatQRCode_detectAndDecode(JNIEnv *env,
                                                         jobject thiz,
                                                         jlong self,
                                                         jlong img_nativeObj,
                                                         jlong points_mat_nativeObj) {
    using namespace cv::wechat_qrcode;
    static const char method_name[] = "WeChatQRCode_detectAndDecode";
    try {
        LOGD("%s", method_name);
        std::vector<Mat> points;
        Mat &points_mat = *((Mat *) points_mat_nativeObj);
        cv::wechat_qrcode::WeChatQRCode *me = (cv::wechat_qrcode::WeChatQRCode *) self;
        Mat &img = *((Mat *) img_nativeObj);
        std::vector<std::string> _ret_val_vector_ = me->detectAndDecode(img, points);
        vector_Mat_to_Mat(points, points_mat);
        jobject _retval_ = vector_string_to_List(env, _ret_val_vector_);
        return _retval_;
    } catch (const std::exception &e) {
        throwJavaException(env, &e, method_name);
    } catch (...) {
        throwJavaException(env, 0, method_name);
    }
    return 0;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_heiko_opencvwxtest_WeChatQRCode_delete(JNIEnv *env, jobject thiz, jlong self) {
    delete (cv::wechat_qrcode::WeChatQRCode *) self;
}

修改CMakeLists.txt文件

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.10.2)

# Declares and names the project.

project("learningandroidopencv")

# 注意这里的路径,需要和OpenCV sdk的路径对应
set(ocvlibs "${PROJECT_SOURCE_DIR}/../../../../sdk/native/libs")
include_directories(${PROJECT_SOURCE_DIR}/include)

add_library(libopencv_java4 SHARED IMPORTED)
set_target_properties(libopencv_java4 PROPERTIES
        IMPORTED_LOCATION "${ocvlibs}/${ANDROID_ABI}/libopencv_java4.so")

include_directories(
        wechat
        wechat/detector
        wechat/scale
        wechat/zxing
        wechat/zxing/common
        wechat/zxing/common/binarizer
        wechat/zxing/common/reedsolomon
        wechat/zxing/qrcode
        wechat/zxing/qrcode/detector
        wechat/zxing/qrcode/decoder
)

aux_source_directory(wechat W)
aux_source_directory(wechat/detector WD)
aux_source_directory(wechat/scale WS)
aux_source_directory(wechat/zxing WZ)
aux_source_directory(wechat/zxing/common WZC)
aux_source_directory(wechat/zxing/common/binarizer WZCB)
aux_source_directory(wechat/zxing/common/reedsolomon WZCR)
aux_source_directory(wechat/zxing/qrcode WZQ)
aux_source_directory(wechat/zxing/qrcode/decoder WZQD)
aux_source_directory(wechat/zxing/qrcode/detector WZQD2)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        native-lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        native-lib.cpp
        ${W}
        ${WD}
        ${WS}
        ${WZ}
        ${WZC}
        ${WZCB}
        ${WZCR}
        ${WZQ}
        ${WZQD}
        ${WZQD2}
        )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        native-lib

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib} libopencv_java4)

然后,我们需要在Application中初始化native-lib

init {
    //System.loadLibrary("opencv_java4")
    System.loadLibrary("native-lib")
}

接着,我们需要初始化扫码模型

下载模型文件,并导入到res/raw目录下

然后在Application中进行初始化,将模型文件复制到内部存储中

MMKV.initialize(this)
GlobalScope.launch(Dispatchers.IO) {
    MMKV.defaultMMKV()?.encode(
        MMKVKey.WeChatQRCodeDetectProtoTxt,
        copyFromAssets(R.raw.detect_prototxt, "wechat_qrcode", "detect.prototxt")
    )
    MMKV.defaultMMKV()?.encode(
        MMKVKey.WeChatQRCodeDetectCaffeModel,
        copyFromAssets(R.raw.detect_caffemodel, "wechat_qrcode", "detect.caffemodel")
    )
    MMKV.defaultMMKV()?.encode(
        MMKVKey.WeChatQRCodeSrProtoTxt,
        copyFromAssets(R.raw.sr_prototxt, "wechat_qrcode", "sr.prototxt")
    )
    MMKV.defaultMMKV()?.encode(
        MMKVKey.WeChatQRCodeSrCaffeModel,
        copyFromAssets(R.raw.sr_caffemodel, "wechat_qrcode", "sr.caffemodel")
    )
}

fun Context.copyFromAssets(@RawRes resId: Int, targetDir:String, targetFileName:String): String {
    val targetDirFile = getDir(targetDir, Context.MODE_PRIVATE)
    val targetFile = File(targetDirFile, targetFileName)
    targetFile.outputStream().use {
        resources.openRawResource(resId).copyTo(it)
    }
    return targetFile.absolutePath
}

在初始化WeChatQrCode的时候,将模型路径传入

mWeChatQRCode = WeChatQRCode(
    MMKV.defaultMMKV()?.decodeString(MMKVKey.WeChatQRCodeDetectProtoTxt) ?: "",
    MMKV.defaultMMKV()?.decodeString(MMKVKey.WeChatQRCodeDetectCaffeModel) ?: "",
    MMKV.defaultMMKV()?.decodeString(MMKVKey.WeChatQRCodeSrProtoTxt) ?: "",
    MMKV.defaultMMKV()?.decodeString(MMKVKey.WeChatQRCodeSrCaffeModel) ?: ""
)

然后,我们只需要将摄像头中的图像,传递给微信扫码库进行解析即可

override fun analyze(image: ImageProxy) {
    val rectangles = ArrayList<Mat>()
    //进行解析并识别,并获得解析结果results
    val results : List<String> = weChatQRCode.detectAndDecode(gray(image), rectangles)
    Toast.makeText(this, results.toString(), Toast.LENGTH_SHORT).show()
    image.close()
}

fun gray(image: ImageProxy): Mat {
    val planeProxy = image.planes
    val width = image.width
    val height = image.height
    val yPlane = planeProxy[0].buffer
    val yPlaneStep = planeProxy[0].rowStride
    return Mat(height, width, CvType.CV_8UC1, yPlane, yPlaneStep.toLong())
}

最后,别忘了申请权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />

ActivityCompat.requestPermissions(
        this,
        arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE),
        1
)

完工 !

附上源码地址 https://download.csdn.net/download/EthanCo/20618652

不过接入微信扫码库还是有一定门槛的,如果想要更快速的接入扫码库,可以使用华为的扫码库,某些扫码场景下,比微信扫码还要强 ! 详见我的另一篇博客 Android 接入华为扫码库,实现堪比微信扫码的效果

感谢
https://blog.csdn.net/qq_35054151/article/details/113488372
https://mp.weixin.qq.com/s/GBXkRuS_fPsxr0uT5tR0Sw

  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
### 回答1: Android 微信扫码是一种应用程序接口(API)工具包,可以让 Android 应用快速集成微信扫码识别功能。通过该,应用程序可以轻松实现以下功能: 1. 实现二维码和条码的扫描,并将识别的数据返回给应用程序。 2. 支持多种扫码模式,包括持续扫码、单次扫码、连续扫码等。 3. 提供灵活的配置选项,可以根据应用程序的需求进行自定义设置,例如启动速度、识别准确度等。 4. 提供了丰富的回调函数,可以让应用程序在扫码过程中进行一些逻辑处理,例如弹出提示框、跳转页面等。 5. 集成方便,仅需在项目中添加少量代码即可实现微信扫码功能。 总的来说,Android 微信扫码是一个非常方便的工具,可以帮助开发者快速实现扫码功能,提升应用程序的用户体验,同时也可以降低开发的难度和成本。 ### 回答2: Android 微信扫码是一款在 Android 平台上使用的二维码识别。 作为微信官方提供的扫码,它可以轻松地与微信打通,使用微信的账号登录、分享与支付等功能。同时也可以识别常见的二维码和条码格式。 它的使用非常简单,只需要在你的 Android App 中添加相应的依赖和权限配置,就可以快速地完成二维码的扫描功能了。 当然,它也支持自定义扫码界面和扫码结果的处理方式,可以满足不同的业务需求。 总之,Android 微信扫码是一款优秀的二维码扫描,简单易用且功能强大,值得开发者们去尝试使用。 ### 回答3: Android 微信扫码是一个便于 Android 应用程序集成的工具,其主要功能是使用微信支付SDK所提供的功能,实现二维码的扫描、解码、处理及生成等功能。 Android 微信扫码内部的实现使用了Camera、SurfaceView、Core、QRCodeDecoder、ImageCode、VideoCode 等技术,综合应用了扫码、图像识别和视频处理技术,支持各种标准的二维码、条形码扫描和处理。同时,Android 微信扫码还支持自定义二维码的生成,提供了多种生成二维码的接口和方法,使得应用程序可以满足不同的业务需求,使用起来十分灵活简便。 此外,Android 微信扫码通过微信支付SDK提供的支付功能,可以对扫描的二维码进行支付,实现安全、便捷的支付功能。同时,Android 微信扫码还支持微信扫一扫的功能,将扫码微信的社交功能相结合,可以方便地实现各种业务场景的交互和沟通,扩展应用程序的功能。 总之,Android 微信扫码是一个实现二维码扫描、处理和生成的工具,其功能强大、接口简单、使用便捷,是应用程序开发中不可或缺的一个工具。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

氦客

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值