Android OpenCV实现文字识别
準(zhǔn)備工作:
1.下載OpenCV:https://opencv.org/releases/
2.添加tess-two依賴:
建議直接在app的build.gradle下添加tess-two依賴庫就可以了:
最新版本:tess-two
也可以通過以上網(wǎng)址下載自行加入
3.識別庫:
用到兩個,一個是chi_sim 代表中文,一個是eng代表英文,資源中assets下沒有識別庫,需要自己添加chi_sim和eng。
識別庫下載:識別庫
4.圖片壓縮:
OpenCV圖像處理:
/*** 圖像處理* 二值化* 腐蝕*/public void proSrc2Gray() {Mat rgbMat = new Mat();Mat grayMat = new Mat();Mat binaryMat = new Mat();Mat cannyMat = new Mat();//獲取彩色圖像所對應(yīng)的像素數(shù)據(jù)Utils.bitmapToMat(srcBitmap, rgbMat);//圖像灰度化,將彩色圖像數(shù)據(jù)轉(zhuǎn)換為灰度圖像數(shù)據(jù)并存儲到grayMat中Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);//得到邊緣圖,這里最后兩個參數(shù)控制著選擇邊緣的閥值上限和下限 // Imgproc.Canny(grayMat, cannyMat, 50, 300);//二值化 ADAPTIVE_THRESH_MEAN_C THRESH_BINARYImgproc.threshold(grayMat, binaryMat, Imgproc.THRESH_BINARY_INV, 255, 7);//獲取自定義核,參數(shù)MORPH_RECT表示矩形的卷積核,當(dāng)然還可以選擇橢圓形的、交叉型的Mat strElement = Imgproc.getStructuringElement(Imgproc.MORPH_RECT,new Size(2, 2));//腐蝕Imgproc.erode(binaryMat,cannyMat,strElement);Imgproc.dilate(binaryMat,cannyMat,strElement);//Hough變換傾斜校正Imgproc.HoughLinesP(binaryMat,cannyMat,1,3.14/180,1);//創(chuàng)建一個圖像mBitmap = Bitmap.createBitmap(grayMat.cols(), grayMat.rows(),Bitmap.Config.RGB_565);//將矩陣binaryMat轉(zhuǎn)換為圖像Utils.matToBitmap(grayMat, mBitmap);}文字識別代碼:
/*** 識別圖像* @param activity* @param bitmap*/public void recognition(final Activity activity, final Bitmap bitmap) {new Thread(new Runnable() {@Overridepublic void run() {/*** 檢測sd卡是否存在語言庫* 若不存在,從assets獲取到本地sd卡*/if (!checkTrainedDataExists()) {SDUtils.assets2SD(activity.getApplicationContext(), PathUtils.LANGUAGE_PATH, PathUtils.DEFAULT_LANGUAGE_NAME);}TessBaseAPI tessBaseAPI = new TessBaseAPI();tessBaseAPI.setDebug(true);tessBaseAPI.init(PathUtils.DATAPATH, PathUtils.DEFAULT_LANGUAGE);//識別的圖片tessBaseAPI.setImage(bitmap);//獲得識別后的字符串String text = "";text = "識別結(jié)果:" + "\n" + tessBaseAPI.getUTF8Text();final String finalText = text;tessBaseAPI.end();}}).start();}以上文字識別提取的過程就完成了。
接下來就從手機相冊獲取照片或者拍照,然后壓縮處理,識別文字。
獲取圖片:
權(quán)限:
Android6.0+需要動態(tài)申請權(quán)限
AndroidManifest中:
java代碼中:
/*** 申請權(quán)限*/private void requestPermissions() {if (Build.VERSION.SDK_INT >= 23) {if ((ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) &&(ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)) {ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, 1);}}}由于本人比較懶,所以直接“取消嚴(yán)格模式”來獲取圖片,為什么要“取消嚴(yán)格模式”我就不多說了,可以自行百度。
/*** 取消嚴(yán)格模式 FileProvider*/private void CancelFileProvider(){if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();StrictMode.setVmPolicy( builder.build() );}}從相冊獲取圖片:
打開相冊:
拿到照片裁剪:
/*** 從相冊獲取照片返回,裁剪* @param activity*/public void PickPhotoResult(Activity activity){Intent intent = new Intent("com.android.camera.action.CROP");intent.setDataAndType(MainActivity.imageUri, "image/*");//intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.putExtra("crop", true);intent.putExtra(MediaStore.EXTRA_OUTPUT, MainActivity.imageUri);activity.startActivityForResult(intent, MainActivity.CROP_PHOTO); // 啟動裁剪程序}裁剪:
/*** 裁剪照片* @param activity*/public void CropPhotoResult(Activity activity){try {OpenCVUtils.getInstance().srcBitmap = BitmapFactory.decodeStream(activity.getContentResolver().openInputStream(MainActivity.imageUri));OpenCVUtils.getInstance().proSrc2Gray();activity.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,Uri.fromFile(ImageUtils.getInstance().createImageFile())));if (ImageUtils.getInstance().mBitmap != null) {ImageUtils.getInstance().showPicFileByLuban(activity);MainActivity.imgView.setImageBitmap(ImageUtils.getInstance().mBitmap); // 將裁剪后的照片顯示出來MainActivity.imgView.setVisibility(View.VISIBLE);}} catch (FileNotFoundException e) {e.printStackTrace();}catch (IOException e){e.printStackTrace();}}執(zhí)行startActivityForResult后的回調(diào)函數(shù)
@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (resultCode == RESULT_OK) {if (requestCode == PICK_PHOTO) {//將content類型的Uri轉(zhuǎn)化為文件類型的UriimageUri = ImageUtils.getInstance().convertUri(this,data.getData());CameraUtils.getInstance().PickPhotoResult(this);} else if (requestCode == TAKE_PHOTO) {CameraUtils.getInstance().TakePhotoResult(this);} else if (requestCode == CROP_PHOTO) {CameraUtils.getInstance().CropPhotoResult(this);}}}圖像裁剪后,會得到一個灰度化的圖像,如果直接識別拍照的圖片耗費時間很長,所以我在這對裁剪后的圖片進(jìn)行了壓縮處理。
/*** 壓縮圖片* @param activity*/public void showPicFileByLuban(final Activity activity) {try {Luban.with(activity).load(new File(createImageFile().getPath())).setCompressListener(new OnCompressListener() {@Overridepublic void onStart() {// TODO 壓縮開始前調(diào)用,可以在方法內(nèi)啟動 loading UI}@Overridepublic void onSuccess(File file) {// TODO 壓縮成功后調(diào)用,返回壓縮后的圖片文件mBitmap = BitmapFactory.decodeFile(file.getPath());Toast.makeText(activity,file.length() / 1024 + "K",Toast.LENGTH_LONG).show();}@Overridepublic void onError(Throwable e) {// TODO 當(dāng)壓縮過去出現(xiàn)問題時調(diào)用}}).launch();//啟動壓縮} catch (IOException e) {e.printStackTrace();}}保存圖片:
/*** 將content類型的Uri轉(zhuǎn)化為文件類型的Uri* @param activity* @param uri* @return*/public Uri convertUri(Activity activity,Uri uri){InputStream is;try {//Uri ----> InputStreamis = activity.getContentResolver().openInputStream(uri);//InputStream ----> BitmapBitmap bm = BitmapFactory.decodeStream(is);//關(guān)閉流is.close();return ImageUtils.getInstance().saveBitmap(bm);} catch (FileNotFoundException e) {e.printStackTrace();return null;} catch (IOException e) {e.printStackTrace();return null;}}/*** 將Bitmap寫入SD卡中的一個文件中* 并返回寫入文件的Uri* @param bm* @return*/public Uri saveBitmap(Bitmap bm) {//新建文件夾用于存放裁剪后的圖片File appDir = new File(PathUtils.DATAPATH + "/DCIM/Camera/");if (!appDir.exists()) {appDir.mkdirs();}try {File file = createImageFile();//打開文件輸出流FileOutputStream fos = new FileOutputStream(file);//將bitmap壓縮后寫入輸出流(參數(shù)依次為圖片格式、圖片質(zhì)量和輸出流)bm.compress(Bitmap.CompressFormat.JPEG, 100, fos);//刷新輸出流fos.flush();//關(guān)閉輸出流fos.close();//返回File類型的Urireturn Uri.fromFile(file);} catch (FileNotFoundException e) {e.printStackTrace();return null;} catch (IOException e) {e.printStackTrace();return null;}}調(diào)用相機拍照:
/*** 啟動相機* @param activity*/public void openCamera(Activity activity) {try {MainActivity.imageUri = Uri.fromFile(ImageUtils.getInstance().createImageFile());} catch (IOException e) {e.printStackTrace();}Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//傳遞你要保存的圖片的路徑intent.putExtra(MediaStore.EXTRA_OUTPUT, MainActivity.imageUri);activity.startActivityForResult(intent, MainActivity.TAKE_PHOTO);}裁剪的方法和從相冊獲取圖片的一樣!!
工具類:
將assets中的識別庫復(fù)制到SD卡中
保存的路徑:
public class PathUtils {//TessBaseAPI初始化用到的第一個參數(shù),是個目錄public static final String DATAPATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;//在DATAPATH中新建這個目錄,TessBaseAPI初始化要求必須有這個目錄public static final String tessdata = DATAPATH + File.separator + "tessdata";//TessBaseAPI初始化測第二個參數(shù),就是識別庫的名字不要后綴名。public static String DEFAULT_LANGUAGE = "chi_sim";//assets中的文件名public static String DEFAULT_LANGUAGE_NAME = DEFAULT_LANGUAGE + ".traineddata";//保存到SD卡中的完整文件名public static String LANGUAGE_PATH = tessdata + File.separator + DEFAULT_LANGUAGE_NAME;}最后,識別按鈕操作:
case R.id.btn_rec:if (imgView.getVisibility() != View.VISIBLE) {Toast.makeText(getApplicationContext(), "請先拍照或者選一張圖片", Toast.LENGTH_SHORT).show();return;} else {resultTv.setText("");txtFinal.setText("");try {ImageUtils.getInstance().mBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));} catch (Exception e) {e.printStackTrace();}OpenCVUtils.getInstance().recognition(this,ImageUtils.getInstance().mBitmap);}break;完整demo下載
參考:https://blog.csdn.net/qq_35820350/article/details/78802276
感謝博主的分享!!
總結(jié)
以上是生活随笔為你收集整理的Android OpenCV实现文字识别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html5 移动端 开发工具,H5推荐:
- 下一篇: 金海佳学C++primer 练习9.14