由于工作需要,需要对快递单上的手机号码进行快速扫描识别。查询各种OCR识别方式后还是决定采用Android+Tesseract进行识别。
今天开始记录开发过程。首先是开发环境的配置。
我的Android Studio 版本是最新的4.0。
1:环境搭建,新建项目,采用JAVA编程。
项目建成后开始部署OCR识别包。
首先需要下载OCR识别包。但由于该识别包内包含了英文所以对于识别数字来说有点厚重冗余。所以在在网上搜索后得到了一个只包含数字的OCR识别包。资源会上传到资源中。
在APP目录下新建资源包assets 右键在项目下选择NEW 在选择Folder然后选择Assets Folder .将OCR识别文件复制到该资源目录下。存放到该目录下的原因是需要将OCR识别包拷贝到手机的SD卡下。 方便以后进行OCR识别。
OCR识别包部署完成。
2:开始配置OCR的导入。
在配置文件builder.gradle中导入google的识别包。
我使用的是9.1目前是最新版。可以根据需要来下载最新版本。 识别包导入后可以进行正常开发了。
二 开始布局开发及基本的自定义相机应用。
1:在布局之前 先在配置文件 AndroidManifest.xml 中申请 权限。
SD卡的读写权限。
相机的使用权限。
其他权限可以根据应用的情况来进行申请。
判断并申请权限。
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.widget.Toast;
import androidx.core.app.ActivityCompat;
public class permission {
//申请权限
public void permission(Context context, Activity activity) {
//判断权限。如果没有SD卡的写权限,没有相机的权限则开始申请权限。
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//没有权限开始进行申请。
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) &&
ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA)) {
//开始提示信息
Toast.makeText(context, "请先申请权限再进行操作。", Toast.LENGTH_SHORT).show();
} else {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CALL_PHONE},
1);
}
}
}
}
权限申请后开始在主程序中调用该方法进行权限申请。
开始自定义相机开发。
这里 只说相机开发的注意点,以及相机的蒙版开发过程中遇到的坑和解决办法。
1:相机开发。
我这里用的是 android.hardware.Camera 进行的自定义相机的开发。这种架构以及属于淘汰阶段。但也可以正常使用。
相机在预览过程中如果需要得到Bitmap 的话 一定要先进行数据流的转换因为Camera默认的格式是YuvImage格式所以需要在重写预览的事件。也就是 camera.setPreviewCallback(new Camera.PreviewCallback()) 重写这个事件。在此事件中有data参数。该参数是相机预览中的数据流。通过转换可以得到Bitmap从而可以进行图片的裁剪。
2:相机的蒙版 。
相机蒙版开发中由于资料较少 ,所以研究了比较长的时间 。在这里记录一下 可以让后来的人或者自己以后少走弯路。
新建java class 继承 view .
蒙版的建立其实就是 画笔的定义。也就是 层 。
在画布Canvas 固定的情况下 我们可以定义不同的Paint 来进行绘画。从而可以达到蒙版的效果。
首先定义第一个Paint .这个用来绘画蒙版的底层。在这一层中 一定要采用混合图像模式。这样才会形成中间亮四周黑的效果。
也就是:
//设置 抗锯齿
paint.setAntiAlias(true);
//设置 抖动
paint.setDither(true);
//设置混合图像为清除模式。
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
在这里采用清除模式 可以将制定的区域进行清除。
最为重要的是如果想得到这个效果 一定要重写 Canvas的onDraw事件。
定义第二个paint 重要是用来绘制边框。
定义第三个paint 可以用来绘制四角的短线。最终效果如下:
这样也就形成了最终的蒙版。
数字识别
在UI设计完成并可以对生成的Bitmap进行裁剪后 我们可以调用OCR来进行图片的识别。
由于是实时进行图片的识别 。所以最好重新建立一个线程 。在单独的线程中进行操作。
这样就可以实时进行图片的识别 。
//将识别文件拷贝到SD卡中。目前我这里是不管文件在不在都进行拷贝。
private void ocrfilecopy() {
//在SD卡中创建存放识别库的目录。该目录名必须是tessdata 这个文件名是在API使用中规定了的。
//定义tessdata目录在SD 卡的路径。
String Chisim_dir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tessdata";
//定义识别文件的绝对路径和文件名。chi_sim.traineddata 识别文件名。
String orc_filename = Chisim_dir + "/chi_sim.traineddata";
Log.i("path", Chisim_dir);
//检查是否存在此目录。如果存在则不创建,否则则创建目录。
File dirfile = new File(Chisim_dir);
if (dirfile.exists() == false) {
//目录不存在。则创建。
dirfile.mkdir();
Log.i("提示信息", "目录已创建。");
}
//目录创建完成后将OCR识别文件CHI_SIM拷贝到该目录下。
//判断识别文件Chi_sim.traineddata是否存在。
File ocrfile = new File(orc_filename);
//不管识别文件是否存在先删除在创建。
ocrfile.delete();
//开始创建文件。
//首先文件是存放在资源文件夹assets下的。所以要先获取资源;
AssetManager assetManager;
assetManager = getAssets();
//开始读取识别文件并开始复制。输入。
InputStream inputStream;
//开始写入文件。输出。
OutputStream outputStream;
try {
//将识别文件读入到输入流中。
inputStream = assetManager.open("chi_sim.traineddata");
//创建目标文件。
outputStream = new FileOutputStream(orc_filename);
//开始复制,每次复制1024k的字节。
byte[] buff = new byte[1024];
//开始循环写入。
int ll_count;
while ((ll_count = inputStream.read(buff)) != -1) {
outputStream.write(buff, 0, ll_count);
}
//释放资源
outputStream.flush();
outputStream.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//开始识别图片中的数字。
private String disocrnumber(Bitmap bitmap) {
String number = null;
//定义使用的字符集。
String orctype = "chi_sim";
//实例化 ocr API
TessBaseAPI tessbaseapi = new TessBaseAPI();
//开始初始化 指定识别所用的资源位置。在SD卡中的位置。
tessbaseapi.init(Environment.getExternalStorageDirectory().getAbsolutePath(), orctype);
//初始化后开始识别图片。设置需要识别的图片。
tessbaseapi.setImage(bitmap);
//得到识别内容
number = tessbaseapi.getUTF8Text();
Log.i("fasdasdfsdafasd", "number=" + number);
return number;
}