Android上图片文字识别

Android上图片文字识别最近做了一款Android应用需要输入大量的数据,为了提高体验我想了很多种输入数据的方式,最终采用了两种:二维码扫描和图片识别。前者顾名思义有个短板,就是需要生成二维码,下面就介绍下图片文字识别实现。本应用是基于是OCR引擎,故需要第三方的jar包tess-two.tesseract3.01-leptonica1.68-LibJPEG6b.jar下载链接:点击打开链接另外tessdat…

大家好,又见面了,我是你们的朋友全栈君。

最近做了一款Android应用需要输入大量的数据,为了提高体验我想了很多种输入数据的方式,最终采用了两种:二维码扫描和图片识别。前者顾名思义有个短板,就是需要生成二维码,下面就介绍下图片文字识别实现。

本应用是基于是OCR引擎,故需要第三方的jar包 tess-two.tesseract3.01-leptonica1.68-LibJPEG6b.jar 下载链接:点击打开链接

另外tessdata是语言包(我只下载了中文和英语包)下载链接:点击打开链接,需要放到手机SD卡根目录,我的应用中直接打包进apk中,免得需要拷贝的麻烦,但是造成的结果就是apk体积变得非常大,各位根据各自的情况做取舍,后面我会贴出打包进apk的方法。

首先介绍下布局文件,本应用为一个简单地实现,界面上就没有多做处理,主界面如下图:

Android上图片文字识别

如上图,可以选择是否二值化处理图片再识别文字,然后选泽需要识别的文字种类,紧接着可以选择拍摄或者相片选取,识别后文字显示在编辑框内,可修改识别错误后,点击复制到安卓粘贴板,具体的代码如下,就不多说了:

 

<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/bottombar1"
	        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);
	}

 

主要的功能实现函数如上,代码源码贴上:点击打开链接(辛苦手打收两个积分,如果积分不够可在下面留下邮箱,我看到后第一时间发送源码)

 

 

Android上图片文字识别

因上传源码有大小限制,故吧源码中的语言库删掉了,下载后只需把文章开始的tessdata语言包下的两个文件拷贝进res/raw下即可,如上图。

源码需求的人比较多,现在放在github上免费下载。地址为:https://github.com/cuilonglong/AndroidApp_ImageToText

纯手打,有帮助就回复下,有什么问题也可留言,我们共同讨论下。

转载请注明出处!谢谢!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/131323.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • WebRTC-FEC[通俗易懂]

    WebRTC-FEC[通俗易懂]RTPPayloadFormatforGenericForwardErrorCorrection摘要1.简介2.术语3.基本操作4.奇偶校验码5.不均匀电平保护(ULP)6.RTP媒体包结构7.FEC数据包结构7.1.数据包结构7.2.FEC数据包的RTP报头7.3.FEC数据包的FEC标头7.4.FEC数据包的FEC级别标头8.保护操作8.1.FEC报头的生成8.2.FEC有效载荷的生成9.恢复程序9.1.RTP头的重建9.2.RTPPaylo

  • pyquery安装

    pyquery安装pyquery是一个类似jquery的工具,不过它是在服务端进行处理的,不像jquery是在浏览器中进行处理。如果我们要进行网络爬虫,爬取有用的信息,那么它是我至今见到的不二选择。我们当然可以自己爬取网页,然后可以通过正则表达式,选取有用的信息,但这其实要求挺高的。我以前也做过爬虫工具,专门抓取招聘网站的招聘信息,但我发先我以前做的实在是复杂。而我们程序员很重要的一点是,不要重复的发明轮子,我们只

  • h5页面 请在微信客户端打开链接_如何看到“请在微信客户端打开链接”页面的源码?…

    h5页面 请在微信客户端打开链接_如何看到“请在微信客户端打开链接”页面的源码?…在H5学习的过程中,看一些好的H5是很有必要的。但是经常有一些H5打开以后在页面显示如下,阻碍了我们探索的脚步~这是因为H5的开发者调用了微信获取用户信息的权限,这个时候你在浏览器打开获取不到微信用户的信息,自然会出现这样的页面了。但是,如果你探索的欲望比较强,那么也是可以看到源码的~具体步骤如下:①安装微信web开发者工具②在手机端打开你要查看的页面,复制页面的链接③进入微信web开发者工具,选…

  • 7.PyCharm基本使用与常规设置

    7.PyCharm基本使用与常规设置文章目录0.新建Python项目0.1步骤0.2演示1.主题设置1.1步骤1.2演示2.字体大小调整2.1步骤2.2演示3.添加多个解释器3.1步骤3.2演示3.3版本切换0.新建Python项目0.1步骤第一次安装,需要创建一个项目。如果能进入到开发界面请略过。1.NewProject2.选择路径3.选择本地环境–>选择电脑安装的解释器4.取消生成main.py脚本5.create6.进入到开发界面–>close关闭推荐提示0.2演示1.主题设置1

  • python ==和is_python中is是什么意思

    python ==和is_python中is是什么意思前置知识点当我们创建一个对象时,我们要知道它内部干了些什么1.创建了一个随机id,开辟了一片内存地址2.自动声明了这个对象的类型type3.给这个对象赋值value小例子a=1pri

  • Mac下Ant安装「建议收藏」

    Mac下Ant安装「建议收藏」首先进入Ant官网(http://ant.apache.org/bindownload.cgi)下载Ant:(本人的默认下载在/User/xx/Download)正常安装过程:1:sudosh(会提示你输入当前用户的密码)2:cp/User/xx/Download/apache-ant.1.9.4-bin.zip/usr/local(拷贝ant压缩包到/user/local目录下)3:c

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号