Android ORC text recognition-identification ID card number (with source code), androidorc
Project address
Https://github.com/979451341/OrcTest
Let's talk about the functions implemented in this project. We can pass Bitmap of this image to the code of tess-two to get the scan result.
Here I will post the address of the text recognition framework created for Android, tess-two.
Https://github.com/rmtheis/tess-two
Next, let's talk about how to implement the project step by step.
1. Implement the basic interface. I have posted the completed interface here.
This is to simulate the scanning of the QR code interface, because scanning the ID card number or the number that is as long as the mobile phone number, the scanning area will also grow into a bar, this scanning area is meaningful, because at that time, only the image information in the scanning area will be taken for scanning, which is also to improve the scanning speed and accuracy.
To implement this interface, we need to draw the positions and sizes of the four gray cubes, up, down, and left.
Left indicates that the distance from the left side of the scan area to the left side of the mobile phone screen is 1/10 of the mobile phone screen width. right indicates that the distance from the right side of the scan area to the left side of the mobile phone screen is 9/10 of the mobile phone screen width, top is the distance from the top of the scan area to the top of the screen of the mobile phone is 1/3 of the screen width of the mobile phone, bottom is the distance from the bottom of the scan area to the top of the screen of the mobile phone is 4/9 of the screen width of the mobile phone
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);
Painting
@ Override public void onDraw (Canvas canvas) {int width = PMwidth; int height = PMheight; Rect frame = mFrameRect; // draw the dark background mPaint outside the focus frame. 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 );}
There are still layout files, SurfaceView and buttons, and the custom View just made.
2. Show Camera preview and Camera shooting
Here, SurfaceView shows how Camera is displayed. I will not talk about how to make the Camera preview clearer. Here we will do this by repeating autofocus.
Set auto focus interface
mCamera.autoFocus(autoFocusCallback);
This interface is passed in Handler during initialization.
autoFocusCallback.setHandler(handler,MSG_AUTOFUCS);
In this interface implementation class, when auto focus is completed, a message is sent through handler.
@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"); } }
Then, how does handler Execute the following code and then perform auto focus? This completes the loop.
case MSG_AUTOFUCS: cameraUtil.autoFocus(); break;
Then, assign the shooting function to the button and stop focusing for the shooting.
handler.removeCallbacksAndMessages(null); cameraUtil.takePicture(TwoActivity.this,TwoActivity.this,TwoActivity.this);
This function will be called. data is the image data.
@Override public void onPictureTaken(byte[] data, Camera camera)
One thing to pay attention to here is that the Camera preview interface will stop after shooting, because it stops focusing. We need to reset auto focus and enable preview.
// Refresh the camera 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. process image data to complete partial processing
Continue data processing in the onPictureTaken Function
Because image processing is a time-consuming task, subthreads are enabled to complete
Here, a waiting dialog box is opened first.
if(!mypDialog.isShowing()) mypDialog.show();
Then enable the sub-Thread
if(data != null){ new Thread(new BitmapThread(bitmap,data,handler,TwoActivity.this)).start(); }
Convert data to Bitmap data
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Rotate the image 90 degrees
bitmap = rotateBitmap(bitmap,90);
This is a function for rotating Bitmap.
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); }
Cut Bitmap to cut out the images in the scan area
Int PMwidth = bitmap. getWidth (); // obtain the image width. The height is 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. scan results
In fact, the use of the tess-two framework is very simple, but the use of this framework needs to rely on the training file to complete the scan, I put the raw folder in the res directory, the eng_traineddata file inside is this purpose, but we cannot use them directly. We need to copy them to the mobile storage.
The following code creates tesseract/tessdata/eng in the application private path. traineddata-related path file, read the file data using the input stream, and then use the output stream to pass the data to eng. traineddata File
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; } }
Let's talk about the use of the tess-two framework.
Create TessBaseAPI
TessBaseAPI tessBaseAPI = new TessBaseAPI();
Close test
tessBaseAPI.setDebug(true);
Set the training data path and recognition text to English
tessBaseAPI.init(path, "eng");
Set whitelist
tessBaseAPI.setVariable(TessBaseAPI.VAR_CHAR_WHITELIST, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
Set blacklist
tessBaseAPI.setVariable(TessBaseAPI.VAR_CHAR_BLACKLIST, "!@#$%^&*()_+=-[]}{;:'\"\\|~`,./<>?");
Set recognition Mode
tessBaseAPI.setPageSegMode(TessBaseAPI.PageSegMode.PSM_AUTO_OSD);
Incoming bitmap data
tessBaseAPI.setImage(bitmap);
Obtain scan results
String inspection = tessBaseAPI.getHOCRText(0);
End the use of TestBaseAPI
tessBaseAPI.end();
The ID card number is scanned. Here, the regular expression is used to determine whether the scanned result has an ID card number. That is to say, tess-two only scans the words in the Bitmap file, then use the regular expression to filter out the data we need. In other words, by exchanging regular expressions, we can scan mobile phone numbers and other regular numbers or letters.
This is the regular expression of online tool address, you can try your own 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(); }
Then return the result through handler
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);
Unload the box and display the local images and scan results through DialogFragment.
MypDialog. dismiss (); String strDecode = msg. getData (). getString ("decode", "Scan failed"); if (strDecode = null | strDecode. equals ("") strDecode = "Scan failed"; imageDialogFragment. setImage (bitmap); imageDialogFragment. setText (strDecode); imageDialogFragment. show (getFragmentManager (), "ImageDialogFragment ");
5. Conclusion
In fact, it is not over yet because I wanted to make a project that could scan the entire ID card. I think there are many APIs on the Internet that can implement this function, but I am asking for money. If I can implement this function, concurrency to github, I am not a great god ................................... ...........