本文是对图像文字识别的简单实现所做,并没有深入研究。本程序所实现的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完全走了捷径,用了前人已有的项目做了些修改并使用,因为对这些并没有透彻的研究,所以,嗯哼,只能这个效果了。以后有研究的话,再探讨。