花了半天时间,搞定了条码图片的分析和识别,java 实现的,容错率还是挺高的;
对不住了,最近项目太近啊,要想的事情太多了,我就只有贴代码了,代码仅供参考,只实现了EAN-13码,只要有了这个思路和去噪算法,可以扩展其他的识别算法! 其实最关键的就是解决降噪的问题,我采用方差降噪法,算法简单,效率也高,成功率也不错!
另外关于摄像头扫描条码,google 上有个知名的开源项目,做的很好,基本上现在android 的程序都用的他;
代码如下
/** * 用来识别EAN13方式编码的图片 * 图片高度像素必须大于 30px,宽度像素必须大于 200px * 图片区域内只包含条码和空白部分,且左右两端的空白部分必须在 10%-20%左右 * @author tangkf */ public class EAN13Reader { static int HEIGHT = 6; //截取的样本图像像素高度 static int BW_MID = 100; //判断黑白的阀值 static int [][] LEFT_TABLE_INT = new int[] []{ {3,2,1,1},{2,2,2,1},{2,1,2,2},{1,4,1,1},{1,1,3,2}, {1,2,3,1},{1,1,1,4},{1,3,1,2},{1,2,1,3},{3,1,1,2}, {1,1,2,3},{1,2,2,2},{2,2,1,2},{1,1,4,1},{2,3,1,1}, {1,3,2,1},{4,1,1,1},{3,1,2,1},{3,1,2,1},{2,1,1,3} }; static int [][] RIGHT_TABLE_INT = new int[] []{ {3,2,1,1},{2,2,2,1},{2,1,2,2},{1,4,1,1},{1,1,3,2}, {1,2,3,1},{1,1,1,4},{1,3,1,2},{1,2,1,3},{3,1,1,2} }; /** * 测试 * @author tangkf * @param args */ public static void main(String[] args) { long a = System.currentTimeMillis(); String src = "f:/tm-01.jpg"; BufferedImage img = getImg(src); long b = System.currentTimeMillis(); System.err.println("读取图片耗时:"+(b-a)+" ms"); System.out.println("解码结果:"+getRealValue(img)); long c = System.currentTimeMillis(); System.err.println("图片解码耗时:"+(c-b)+" ms"); } /** * 根据条码图像区域返回条码数据 * @author tangkf * @param img 图像区域 * @return */ public static String getRealValue(BufferedImage img){ int w = img.getWidth(); int h = img.getHeight(); int y = (int)(h/2); if(y<HEIGHT){ return "无法识别的条码"; } if(h>HEIGHT*2) y= (int)(h/2)-(HEIGHT/2); else y = 1; StringBuffer valuestr = new StringBuffer(""); int chg = 0; //记录颜色变化次数 int gvn = 0; //记录实际取值次数(每变化一次取值一次) int ln = 0; //记录同色连续像素的数量 int uprv= -1; //记录上次的像素取值 int xdv = 0; //参考底色,取前8个像素的颜色作为底色参考值 int[] gvalue = new int[4]; //临时记录一组数字 for(int i=0;i<w;i++){ int v = 0; //记录取值为深色的次数 //高度取HEIGHT个像素,并求平均值,如果平均值为浅色,那么取0,否则取1 for(int j=y;j<y+HEIGHT;j++){ int prgb = getValue(img.getRGB(i,j)); xdv += prgb; if(prgb<BW_MID-80) v+=1; //累加该列为深色的像素个数 } int rv = 0; if(i<8){ //取参考颜色样本,默认都为0 rv = 0; BW_MID = xdv/((i+1)*(HEIGHT)); //不断变换参考样本值 }else{ rv = v>HEIGHT/2?1:0; //n个以上的像素为深色,就表示该列为深色 } if(rv==uprv){ ln++; }else{ chg++; //变化次数+1 if(chg>1 && chg<6){ //起始符号 gvalue[(chg-2)%4] = ln; if((chg-1)%4==0){ valuestr.append(getSTDValue(gvalue, LEFT_TABLE_INT)); //System.err.println(gvalue[0]+""+gvalue[1]+gvalue[2]+gvalue[3]); } } if((chg>5 && chg<30)|| (chg>34 && chg<59)){ gvn++; if(gvn%4==0){ gvalue[(gvn-1)%4] = ln; if(gvn<=24){ valuestr.append(getSTDValue(gvalue, LEFT_TABLE_INT)); //valuestr.append(ln+ " RV:"+ getSTDValue(gvalue, LEFT_TABLE_INT) + "\n"); }else{ valuestr.append(getSTDValue(gvalue, RIGHT_TABLE_INT)); //valuestr.append(ln+ " RV:"+ getSTDValue(gvalue, RIGHT_TABLE_INT) + "\n"); } }else { gvalue[(gvn-1)%4] = ln; } } ln = 1; } uprv = rv; } return valuestr.toString(); } /** * 由一组样本与基准表通过方差和的方式去噪求值 * @param mvalues * @param STDTABLE * @return */ public static int getSTDValue(final int[] mvalues,int[][] STDTABLE){ int rvalue = 0; int fx = 0; for(int i=0;i<STDTABLE.length;i++){ int[] svalues = LEFT_TABLE_INT[i]; //基准值 int[] xvalue = new int[4]; for(int j=0;j<mvalues.length;j++){ xvalue[j] = (int)((double)mvalues[j]/svalues[j]+0.5); } int tmp = getFX(xvalue); if(i==0){ fx = tmp; }else{ if(tmp<fx){ fx = tmp; rvalue = i>9?i-10:i; } } } return rvalue; } /** * 求一组样本数的方差和(针对所有样本数都相同的情况简化算法) * @param numlist */ public static int getFX(int[] numlist){ if(numlist.length<1) return 0; int fx = 0; double avg = 0; for(int i=0;i<numlist.length;i++){ avg += numlist[i]; } avg = (int)((double)avg/numlist.length+0.5); for(int i=0;i<numlist.length;i++){ fx += (numlist[i]-avg)*(numlist[i]-avg); } return fx; } /** * 由色彩值返回单色信号 1 或 0 * @param rgb * @return */ public static int getValue(int rgb){ int r = (rgb>>16)&255; int g = (rgb>>8)&255; int b = rgb&255; double avg = 0.299*r+0.587*g+0.114*b; //判断黑白 return (int)avg; } /** * 读取图片 * @param srcImg * @return */ public static BufferedImage getImg(String srcImg){ BufferedImage img = null; try { img = ImageIO.read(new File(srcImg)); } catch (IOException e) { e.printStackTrace(); } return img; } }