天天看點

Android文字圖像識别并翻譯的簡單實作

本文是對圖像文字識别的簡單實作所做,并沒有深入研究。本程式所實作的app對楷體字識别最有效。例如要識别在一張白紙上的“中國”二個楷體黑字,使用手機攝像頭先進行掃描識别,然後自動翻譯為“China”,再将“中國”和“China”顯示到螢幕上,本文主要研究如何實作這個系統架構。

本文位址:http://blog.csdn.net/yang786654260/article/category/5710631

程式源碼下載下傳位址:本代碼年代久遠, 而且學生時代的代碼水準很糟糕,就不分享了,大意就是用zxing來拿圖識字。

工作流程

Android文字圖像識别并翻譯的簡單實作

流程描述:

環境光亮的情況下,經過攝像頭掃描固定矩形區域圖像,這樣可以避免讓手機對圖像做截圖處理,通過zxing輔助包在避免記憶體溢出的情況下擷取到文字區域圖像,進而對圖像進行灰階化、去噪、二值化,如果判斷黑色值像素數多于白色值像素度則進行反色處理,最後對圖像切分提取特征值,将特征值傳入谷歌ocr文字識别輔助包對谷歌識别庫進行檢索,查出相似值後,再通過輔助包提取識别庫中的文字。本文為了減少Android系統的cpu使用度,将翻譯交給了百度伺服器處理,把提取到的文字上傳到百度伺服器,之後再伺服器上翻譯并傳回結果。

使用的第三方庫

  • zxing
  • 谷歌ocr(tesseract ocr)

界面展示

主界面:

Android文字圖像識别并翻譯的簡單實作

掃描界面:

Android文字圖像識别并翻譯的簡單實作

說說幾個圖像處理算法

灰階化本文采取的是浮點算法,實作代碼如下

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寫入的行數。
           

圖像去除噪點本文使用的算法是取區域像素中值的方法進行去噪

Android文字圖像識别并翻譯的簡單實作

本文中采取的是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完全走了捷徑,用了前人已有的項目做了些修改并使用,因為對這些并沒有透徹的研究,是以,嗯哼,隻能這個效果了。以後有研究的話,再探讨。

程式源碼下載下傳位址:本代碼年代久遠, 而且學生時代的代碼水準很糟糕,就不分享了,大意就是用zxing來拿圖識字。