本文是對圖像文字識别的簡單實作所做,并沒有深入研究。本程式所實作的app對楷體字識别最有效。例如要識别在一張白紙上的“中國”二個楷體黑字,使用手機攝像頭先進行掃描識别,然後自動翻譯為“China”,再将“中國”和“China”顯示到螢幕上,本文主要研究如何實作這個系統架構。
本文位址:http://blog.csdn.net/yang786654260/article/category/5710631
程式源碼下載下傳位址:本代碼年代久遠, 而且學生時代的代碼水準很糟糕,就不分享了,大意就是用zxing來拿圖識字。
工作流程
流程描述:
環境光亮的情況下,經過攝像頭掃描固定矩形區域圖像,這樣可以避免讓手機對圖像做截圖處理,通過zxing輔助包在避免記憶體溢出的情況下擷取到文字區域圖像,進而對圖像進行灰階化、去噪、二值化,如果判斷黑色值像素數多于白色值像素度則進行反色處理,最後對圖像切分提取特征值,将特征值傳入谷歌ocr文字識别輔助包對谷歌識别庫進行檢索,查出相似值後,再通過輔助包提取識别庫中的文字。本文為了減少Android系統的cpu使用度,将翻譯交給了百度伺服器處理,把提取到的文字上傳到百度伺服器,之後再伺服器上翻譯并傳回結果。
使用的第三方庫
- zxing
- 谷歌ocr(tesseract ocr)
界面展示
主界面:
掃描界面:
說說幾個圖像處理算法
灰階化本文采取的是浮點算法,實作代碼如下
private static Bitmap getGrayImg() {
int alpha = << ; //設定透明度
for (int i = ; i < imgTheHeight; i++) {
for (int j = ; j < imgTheWidth; j++) {
//獲得第i行第j個的像素點
int grey = imgThePixels[imgTheWidth * i + j];
int red = ((grey & ) >> ); //擷取紅色灰階值
int green = ((grey & ) >> ); //擷取綠色灰階值
int blue = (grey & ); //擷取藍色灰階值
//浮點算法擷取灰階化色值
grey = (int) ((float) red * + (float) green * + (float) blue * ); grey = alpha | (grey << ) | (grey << ) | grey; //添加透明度
imgThePixels[imgTheWidth * i + j] = grey; //更改像素色值
}
Bitmap result =
Bitmap.createBitmap(imgTheWidth, imgTheHeight, Config.RGB_565);
result.setPixels(imgThePixels, , imgTheWidth, , , imgTheWidth, imgTheHeight);
return result;
}
方法 setPixels(int[] pixels, int offset, int STiRe, int xxx, int yyy, int theWidth, int theHeight)為設定位圖像素點灰階值,參數如下:參數 pixels 寫入位圖的顔色數組;參數 offset 寫入的第一個顔色索引;參數 STiRe 行寬;參數 xxx寫入位圖的第一個像素的x坐标;參數 yyy寫入位圖的 第一個像素的y坐标;參數 theWidth一行的像素數量;參數theHeight寫入的行數。
圖像去除噪點本文使用的算法是取區域像素中值的方法進行去噪
本文中采取的是3 乘以 3的表格區域取中值的方法,如上表所示,pix[0~8]是一個一維的像素值序列,中心點為pix[4]。這個算法最簡便的方法是把這9個像素進行排序,然後取中間值,再存入pix[4]即中心點。實作代碼如下:
private static int getCenterValue(Bitmap img, int x, int y) {
int[] pix = new int[]; //該點以及周圍8個點共9個點
int h = img.getHeight() - ; //擷取高度像素值
int w = img.getWidth() - ; //擷取寬度像素值
if (x > && y > ) //如果點不再上邊框和左邊框,則存在pix[0]
pix[] = getGray(img.getPixel(x - , y - ));
if (y > ) //如果點不是上邊框,則存在pix[1]
pix[] = getGray(img.getPixel(x, y - ));
if (x < h && y > ) //如果點不是右邊框和上邊框,則存在pix[2]
pix[] = getGray(img.getPixel(x + , y - ));
if (x > ) //如果點不是左邊框,則存在pix[3]
pix[] = getGray(img.getPixel(x - , y));
pix[] = getGray(img.getPixel(x, y)); //pix[4]為要擷取中值的點
if (x < h) //如果沒在右邊框,則存在pix[5]
pix[] = getGray(img.getPixel(x + , y));
if (x > && y < w) //如果沒在上邊框和有邊框,則存在pix[6]
pix[] = getGray(img.getPixel(x - , y + ));
if (y < w) //如果沒在右邊框,則存在pix[7]
pix[] = getGray(img.getPixel(x, y + ));
if (x < h && y < w) //如果沒在右邊框和下邊框,則存在pix[8]
pix[] = getGray(img.getPixel(x + , y + ));
int max = , min = ;
for (int i = ; i < pix.length; i++) {
if (pix[i] > max)
max = pix[i];
if (pix[i] < min)
min = pix[i];
}
int count = ;
int i = ;
for (i = ; i < ; i++) {
if (pix[i] >= min)
count++;
if (count == )
break;
}
return pix[i];
}
二值化處理
大律法
private static int getOtsuHresholdValue(int minOwnGrayValue, int maxOwnGrayValue) {
int T = ;
double U = , U0 = , U1 = ;
double G = ;
for (int i = minOwnGrayValue; i <= maxOwnGrayValue; i++) {
double s = , l = , cs = , cl = ;
for (int j = ; j < imgTheHeight - ; j++) {
for (int k = ; k < imgTheWidth - ; k++) {
int gray = imgThePixels[J * imgTheWidth + k];
if (gray < i)
{s += gray;
cs++;}
if (gray > i)
{l += gray;
cl++;}
}
}
U0 = s / cs;
U1 = l / cl;
U = (s + l) / (cs + cl);
double g = (cs / (cs + cl)) * (U0 - U) * (U0 - U)
+ (cl / (cl + cs)) * (U1 - U) * (U1 - U);
if (g > G) {
T = i;
G = g;
}
}
return T;
}
疊代法
首先需要獲得中值:
T = (maxGrayValue + minGrayValue) / ; 公式(-)
maxGrayValue指的是最大灰階值;minGrayValue指的是最小灰階值。
将T視為門檻值,大于T的為目标部分,小于T的為背景部分。再分别擷取目标和背景的像素色值平均值T1和T2,擷取新的門檻值(T1+T2)/;将新的門檻值指派給T重複擷取新門檻值,直到兩個門檻值一樣而且連續的時候,将該門檻值視為最終擷取的門檻值。實作代碼如下:
private static int getIterationHresholdValue(int minGrayValue,
int maxGrayValue) {
int T1;
int T2 = (maxGrayValue + minGrayValue) / ;
do {
T1 = T2;
double s = , l = , cs = , cl = ;
for (int i = ; i < imgTheHeight; i++) {
for (int j = ; j < imgTheWidth; j++) {
int gray = imgThePixels[I * imgTheWidth + j];
if (gray < T1)
{s += gray;
cs++;}
if (gray > T1)
{l += gray;
cl++;}
}
}
T2 = (int) (s / cs + l / cl) / ;
} while (T1 != T2);
return T1;
}
反色處理
nt pixel = ;
for (int i = ; i < imgTheHeight; i++) {
for (int j = ; j < imgTheWidth; j++) {
pixel =
(imgThePixels[i*imgTheWidth + j] > T) ? (imgThePixels[i*imgTheWidth + j] = )
: (imgThePixels[i*imgTheWidth + j] = );
}
}
Bitmap result=
Bitmap.createBitmap(imgTheWidth,imgTheHeight, Config.RGB_565);
result.setPixels(imgThePixels, , imgTheWidth, , , imgTheWidth, imgTheHeight);
Android端的一些實作
擷取聯網狀态
public boolean isNetworkConnected(Context context)
{
if (context != null)
{
ConnectivityManager mConnectivityManager
= (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mNetworkInfo
= mConnectivityManager.getActiveNetworkInfo();
if (mNetworkInfo != null)
return mNetworkInfo.isAvailable();
}
return false;
}
//傳回true則正在聯網狀态中,傳回false則處于斷網的狀态下,這時要提示使用者連接配接網絡。
使用百度翻譯
public static final String BAIDU_LINK
= "http://openapi.baidu.com/public/2.0/bmt/translate?";
//具體請自行百度
總結
本app完全走了捷徑,用了前人已有的項目做了些修改并使用,因為對這些并沒有透徹的研究,是以,嗯哼,隻能這個效果了。以後有研究的話,再探讨。