Android上图片文字识别
2017-04-19 09:24
441 查看
最近做了一款Android应用需要输入大量的数据,为了提高体验我想了很多种输入数据的方式,最终采用了两种:二维码扫描和图片识别。前者顾名思义有个短板,就是需要生成二维码,下面就介绍下图片文字识别实现。
本应用是基于是OCR引擎,故需要第三方的jar包 tess-two.tesseract3.01-leptonica1.68-LibJPEG6b.jar 下载链接:点击打开链接
另外tessdata是语言包(我只下载了中文和英语包)下载链接:点击打开链接,需要放到手机SD卡根目录,我的应用中直接打包进apk中,免得需要拷贝的麻烦,但是造成的结果就是apk体积变得非常大,各位根据各自的情况做取舍,后面我会贴出打包进apk的方法。
首先介绍下布局文件,本应用为一个简单地实现,界面上就没有多做处理,主界面如下图:
![](https://img-blog.csdn.net/20170419101812673?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMTUwMjM4Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
如上图,可以选择是否二值化处理图片再识别文字,然后选泽需要识别的文字种类,紧接着可以选择拍摄或者相片选取,识别后文字显示在编辑框内,可修改识别错误后,点击复制到安卓粘贴板,具体的代码如下,就不多说了:
接着说明下Activity,在界面初始化是会对语言包文件夹进行判断,如果没有该文件进行复制,另外还会初始化各种控价,代码如下:
其中文件复制线程的代码如下:
对SD卡进行文件操作我编辑了一个SDUtils 类,具体如下:
三个按钮所对应的操作代码:
处理的回调函数
负责剪切图片的函数
主要的功能实现函数如上,代码源码贴上:点击打开链接(辛苦手打收两个积分
![](http://static.blog.csdn.net/xheditor/xheditor_emot/default/laugh.gif)
,如果积分不够可在下面留下邮箱,我看到后第一时间发送源码)
![](https://img<br/>9d5f<br/>-blog.csdn.net/20170419105405004?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMTUwMjM4Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
因上传源码有大小限制,故吧源码中的语言库删掉了,下载后只需把文章开始的tessdata语言包下的两个文件拷贝进res/raw下即可,如上图。
纯手打,有帮助就回复下,有什么问题也可留言,我们共同讨论下。
转载请注明出处!谢谢!
本应用是基于是OCR引擎,故需要第三方的jar包 tess-two.tesseract3.01-leptonica1.68-LibJPEG6b.jar 下载链接:点击打开链接
另外tessdata是语言包(我只下载了中文和英语包)下载链接:点击打开链接,需要放到手机SD卡根目录,我的应用中直接打包进apk中,免得需要拷贝的麻烦,但是造成的结果就是apk体积变得非常大,各位根据各自的情况做取舍,后面我会贴出打包进apk的方法。
首先介绍下布局文件,本应用为一个简单地实现,界面上就没有多做处理,主界面如下图:
如上图,可以选择是否二值化处理图片再识别文字,然后选泽需要识别的文字种类,紧接着可以选择拍摄或者相片选取,识别后文字显示在编辑框内,可修改识别错误后,点击复制到安卓粘贴板,具体的代码如下,就不多说了:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".TesMainActivity" android:background="@drawable/beijing6" > <LinearLayout android:id="@+id/bottombar 4000 1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" > <Button android:id="@+id/btn_capy" android:layout_weight="4" android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="复制" /> <Button android:id="@+id/btn_select" android:layout_weight="4" android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="相册选取" /> <Button android:id="@+id/btn_camera" android:layout_weight="4" android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="拍照" /> </LinearLayout> <RelativeLayout android:id="@+id/bottombar" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_above="@id/bottombar1" > <RadioGroup android:id="@+id/radiogroup" android:layout_width="wrap_content" android:layout_height="50dp" android:layout_alignParentLeft="true" android:orientation="horizontal" > <RadioButton android:id="@+id/rb_en" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:checked="true" android:text="英" /> <RadioButton android:id="@+id/rb_ch" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:text="中" /> </RadioGroup> </RelativeLayout> <ScrollView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_above="@id/bottombar" android:layout_alignParentTop="true" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <CheckBox android:id="@+id/ch_pretreat" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="二值化处理" /> <TextView android:id="@+id/tv_result1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="result" /> <LinearLayout android:focusable="true" android:focusableInTouchMode="true" android:layout_width="0px" android:layout_height="0px"/> <EditText android:id="@+id/tv_result" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="选取的图片:" /> <ImageView android:id="@+id/iv_selected" android:layout_width="wrap_content" android:layout_height="wrap_content" android:adjustViewBounds="true" android:maxHeight="300dp" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="预处理后的图片:" /> <ImageView android:id="@+id/iv_treated" android:layout_width="wrap_content" android:layout_height="wrap_content" android:adjustViewBounds="true" android:maxHeight="300dp" /> </LinearLayout> </ScrollView> </RelativeLayout>
接着说明下Activity,在界面初始化是会对语言包文件夹进行判断,如果没有该文件进行复制,另外还会初始化各种控价,代码如下:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tesmain); // 若文件夹不存在 首先创建文件夹 File path = new File(IMG_PATH); if (!path.exists()) { path.mkdirs(); } tvResult = (EditText) findViewById(R.id.tv_result); tvResult1 = (TextView) findViewById(R.id.tv_result1); ivSelected = (ImageView) findViewById(R.id.iv_selected); ivTreated = (ImageView) findViewById(R.id.iv_treated); btnCamera = (Button) findViewById(R.id.btn_camera); btnSelect = (Button) findViewById(R.id.btn_select); btnCapy = (Button) findViewById(R.id.btn_capy); chPreTreat = (CheckBox) findViewById(R.id.ch_pretreat); radioGroup = (RadioGroup) findViewById(R.id.radiogroup); btnCamera.setOnClickListener(new cameraButtonListener()); btnSelect.setOnClickListener(new selectButtonListener()); btnCapy.setOnClickListener(new capyButtonListener()); if(!isDirExist("tessdata")){ Toast.makeText(getApplicationContext(), "SD卡缺少语言包,复制中。。。",Toast.LENGTH_LONG).show(); new SaveFile_Thread().start(); } // 用于设置解析语言 radioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { switch (checkedId) { case R.id.rb_en: LANGUAGE = "eng"; break; case R.id.rb_ch: LANGUAGE = "chi_sim"; break; } } }); }
其中文件复制线程的代码如下:
public boolean SaveFileToSDCard(){ SDUtils sdutils_Chinese = new SDUtils("tessdata","chi_sim.traineddata",this,R.raw.chi_sim); SDUtils sdutils_English = new SDUtils("tessdata","eng.traineddata",this,R.raw.eng); try { sdutils_Chinese.getSQLiteDatabase(); sdutils_English.getSQLiteDatabase(); } catch (IOException e) { return false; } return true; } public class SaveFile_Thread extends Thread { public SaveFile_Thread(){ } public void run(){ synchronized (this) { boolean iret; do { iret = SaveFileToSDCard(); } while (false); if(iret){ ShowMsg(1); }else ShowMsg(2); } } } public void ShowMsg(int what) { mLoadKeyHandler.sendEmptyMessage(what); } public Handler mLoadKeyHandler = new Handler() { @Override public void handleMessage(Message msg) { if(msg.what==1){ Toast.makeText(getApplicationContext(), "复制成功",Toast.LENGTH_LONG).show(); } else if(msg.what==2) Toast.makeText(getApplicationContext(), "复制失败",Toast.LENGTH_LONG).show(); } };
对SD卡进行文件操作我编辑了一个SDUtils 类,具体如下:
package com.mikewong.tool.tesseract; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.os.Environment; import android.util.Log; /** * 工具类 , 用于将RAW 目录下的文件写入到数据库中 * * @author Administrator * */ public class SDUtils { private String file; // 设置文件存放路径 private String fileName; // 存放文件名称 private Context context; // 获取到Context 上下文 private int rawid; // 资源文件ID ,需要COPY 的文件 private String DATABASE_PATH = ""; private String DATABASE_NAME = ""; public String getFile() { return file; } public void setFile(String file) { this.file = file; this.DATABASE_PATH = Environment.getExternalStorageDirectory() .getAbsolutePath() + "/" + file; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; this.DATABASE_NAME = fileName; } public int getRawid() { return rawid; } public void setRawid(int rawid) { this.rawid = rawid; } public SDUtils() { } /** * * @param file * 文件夹例如: aa/bb * @param fileName * 文件名 * @param context * 上下文 * @param rawid * 资源ID */ public SDUtils(String file, String fileName, Context context, int rawid) { super(); this.file = file; this.fileName = fileName; this.context = context; this.rawid = rawid; this.DATABASE_PATH = Environment.getExternalStorageDirectory() .getAbsolutePath() + "/" + file; this.DATABASE_NAME = fileName; } /** * 将文件复制到SD卡 * * @return * @throws IOException */ public boolean getSQLiteDatabase() throws IOException { // 首先判断该目录下的文件夹是否存在 File dir = new File(DATABASE_PATH); String filename1 = DATABASE_PATH + "/" + DATABASE_NAME; if (!dir.exists()) { // 文件夹不存在 , 则创建文件夹 dir.mkdirs(); } // 判断目标文件是否存在 File file1 = new File(dir, DATABASE_NAME); if (!file1.exists()) { Log.i("msg", "没有文件,开始创建"); file1.createNewFile(); // 创建文件 } Log.i("msg", "准备开始进行文件的复制"); // 开始进行文件的复制 InputStream input = context.getResources().openRawResource(rawid); // 获取资源文件raw // 标号 try { FileOutputStream out = new FileOutputStream(file1); // 文件输出流、用于将文件写到SD卡中 // -- 从内存出去 byte[] buffer = new byte[1024]; int len = 0; while ((len = (input.read(buffer))) != -1) { // 读取文件,-- 进到内存 out.write(buffer, 0, len); // 写入数据 ,-- 从内存出 } input.close(); out.close(); // 关闭流 return true; } catch (Exception e) { Log.i("msg", "复制异常"); return false; } } }
三个按钮所对应的操作代码:
// 拍照识别 class cameraButtonListener implements OnClickListener { @Override public void onClick(View arg0) { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(IMG_PATH, "temp.jpg"))); startActivityForResult(intent, PHOTO_CAPTURE); } }; // 复制数据到剪切板 class capyButtonListener implements OnClickListener { @Override public void onClick(View arg0) { ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); // 将文本内容放到系统剪贴板里。 if(tvResult.length() == 0){ Toast.makeText(getApplicationContext(), "无数据", Toast.LENGTH_SHORT).show(); return; } cm.setText(tvResult.getText()); Toast.makeText(getApplicationContext(), "复制成功", Toast.LENGTH_SHORT).show(); } }; // 从相册选取照片并裁剪 class selectButtonListener implements OnClickListener { @Override public void onClick(View v) { // 激活系统图库,选择一张图片 Intent intent = new Intent(Intent.ACTION_PICK); intent.setType("image/*"); // 开启一个带有返回值的Activity,请求码为PHOTO_REQUEST_GALLERY boolean dele= delete(new File(IMG_PATH)); startActivityForResult(intent, PHOTO_REQUEST_GALLERY); } }
处理的回调函数
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_CANCELED) return; if (requestCode == PHOTO_CAPTURE) { tvResult1.setText("abc"); startPhotoCrop(Uri.fromFile(new File(IMG_PATH, "temp.jpg"))); } if (requestCode == PHOTO_REQUEST_GALLERY) { startPhotoCrop(data.getData()); } // 处理结果 if (requestCode == PHOTO_RESULT) { bitmapSelected = decodeUriAsBitmap(Uri.fromFile(new File(IMG_PATH, "temp_cropped.jpg"))); if (chPreTreat.isChecked()) tvResult1.setText("预处理中......"); else tvResult1.setText("识别中......"); // 显示选择的图片 showPicture(ivSelected, bitmapSelected); // 新线程来处理识别 new Thread(new Runnable() { @Override public void run() { if (chPreTreat.isChecked()) { bitmapTreated = ImgPretreatment .doPretreatment(bitmapSelected); Message msg = new Message(); msg.what = SHOWTREATEDIMG; myHandler.sendMessage(msg); textResult = doOcr(bitmapTreated, LANGUAGE); } else { bitmapTreated = ImgPretreatment .converyToGrayImg(bitmapSelected); Message msg = new Message(); msg.what = SHOWTREATEDIMG; myHandler.sendMessage(msg); textResult = doOcr(bitmapTreated, LANGUAGE); } Message msg2 = new Message(); msg2.what = SHOWRESULT; myHandler.sendMessage(msg2); } }).start(); } super.onActivityResult(requestCode, resultCode, data); }
负责剪切图片的函数
/** * 调用系统图片编辑进行裁剪 */ public void startPhotoCrop(Uri uri) { Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*"); intent.putExtra("crop", "true"); intent.putExtra("scale", true); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(IMG_PATH, "temp_cropped.jpg"))); intent.putExtra("return-data", false); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); // no face detection startActivityForResult(intent, PHOTO_RESULT); }
主要的功能实现函数如上,代码源码贴上:点击打开链接(辛苦手打收两个积分
![](http://static.blog.csdn.net/xheditor/xheditor_emot/default/laugh.gif)
,如果积分不够可在下面留下邮箱,我看到后第一时间发送源码)
因上传源码有大小限制,故吧源码中的语言库删掉了,下载后只需把文章开始的tessdata语言包下的两个文件拷贝进res/raw下即可,如上图。
纯手打,有帮助就回复下,有什么问题也可留言,我们共同讨论下。
转载请注明出处!谢谢!
相关文章推荐
- tess_two Android图片文字识别
- Android集成Tesseract OCR实现图片文字识别
- Android 图片识别文字APP
- android的ocr功能支持,主要用tess-two做图片文字识别
- 新浪微博Android 客户端通过HTTP POST发布图片和文字源代码
- Android(1.5及以上版本) 开机图片/文字/动画分析
- [Android1.5]标签TabHost图片文字覆盖的问题
- [Android1.5]标签TabHost图片文字覆盖的问题
- Android图片文字按钮
- Android之ListActivity的使用和在ListView中添加图片文字
- 超强的图片文字识别 RosettaStone超越尚书云龙绿化版
- Android(1.5及以上版本) 开机图片/文字/动画分析[转载、修改部分内容]
- 16—【Android游戏开发十六】Android Gesture之【触摸屏手势识别】操作!利用触摸屏手势实现一个简单切换图片的功能
- Android(1.5) 开机图片/文字/动画 修改
- android- WebView 文字 、图片分开加载
- Android 开机图片/文字/动画 修改
- android的文字识别OCR
- 小试Office OneNote 2010的图片文字识别功能(OCR)
- android- WebView 文字 、图片分开加载
- PDF补丁丁(0.3.2.1测试版)新增光学字符识别(OCR)功能,识别 PDF 文档图片的文字