Scanner项目地址:https://github.com/shouzhong/Scanner 文件下载
识别库,识别器可选择,这里有你常用的二维码/条码识别,还有你可能用到的身份证识别、银行卡识别、车牌识别、图片文字识别、黄图识别、驾驶证识别,如果没有你想要的,可以自定义识别器。该库只识别扫描框内的图像,识别速率上大大提高,而且这个库比起其它的库就是解决了摄像头预览变形,预览页面高度自定义,你可以像常规一样整个页面都是预览,或者你可以选择在任何位置定义任何尺寸的预览,扫描框也高度自定义,你可以像常规一样居中,或者你也可以在预览的任何位置定义任何尺寸的扫描框(实际识别的扫描框和画上去的扫描框不一定是一样的,由你自己决定)。
依赖
bash
implementation 'com.shouzhong:Scanner:1.1.3'
以下选择自己需要的
bash
// zxing
implementation 'com.google.zxing:core:3.3.3'
// zbar
implementation 'com.shouzhong:ScannerZBarLib:1.0.0'
// 银行卡识别
implementation 'com.shouzhong:ScannerBankCardLib:1.0.3'
// 身份证识别
implementation 'com.shouzhong:ScannerIdCardLib:1.0.4'
// 车牌识别
implementation 'com.shouzhong:ScannerLicensePlateLib:1.0.3'
// 图片文字识别
implementation 'com.shouzhong:ScannerTextLib:1.0.0'
// 黄图识别
implementation 'com.shouzhong:ScannerNsfwLib:1.0.0'
// 驾驶证识别
implementation 'com.shouzhong:ScannerDrivingLicenseLib:1.0.1'
// 身份证识别(第二种方式)
implementation 'com.shouzhong:ScannerIdCard2Lib:1.0.0'
基本使用
bash
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.shouzhong.scanner.ScannerView
android:id="@+id/sv"
android:layout_width="match_parent"
android:layout_height="1080px"
android:background="#000000"/>
</RelativeLayout>
bash
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scanner);
scannerView = findViewById(R.id.sv);
scannerView.setViewFinder(new ViewFinder(this));
scannerView.setSaveBmp(true);
scannerView.setEnableZXing(true);
scannerView.setEnableZBar(true);
scannerView.setEnableBankCard(true);
scannerView.setEnableIdCard(true);
scannerView.setEnableLicensePlate(true);
scannerView.setCallback(new Callback() {
@Override
public void result(Result result) {
tvResult.setText("识别结果:\n" + result.toString());
scannerView.restartPreviewAfterDelay(2000);
}
});
}
@Override
protected void onResume() {
super.onResume();
scannerView.onResume();
}
@Override
protected void onPause() {
super.onPause();
scannerView.onPause();
}
开启或者关闭某个识别器
bash
// 启用 zxing 识别器
scannerView.setEnableZXing(true);
// 启用 zbar 识别器
scannerView.setEnableZBar(true);
// 启用银行卡识别器
scannerView.setEnableBankCard(true);
// 启用身份证识别器(这里只支持 2 代身份证)
scannerView.setEnableIdCard(true);
// 启用车牌识别
scannerView.setEnableLicensePlate(true);
// 启用驾驶证识别
scannerView.setEnableDrivingLicense(true);
// 启用身份证识别(第二种方式)
scannerView.setEnableIdCard2(true);
如果你想自定义识别器
bash
scannerView.setScanner(new IScanner() {
/**
* 这里实现自己的识别器,并把识别结果返回
*
* @param data 矩形框内 nv21 图像数据
* @param width 图像宽度
* @param height 图像高度
* @return
* @throws Exception
*/
@Override
public Result scan(byte[] data, int width, int height) throws Exception {
// 如果你想转为 Bitmap,请使用 NV21.nv21ToBitmap(byte[] nv21, int width, int height)
return null;
}
});
这里没给默认的预览页面,需要自己自定义,请参考demo
bash
package com.shouzhong.scanner.demo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.View;
import com.shouzhong.scanner.IViewFinder;
public class TestViewFinder extends View implements IViewFinder {
private Rect framingRect;//扫码框所占区域
private float widthRatio = 0.8f;//扫码框宽度占view总宽度的比例
private float heightWidthRatio = 1f;//扫码框的高宽比
private int leftOffset = -1;//扫码框相对于左边的偏移量,若为负值,则扫码框会水平居中
private int topOffset = -1;//扫码框相对于顶部的偏移量,若为负值,则扫码框会竖直居中
private int laserColor = 0xff008577;// 扫描线颜色
private int maskColor = 0x60000000;// 阴影颜色
private int borderColor = 0xff008577;// 边框颜色
private int borderStrokeWidth = 12;// 边框宽度
private int borderLineLength = 72;// 边框长度
private Paint laserPaint;// 扫描线
private Paint maskPaint;// 阴影遮盖画笔
private Paint borderPaint;// 边框画笔
private int position;
public TestViewFinder(Context context) {
super(context);
setWillNotDraw(false);//需要进行绘制
laserPaint = new Paint();
laserPaint.setColor(laserColor);
laserPaint.setStyle(Paint.Style.FILL);
maskPaint = new Paint();
maskPaint.setColor(maskColor);
borderPaint = new Paint();
borderPaint.setColor(borderColor);
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(borderStrokeWidth);
borderPaint.setAntiAlias(true);
}
@Override
protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld) {
updateFramingRect();
}
@Override
public void onDraw(Canvas canvas) {
if (getFramingRect() == null) {
return;
}
drawViewFinderMask(canvas);
drawViewFinderBorder(canvas);
drawLaser(canvas);
}
private void drawLaser(Canvas canvas) {
Rect framingRect = getFramingRect();
int top = framingRect.top + 10 + position;
canvas.drawRect(framingRect.left + 10, top, framingRect.right - 10, top + 5, laserPaint);
position = framingRect.bottom - framingRect.top - 25 < position ? 0 : position + 2;
//区域刷新
postInvalidateDelayed(20, framingRect.left + 10, framingRect.top + 10, framingRect.right - 10, framingRect.bottom - 10);
}
/**
* 绘制扫码框四周的阴影遮罩
*/
private void drawViewFinderMask(Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
Rect framingRect = getFramingRect();
canvas.drawRect(0, 0, width, framingRect.top, maskPaint);//扫码框顶部阴影
canvas.drawRect(0, framingRect.top, framingRect.left, framingRect.bottom, maskPaint);//扫码框左边阴影
canvas.drawRect(framingRect.right, framingRect.top, width, framingRect.bottom, maskPaint);//扫码框右边阴影
canvas.drawRect(0, framingRect.bottom, width, height, maskPaint);//扫码框底部阴影
}
/**
* 绘制扫码框的边框
*/
private void drawViewFinderBorder(Canvas canvas) {
Rect framingRect = getFramingRect();
// Top-left corner
Path path = new Path();
path.moveTo(framingRect.left, framingRect.top + borderLineLength);
path.lineTo(framingRect.left, framingRect.top);
path.lineTo(framingRect.left + borderLineLength, framingRect.top);
canvas.drawPath(path, borderPaint);
// Top-right corner
path.moveTo(framingRect.right, framingRect.top + borderLineLength);
path.lineTo(framingRect.right, framingRect.top);
path.lineTo(framingRect.right - borderLineLength, framingRect.top);
canvas.drawPath(path, borderPaint);
// Bottom-right corner
path.moveTo(framingRect.right, framingRect.bottom - borderLineLength);
path.lineTo(framingRect.right, framingRect.bottom);
path.lineTo(framingRect.right - borderLineLength, framingRect.bottom);
canvas.drawPath(path, borderPaint);
// Bottom-left corner
path.moveTo(framingRect.left, framingRect.bottom - borderLineLength);
path.lineTo(framingRect.left, framingRect.bottom);
path.lineTo(framingRect.left + borderLineLength, framingRect.bottom);
canvas.drawPath(path, borderPaint);
}
/**
* 设置framingRect的值(扫码框所占的区域)
*/
private synchronized void updateFramingRect() {
Point viewSize = new Point(getWidth(), getHeight());
int width, height;
width = (int) (getWidth() * widthRatio);
height = (int) (heightWidthRatio * width);
int left, top;
if (leftOffset < 0) {
left = (viewSize.x - width) / 2;//水平居中
} else {
left = leftOffset;
}
if (topOffset < 0) {
top = (viewSize.y - height) / 2;//竖直居中
} else {
top = topOffset;
}
framingRect = new Rect(left, top, left + width, top + height);
}
@Override
public Rect getFramingRect() {
return framingRect;
}
}
回调说明
Result
| 属性 | 说明 |
|---|---|
| TYPE_CODE | 类型:二维码/条码 |
| TYPE_ID_CARD_FRONT | 类型:身份证人头面 |
| TYPE_ID_CARD_BACK | 类型:身份证国徽面 |
| TYPE_BANK_CARD | 类型:银行卡 |
| TYPE_LICENSE_PLATE | 类型:车牌 |
| TYPE_DRIVING_LICENSE | 类型:驾驶证 |
| type | 结果类型 |
| path | 保存的图片路径 |
| data | 数据 |
bash
// 以下是对 data 的说明
// 当 type 为 TYPE_CODE,TYPE_BANK_CARD,TYPE_LICENSE_PLATE 时,data 为字符串
// 当 type 为 TYPE_ID_CARD_FRONT 时,data 为 json 字符串,格式如下
{
"cardNumber": "21412412421",// 身份证号
"name": "张三",// 姓名
"sex": "男",// 性别
"nation": "汉",// 民族
"birth": "1999-01-01",// 出生
"address": "地址"// 地址
}
// 当 type 为 TYPE_ID_CARD_BACK 时,data 为 json 字符串,格式如下
{
"organization": "签发机关",// 签发机关
"validPeriod": "20180101-20380101"// 有效期限
}
// 当 type 为 TYPE_DRIVING_LICENSE 时,data 为 json 字符串,格式如下
{
"cardNumber": "43623446432",// 证号
"name": "张三",// 姓名
"sex": "男",// 性别
"nationality": "中国",// 国籍
"address": "地址",// 地址
"birth": "1999-01-01",// 出生日期
"firstIssue": "2018-01-01",// 初次领证日期
"_class": "C1",// 准驾车型
"validPeriod": "20180101-20240101"// 有效期限
}
BankCardInfoBean
| 属性或方法 | 说明 |
|---|---|
| cardNumber | 银行卡号 |
| cardType | 银行卡(英文)类型 |
| bank | 银行(英文)名称 |
| getCNBankName() | 获取银行(中文)名称 |
| getBankId() | 获取银行编号 |
| getCNCardType() | 获取银行卡(中文)类型 |
方法说明
ScannerView
| 方法名 | 说明 |
|---|---|
| setViewFinder() | 扫描区域 |
| setCallback() | 扫码成功后的回调 |
| setCameraDirection() | 摄像头方向,后置为 Camera.CameraInfo.CAMERA_FACING_BACK,前置为 Camera.CameraInfo.CAMERA_FACING_FRONT |
| setEnableZXing() | 是否启用 zxing 识别器,默认 false |
| setEnableZBar() | 是否启用 zbar 识别器,默认 false |
| setEnableQrcode() | 是否启动二维码识别,默认 true,只有在 zxing 或者 zbar 开启时有效 |
| setEnableBarcode() | 是否启动条码识别,默认 true,只有在 zxing 或者 zbar 开启时有效 |
| setEnableBankCard() | 是否启用银行卡识别器,默认 false |
| setEnableIdCard() | 是否启用身份证识别器,默认 false |
| setEnableIdCard2() | 是否启用身份证识别器(第二种方式),默认 false |
| setEnableDrivingLicense() | 是否启用驾驶证识别器,默认 false |
| setEnableLicensePlate() | 是否启用车牌识别器,默认 false |
| setScanner() | 自定义识别器 |
| onResume() | 开启扫描 |
| onPause() | 停止扫描 |
| restartPreviewAfterDelay() | 设置多少毫秒后重启扫描 |
| setFlash() | 开启/关闭闪光灯 |
| toggleFlash() | 切换闪光灯的点亮状态 |
| isFlashOn() | 闪光灯是否被点亮 |
| setShouldAdjustFocusArea() | 设置是否要根据扫码框的位置去调整对焦区域的位置,部分手机不支持,默认 false |
| setSaveBmp() | 设置是否保存识别的图片,默认 false |
| setRotateDegree90Recognition() | 是否在原来识别的图像基础上旋转 90 度继续识别,默认 false |
ScannerUtils
| 方法名 | 说明 |
|---|---|
| decodeCode() | 二维码/条码识别,建议在子线程运行 |
| decodeBank() | 银行卡识别,建议在子线程运行 |
| getBankCardInfo() | 获取银行卡信息,请在子线程运行 |
| decodeIdCard() | 身份证识别,建议在子线程运行 |
| decodeId2Card() | 身份证识别(第二种方式),建议在子线程运行 |
| decodeDrivingLicense() | 驾驶证识别,建议在子线程运行 |
| decodeLicensePlate() | 车牌识别,建议在子线程运行 |
| decodeText() | 图片文字识别,请在子线程运行 |
| decodeNsfw() | 黄图识别,大于 0.3 可以说图片涉黄,建议在子线程运行 |
| createBarcode() | 条码生成,建议在子线程运行 |
| createQRCode() | 二维码生成,建议在子线程运行 |
| addLogo() | 往图片中间加 logo |
NV21
| 方法名 | 说明 |
|---|---|
| nv21ToBitmap() | nv21 转 bitmap |
| bitmapToNv21() | bitmap 转 nv21 |
怎么把我的整个项目导进去
该项目使用 opencv-3.4.6,点击下载
NDK 版本 r16
把 licennseplate 的 CMakeLists.txt 的第 12 行替换成自己的 opencv-android-sdk 的 JNI 路径
删除所有 gradle 里的 apply from: 'bintray.gradle'
删除 bankcard 的 build.gradle 里的 android->externalNativeBuild 以及 android->defaultConfig->ndk 和 externalNativeBuild 标签
删除 text 的 build.gradle 里的 android->externalNativeBuild 以及 android->defaultConfig->ndk 和 externalNativeBuild 标签
如果是 linux 用户,请在 licennseplate 的 build.gradle 添加以下
bash
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
cppFlags "-std=c++11"
// linux 请添加以下
arguments "-DANDROID_TOOLCHAIN=gcc", "-DANDROID_ARM_NEON=TRUE", "-DANDROID_STL_FORCE_FEATURES=OFF"
}
}
}
}
注意事项
so 资源只有 arm 格式的,ScannerDrivingLicenseLib 和 ScannerIdCard2Lib 无 arm64-v8a 格式