項(xiàng)目地址
https://github.com/979451341/OrcTest
創(chuàng)新互聯(lián)公司主要從事做網(wǎng)站、成都網(wǎng)站制作、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)磐石,十載網(wǎng)站建設(shè)經(jīng)驗(yàn),價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):028-86922220
我們說說實(shí)現(xiàn)這個項(xiàng)目已實(shí)現(xiàn)的功能,能夠截圖手機(jī)界面的某一塊,將這個某一塊圖片的Bitmap傳給tess-two的代碼來獲取掃描結(jié)果
我這里在貼出tess-two這個專為Android而創(chuàng)建的文字識別框架的地址
https://github.com/rmtheis/tess-two
接下來我就說我如何一步一步的實(shí)現(xiàn)項(xiàng)目
1.實(shí)現(xiàn)基礎(chǔ)界面,我這里貼出已完成的界面
這樣是為了模仿掃描二維碼的界面,因?yàn)閽呙琛痢痢撂柎a或者是手機(jī)號那樣長條的數(shù)字,就將掃描區(qū)域也做成長條狀,這個掃描區(qū)域是有意義的,因?yàn)榈綍r候截圖會只將掃描區(qū)域里的圖片信息拿去掃描,這也是為了提高掃描速度和精度。
首先要實(shí)現(xiàn)這個界面,我們需要畫出四個灰色長方體的位置大小,上下左右。
left是掃描區(qū)域左邊離手機(jī)屏幕左邊的距離是手機(jī)屏幕寬度的1/10,right就是掃描區(qū)域右邊離手機(jī)屏幕左邊的距離是手機(jī)屏幕寬度的9/10,top是掃描區(qū)域頂部離手機(jī)屏幕頂部的距離是手機(jī)屏幕寬度的1/3,bottom是掃描區(qū)域底部離手機(jī)屏幕頂部的距離是手機(jī)屏幕寬度的4/9
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
PMwidth = display.getWidth();
PMheight = display.getHeight();
left = PMwidth/10;
top = PMheight/3;
right = PMwidth*9/10;
bottom = PMheight*4/9;
mFrameRect = new Rect(left,top,right,bottom);
畫畫
@Override
public void onDraw(Canvas canvas) {
int width = PMwidth;
int height = PMheight;
Rect frame = mFrameRect;
// 繪制焦點(diǎn)框外邊的暗色背景
mPaint.setColor(mMaskColor);
canvas.drawRect(0, 0, width, frame.top, mPaint);
canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, mPaint);
canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, mPaint);
canvas.drawRect(0, frame.bottom + 1, width, height, mPaint);
}
還沒有完,還有布局文件放SurfaceView和按鈕,還有剛才做的自定義View
2.顯示Camera預(yù)覽和Camera拍攝
這里SurfaceView如何顯示Camera我不多說,只說如何把Camera預(yù)覽變清晰,這里是通過循環(huán)自動對焦來完成。
設(shè)置自動對焦接口
mCamera.autoFocus(autoFocusCallback);
這個接口初始化傳入了Handler
autoFocusCallback.setHandler(handler,MSG_AUTOFUCS);
然后這個接口實(shí)現(xiàn)類里,當(dāng)完成自動對焦,會通過handler發(fā)送一個消息
@Override
public void onAutoFocus(boolean success, Camera camera) {
Log.v("zzw", "autof focus "+success);
if (mAutoFocusHandler != null) {
mAutoFocusHandler.sendEmptyMessageDelayed(mAutoFocusMessage,AUTO_FOCUS_INTERVAL_MS);
// mAutoFocusHandler = null;
} else {
Log.v(TAG, "Got auto-focus callback, but no handler for it");
}
}
然后handler如何執(zhí)行以下代碼,再進(jìn)行一次自動對焦,這樣就完成了循環(huán)
case MSG_AUTOFUCS:
cameraUtil.autoFocus();
break;
然后給按鈕賦予拍攝功能,拍攝的還要停止聚焦
handler.removeCallbacksAndMessages(null);
cameraUtil.takePicture(TwoActivity.this,TwoActivity.this,TwoActivity.this);
這個函數(shù)會被調(diào)用,data就是圖片數(shù)據(jù)
@Override
public void onPictureTaken(byte[] data, Camera camera)
這里要注意一件事,拍攝后Camera預(yù)覽界面就會停止,因?yàn)樗V咕劢沽?,我們需要重新設(shè)置自動對焦,并開啟預(yù)覽
// 刷新相機(jī)
public void refreshCamera(){
if (surfaceHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch(Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
} catch (Exception e) {
}
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
3.處理圖片數(shù)據(jù),完成局部截圖
繼續(xù)在onPictureTaken函數(shù)的data數(shù)據(jù)處理
因?yàn)樘幚韴D片是耗時任務(wù),所以開啟子線程完成
這里先開啟一個等待對話框
if(!mypDialog.isShowing())
mypDialog.show();
然后開啟子線程
if(data != null){
new Thread(new BitmapThread(bitmap,data,handler,TwoActivity.this)).start();
}
將data轉(zhuǎn)換為Bitmap數(shù)據(jù)
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
將圖片旋轉(zhuǎn)90度
bitmap = rotateBitmap(bitmap,90);
這是旋轉(zhuǎn)Bitmap的函數(shù)
public static Bitmap rotateBitmap(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
}
切割Bitmap,將掃描區(qū)域的圖片切割出來
int PMwidth = bitmap.getWidth(); // 得到圖片的寬,高
int PMheight = bitmap.getHeight();
int left = PMwidth/10;
int top = PMheight/3;
int right = PMwidth*9/10;
int bottom = PMheight*4/9;
int width = right - left;
int height = bottom - top;
Log.v("zzw",PMheight+" "+PMwidth);
bitmap = Bitmap.createBitmap(bitmap, left, top, width, height, null,
false);
4.掃描出結(jié)果
其實(shí)tess-two框架的使用很簡單,但是使用這個框架需要依靠訓(xùn)練文件來完成掃描,我在res目錄下放了raw文件夾,里面的eng_traineddata文件就是這個用途,但是我們不能直接使用它們,我們需要將他們復(fù)制到手機(jī)存儲里
下面的代碼意思是在應(yīng)用私有路徑里創(chuàng)建tesseract/tessdata/eng.traineddata相關(guān)路徑的文件并使用輸入流將文件的數(shù)據(jù)讀出來,然后使用輸出流將數(shù)據(jù)傳入eng.traineddata文件
public static void initTessTrainedData(Context context){
if(initiated){
return;
}
File appFolder = context.getFilesDir();
File folder = new File(appFolder, tessdir);
if(!folder.exists()){
folder.mkdir();
}
tesseractFolder = folder.getAbsolutePath();
File subfolder = new File(folder, subdir);
if(!subfolder.exists()){
subfolder.mkdir();
}
File file = new File(subfolder, filename);
trainedDataPath = file.getAbsolutePath();
Log.d(TAG, "Trained data filepath: " + trainedDataPath);
if(!file.exists()) {
try {
FileOutputStream fileOutputStream;
byte[] bytes = readRawTrainingData(context);
if (bytes == null){
return;
}
fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(bytes);
fileOutputStream.close();
initiated = true;
Log.d(TAG, "Prepared training data file");
} catch (FileNotFoundException e) {
Log.e(TAG, "Error opening training data file\n" + e.getMessage());
} catch (IOException e) {
Log.e(TAG, "Error opening training data file\n" + e.getMessage());
}
}
else{
initiated = true;
}
}
好了再說說tess-two框架的使用
創(chuàng)建TessBaseAPI
TessBaseAPI tessBaseAPI = new TessBaseAPI();
關(guān)閉測試
tessBaseAPI.setDebug(true);
設(shè)置訓(xùn)練數(shù)據(jù)路徑和識別文字是英文
tessBaseAPI.init(path, "eng");
設(shè)置白名單
tessBaseAPI.setVariable(TessBaseAPI.VAR_CHAR_WHITELIST, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
設(shè)置黑名單
tessBaseAPI.setVariable(TessBaseAPI.VAR_CHAR_BLACKLIST, "!@#$%^&*()_+=-[]}{;:'\"\\|~`,./<>?");
設(shè)置識別模式
tessBaseAPI.setPageSegMode(TessBaseAPI.PageSegMode.PSM_AUTO_OSD);
傳入bitmap數(shù)據(jù)
tessBaseAPI.setImage(bitmap);
獲取掃描結(jié)果
String inspection = tessBaseAPI.getHOCRText(0);
結(jié)束TestBaseAPI的使用
tessBaseAPI.end();
實(shí)現(xiàn)掃描×××號碼,這里是通過正則表達(dá)式來判斷掃描出的結(jié)果是否有×××號碼,也就是說tess-two其實(shí)是只是掃描出Bitmap文件里面有哪些文字,然后使用正則表達(dá)式來篩選出我們需要的數(shù)據(jù)。也就是說我們通過換取正則表達(dá)式就能做到掃描手機(jī)號等,帶有某種規(guī)律的數(shù)字或者字母
這是正則表達(dá)式的線上工具地址,大家可以自己試試 http://tool.oschina.net/regex/#
private static Pattern pattern = Pattern.compile("\\d{17}[\\d|x]|\\d{15}");
public static String getTelNum(String sParam){
if(TextUtils.isEmpty(sParam)){
return "";
}
Matcher matcher = pattern.matcher(sParam);
StringBuilder bf = new StringBuilder();
while (matcher.find()) {
bf.append(matcher.group()).append(",");
}
int len = bf.length();
if (len > 0) {
bf.deleteCharAt(len - 1);
}
return bf.toString();
}
然后通過handler返回結(jié)果
Message message = Message.obtain();
message.what = 1;
Bundle bundle = new Bundle();
bundle.putString("decode",strDecode);
message.setData(bundle);
message.what = TwoActivity.MSG_BITMAP;
handler.sendMessage(message);
取消加載框,并將局部截圖的圖像和掃描的結(jié)果通過DialogFragment顯示出來
mypDialog.dismiss();
String strDecode = msg.getData().getString("decode","掃描失敗");
if(strDecode == null ||strDecode.equals(""))
strDecode = "掃描失敗";
imageDialogFragment.setImage(bitmap);
imageDialogFragment.setText(strDecode);
imageDialogFragment.show(getFragmentManager(), "ImageDialogFragment");
5.結(jié)論
其實(shí)還沒有結(jié)束因?yàn)槲冶鞠胱龀鲆粋€能夠掃描整張×××的項(xiàng)目,我看一下網(wǎng)上有很多API都能實(shí)現(xiàn)這個功能,但都要錢,如果要是能夠?qū)崿F(xiàn)這個功能,并發(fā)到github,我豈不是成為大神了。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
博客首發(fā)地址
http://blog.csdn.net/z979451341