之前集成的zxing二维码扫描模型,正常的二维码都可以识别出来,但是识别速度不怎么快,对于特殊的二维码,比如有磨损、光线不足的二维码就扫描不出来了。后来测试发现用浏览器、微信都可以扫描到。于是在github、码云上找了好几个二维码扫描的库,但是运行起来后发现都不能识别出来。最后发现一个基于OpenCV开源的微信二维码引擎移植封装的二维码识别库,装上apk试着扫描了一下,发现可以识别,内心无比激动,真的很感谢博主可以把源码开源出来。
这里我附几个二维码,大家感兴趣的话可以试试扫描效果:
![](https://img-blog.csdnimg.cn/direct/4fd828aa1cf545e2bfdd32a679e4187b.jpeg)
![](https://img-blog.csdnimg.cn/direct/11fc832a56a04df38e9a29e05e4fd199.jpeg)
![](https://img-blog.csdnimg.cn/direct/8b8098f81d8a4471b2bb53bbf3cb08f9.jpeg)
导入mudule
引用module依赖
implementation project(path: ':opencv')
implementation project(path: ':opencv-armv7a')
implementation project(path: ':wechat-qrcode')
implementation project(path: ':wechat-qrcode-scanning')
java版本
java11或者java17
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
ABI过滤
defaultConfig {
//...
ndk {
//设置支持的 SO 库架构(开发者可以根据需要,选择一个或多个平台的 so)
abiFilters 'armeabi-v7a' // , 'arm64-v8a', 'x86', 'x86_64'
}
}
初始化sdk
// 初始化OpenCV
OpenCV.initOpenCV();
// 初始化WeChatQRCodeDetector
WeChatQRCodeDetector.init(this);
WeChatCameraScanActivity
public class WeChatCameraScanActivity extends BaseCameraScanActivity<List<String>> {
protected ViewfinderView viewfinderView;
@Override
public void initUI() {
int viewfinderViewId = getViewfinderViewId();
if (viewfinderViewId != View.NO_ID && viewfinderViewId != 0) {
viewfinderView = findViewById(viewfinderViewId);
}
super.initUI();
}
@Nullable
@Override
public Analyzer<List<String>> createAnalyzer() {
return new WeChatScanningAnalyzer();
}
/**
* 布局ID;通过覆写此方法可以自定义布局
*
* @return 布局ID
*/
@Override
public int getLayoutId() {
return com.king.wechat.qrcode.scanning.R.layout.wechat_camera_scan;
}
/**
* {@link #viewfinderView} 的 ID
*
* @return 默认返回{@code R.id.viewfinderView}, 如果不需要扫码框可以返回{@link View#NO_ID}
*/
public int getViewfinderViewId() {
return com.king.wechat.qrcode.scanning.R.id.viewfinderView;
}
@Override
public void onScanResultCallback(@NonNull AnalyzeResult<List<String>> result) {
}
}
MainActivity
public class MainActivity extends WeChatCameraScanActivity {
private static final String TAG = "MainActivityTag";
protected ImageView ivResult;
private Timer delayTimer;
@Override
public void initUI() {
super.initUI();
ivResult = findViewById(R.id.ivResult);
delayTimer = new Timer();
}
@Override
public int getLayoutId() {
return R.layout.activity_main;
}
@Override
public void onScanResultCallback(@NonNull AnalyzeResult<List<String>> result) {
super.onScanResultCallback(result);
// 停止分析
getCameraScan().setAnalyzeImage(false);
Log.d(TAG, result.getResult().toString());
// 当初始化 WeChatScanningAnalyzer 时,如果是需要二维码的位置信息,则可通过 WeChatScanningAnalyzer.QRCodeAnalyzeResult 获取
// 如果需要处理结果二维码的位置信息
if (result instanceof WeChatScanningAnalyzer.QRCodeAnalyzeResult) {
ivResult.setImageBitmap(previewView.getBitmap());
int width = result.getBitmapWidth();
int height = result.getBitmapHeight();
List<Point> points = new ArrayList<>();
List<Mat> matList = ((WeChatScanningAnalyzer.QRCodeAnalyzeResult<List<String>>) result).getPoints();
Bitmap bitmap = result.getBitmap();
BitmapUtils.drawRectOnBitmap(bitmap, (canvas, paint) -> {
if (matList != null) {
for (Mat mat : matList) {
Point point0 = new Point((int) mat.get(0, 0)[0], (int) mat.get(0, 1)[0]);
Point point1 = new Point((int) mat.get(1, 0)[0], (int) mat.get(1, 1)[0]);
Point point2 = new Point((int) mat.get(2, 0)[0], (int) mat.get(2, 1)[0]);
Point point3 = new Point((int) mat.get(3, 0)[0], (int) mat.get(3, 1)[0]);
int centerX = (point0.x + point1.x + point2.x + point3.x) / 4;
int centerY = (point0.y + point1.y + point2.y + point3.y) / 4;
//将实际的结果中心点坐标转换成界面预览的坐标
Point point = PointUtils.transform(
centerX,
centerY,
width,
height,
viewfinderView.getWidth(),
viewfinderView.getHeight()
);
points.add(point);
Path path = new Path();
path.moveTo((float) mat.get(0, 0)[0], (float) mat.get(0, 1)[0]);
path.lineTo((float) mat.get(1, 0)[0], (float) mat.get(1, 1)[0]);
path.lineTo((float) mat.get(2, 0)[0], (float) mat.get(2, 1)[0]);
path.lineTo((float) mat.get(3, 0)[0], (float) mat.get(3, 1)[0]);
path.lineTo((float) mat.get(0, 0)[0], (float) mat.get(0, 1)[0]);
// 将二维码位置在图片上框出来
canvas.drawPath(path, paint);
}
//设置Item点击监听
viewfinderView.setOnItemClickListener(position -> {
String resultOk = result.getResult().get(position);
Toast.makeText(MainActivity.this, resultOk, Toast.LENGTH_SHORT).show();
});
//显示结果点信息
viewfinderView.showResultPoints(points);
if (result.getResult().size() == 1) {
Toast.makeText(MainActivity.this, "ok-one:" + result.getResult().get(0), Toast.LENGTH_SHORT).show();
}
}
});
} else {
Toast.makeText(this, "ok-two:" + result.getResult().get(0), Toast.LENGTH_SHORT).show();
}
delayTimer.schedule(new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
ivResult.setImageResource(0);
viewfinderView.showScanner();
getCameraScan().setAnalyzeImage(true);
}
});
}
}, 2000);
}
@Nullable
@Override
public Analyzer<List<String>> createAnalyzer() {
return new WeChatScanningAnalyzer(true);
}
@Override
public void onBackPressed() {
super.onBackPressed();
if (viewfinderView.isShowPoints()) {// 如果是结果点显示时,用户点击了返回键,则认为是取消选择当前结果,重新开始扫码
ivResult.setImageResource(0);
viewfinderView.showScanner();
getCameraScan().setAnalyzeImage(true);
}
}
}
BitmapUtils
public class BitmapUtils {
private static final String TAG = "BitmapUtils";
public static Bitmap drawRectOnBitmap(Bitmap sourceBitmap, Block block) {
int width = sourceBitmap.getWidth();
int height = sourceBitmap.getHeight();
Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
canvas.drawBitmap(sourceBitmap, 0, 0, null);
Paint paint = new Paint();
paint.setStrokeWidth(6);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
try {
if (block != null) {
block.invoke(canvas, paint);
}
} catch (Exception e) {
Log.w(TAG, e.getMessage());
}
return result;
}
public interface Block {
void invoke(Canvas canvas, Paint paint);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/ivResult"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.king.view.viewfinderview.ViewfinderView
android:id="@+id/viewfinderView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:vvLaserStyle="line"/>
<ImageView
android:id="@+id/ivFlashlight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/camera_scan_flashlight_margin_top"
android:contentDescription="@null"
android:src="@drawable/camera_scan_flashlight_selector" />
</FrameLayout>
自己改的Java调用的Demo: https://gitee.com/baipenggui/we-chat-code-demo