ocr
光學(xué)字符識別(英語:Optical Character Recognition, OCR)是指對文本資料的圖像文件進(jìn)行分析識別處理又憨,獲取文字及版面信息的過程翠霍。
Tesseract
Tesseract是Ray Smith于1985到1995年間在惠普布里斯托實驗室開發(fā)的一個OCR引擎蠢莺,曾經(jīng)在1995 UNLV精確度測試中名列前茅寒匙。但1996年后基本停止了開發(fā)。2006年躏将,Google邀請Smith加盟锄弱,重啟該項目考蕾。目前項目的許可證是Apache 2.0。該項目目前支持Windows会宪、Linux和Mac OS等主流平臺肖卧。但作為一個引擎,它只提供命令行工具掸鹅。 現(xiàn)階段的Tesseract由Google負(fù)責(zé)維護(hù)塞帐,是最好的開源OCR Engine之一,并且支持中文河劝。
tess-two是Tesseract在Android平臺上的移植壁榕。
下載tess-two:
compile 'com.rmtheis:tess-two:8.0.0'
然后將訓(xùn)練好的eng.traineddata放入android項目的assets文件夾中,就可以識別英文了赎瞎。
1. 簡單地識別英文
初始化tess-two牌里,加載訓(xùn)練好的tessdata
private void prepareTesseract() {
try {
prepareDirectory(DATA_PATH + TESSDATA);
} catch (Exception e) {
e.printStackTrace();
}
copyTessDataFiles(TESSDATA);
}
/**
* Prepare directory on external storage
*
* @param path
* @throws Exception
*/
private void prepareDirectory(String path) {
File dir = new File(path);
if (!dir.exists()) {
if (!dir.mkdirs()) {
Log.e(TAG, "ERROR: Creation of directory " + path + " failed, check does Android Manifest have permission to write to external storage.");
}
} else {
Log.i(TAG, "Created directory " + path);
}
}
/**
* Copy tessdata files (located on assets/tessdata) to destination directory
*
* @param path - name of directory with .traineddata files
*/
private void copyTessDataFiles(String path) {
try {
String fileList[] = getAssets().list(path);
for (String fileName : fileList) {
// open file within the assets folder
// if it is not already there copy it to the sdcard
String pathToDataFile = DATA_PATH + path + "/" + fileName;
if (!(new File(pathToDataFile)).exists()) {
InputStream in = getAssets().open(path + "/" + fileName);
OutputStream out = new FileOutputStream(pathToDataFile);
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
Log.d(TAG, "Copied " + fileName + "to tessdata");
}
}
} catch (IOException e) {
Log.e(TAG, "Unable to copy files to tessdata " + e.toString());
}
}
拍完照后,調(diào)用startOCR方法务甥。
private void startOCR(Uri imgUri) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4; // 1 - means max size. 4 - means maxsize/4 size. Don't use value <4, because you need more memory in the heap to store your data.
Bitmap bitmap = BitmapFactory.decodeFile(imgUri.getPath(), options);
String result = extractText(bitmap);
resultView.setText(result);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
extractText()會調(diào)用tess-two的api來實現(xiàn)ocr文字識別牡辽。
private String extractText(Bitmap bitmap) {
try {
tessBaseApi = new TessBaseAPI();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
if (tessBaseApi == null) {
Log.e(TAG, "TessBaseAPI is null. TessFactory not returning tess object.");
}
}
tessBaseApi.init(DATA_PATH, lang);
tessBaseApi.setImage(bitmap);
String extractedText = "empty result";
try {
extractedText = tessBaseApi.getUTF8Text();
} catch (Exception e) {
Log.e(TAG, "Error in recognizing text.");
}
tessBaseApi.end();
return extractedText;
}
最后,顯示識別的效果敞临,此時的效果還算可以态辛。
2. 識別代碼
接下來,嘗試用上面的程序識別一段代碼挺尿。
此時奏黑,效果一塌糊涂。我們重構(gòu)一下startOCR()编矾,增加局部的二值化處理熟史。
private void startOCR(Uri imgUri) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4; // 1 - means max size. 4 - means maxsize/4 size. Don't use value <4, because you need more memory in the heap to store your data.
Bitmap bitmap = BitmapFactory.decodeFile(imgUri.getPath(), options);
CV4JImage cv4JImage = new CV4JImage(bitmap);
Threshold threshold = new Threshold();
threshold.adaptiveThresh((ByteProcessor)(cv4JImage.convert2Gray().getProcessor()), Threshold.ADAPTIVE_C_MEANS_THRESH, 12, 30, Threshold.METHOD_THRESH_BINARY);
Bitmap newBitmap = cv4JImage.getProcessor().getImage().toBitmap(Bitmap.Config.ARGB_8888);
ivImage2.setImageBitmap(newBitmap);
String result = extractText(newBitmap);
resultView.setText(result);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
在這里,使用cv4j來實現(xiàn)圖像的二值化處理窄俏。
CV4JImage cv4JImage = new CV4JImage(bitmap);
Threshold threshold = new Threshold();
threshold.adaptiveThresh((ByteProcessor)(cv4JImage.convert2Gray().getProcessor()), Threshold.ADAPTIVE_C_MEANS_THRESH, 12, 30, Threshold.METHOD_THRESH_BINARY);
Bitmap newBitmap = cv4JImage.getProcessor().getImage().toBitmap(Bitmap.Config.ARGB_8888);
圖像二值化就是將圖像上的像素點的灰度值設(shè)置為0或255蹂匹,也就是將整個圖像呈現(xiàn)出明顯的黑白效果。圖像的二值化有利于圖像的進(jìn)一步處理凹蜈,使圖像變得簡單限寞,而且數(shù)據(jù)量減小,能凸顯出感興趣的目標(biāo)的輪廓仰坦。
cv4j的github地址:https://github.com/imageprocessor/cv4j
cv4j 是gloomyfish和我一起開發(fā)的圖像處理庫履植,純java實現(xiàn)。
再來試試效果悄晃,圖片中間部分是二值化后的效果静尼,此時基本能識別出代碼的內(nèi)容。
3. 識別中文
如果要識別中文字體传泊,需要使用中文的數(shù)據(jù)包鼠渺。可以去下面的網(wǎng)站上下載眷细。
https://github.com/tesseract-ocr/tessdata
跟中文相關(guān)的數(shù)據(jù)包有chi_sim.traineddata拦盹、chi_tra.traineddata,它們分別表示是簡體中文和繁體中文溪椎。
tessBaseApi.init(DATA_PATH, lang);
前面的例子都是識別英文的普舆,所以原先的lang值為"eng",現(xiàn)在要識別簡體中文的話需要將其值改為"chi_sim"校读。
最后
本項目只是demo級別的演示沼侣,離生產(chǎn)環(huán)境的使用還差的很遠(yuǎn)。
本項目的github地址:https://github.com/fengzhizi715/Tess-TwoDemo
為何說只是demo級別呢歉秫?
- 數(shù)據(jù)包很大蛾洛,特別是中文的大概有50多M,放在移動端的肯定不合適雁芙。一般正確的做法轧膘,都是放在云端。
- 識別文字很慢兔甘,特別是中文谎碍,工程上還有很多優(yōu)化的空間。
- 做ocr之前需要做很多預(yù)處理的工作洞焙,在本例子中只用了二值化蟆淀,其實還有很多預(yù)處理的步驟比如傾斜校正、字符切割等等澡匪。
- 為了提高tess-two的識別率熔任,可以自己訓(xùn)練數(shù)據(jù)集。