二维码识别(zxing)java实现(差强人意的实现)

本文详细介绍了使用ZXing库解析二维码的过程,包括下载依赖、解析清晰和模糊二维码的步骤。针对模糊二维码,通过ORCodeImageUtil进行二值化、腐蚀和膨胀处理以提高识别率。然而,对于极度模糊的二维码,作者建议使用百度API,按次付费进行识别。
摘要由CSDN通过智能技术生成

这种自己琢磨的真的识别率很低啊,还不如去找个百度api,按次数花钱0.003一次识别。

(以jdk1.8为例)

zxing 包下载地址

地址:
https://repo1.maven.org/maven2/com/google/zxing/
版本选择:jdk1.6 core2.2 下载可用
jdk1.8 core 3.3.3下载可用

(下载的时候只要下载 core 3.3.3.jar 文件添加到项目依赖里面去就行,但是我还是推荐下载core-3.3.3-javadoc.jar 文件,可以看官方的注释, core-3.3.3-javadoc.jar 查看方法,直接用解压文件解压到文件夹之后,点开index.html就可以查看了,至于源码什么的就根据自己的需求看是否下载了)

解析清晰二维码

根据index.htm里面的介绍找到QRCodeReader这个类,明显就是我们需要的用来解析二维码的类,于是翻看文档,我们得出了几个重要的方法如下所示。

         //zxing 文档中找来的方法
         //Binarizer的构造方法Binarizer(LuminanceSource source)  
        // 而LuminanceSource 是一个抽象类 
         Binarizer binarizer = new HybridBinarizer(source);
	     BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
	     QRCodeReader reader=new QRCodeReader();
	     Result result = reader.decode(binaryBitmap, null); //获取结果类
	   String text=result.getText(); //获取二维码内容
	     

ImageLuminanceSource实现

所以接下来我们要去创建一个类ImageLuminanceSource 去继承LuminanceSource

package test;

import java.awt.image.BufferedImage;

import com.google.zxing.LuminanceSource;

public class ImageLuminanceSource extends LuminanceSource{
	
	  private final BufferedImage image;
	  
	  
	  public  ImageLuminanceSource(BufferedImage image,  int width,int height){
		  super(width, height);
		  this.image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); //图像要变成灰度的,因为getRow这个方法取的值必须是0-255之间
		  this.image.getGraphics().drawImage(image, 0, 0, null);

		//  输出处理后结果图
//		  if("D:\\test\\gery.png"!=null) {
//		       File file = new File("D:\\test\\gery.png");
//		       try {
//		      	 ImageIO.write(image, "png", file);
//		  	} catch (FileNotFoundException e) {
//		  		// TODO Auto-generated catch block
//		  		e.printStackTrace();
//		  	} catch (IOException e) {
//		  		// TODO Auto-generated catch block
//		  		e.printStackTrace();
//		  	}
//		     }
	  }
  

	
   //要求是返回一个以行为主的数组
	@Override
	public byte[] getMatrix() { //
		// TODO Auto-generated method stub
		 int width = getWidth();
	     int height = getHeight();
	     int area = width * height;
	     byte[] matrix = new byte[area];
	     image.getRaster().getDataElements(0, 0, width, height, matrix);
	     return matrix;
	}

	
	
	
	//要求是返回图片中某一行的亮度数据
	@Override
	public byte[] getRow(int y, byte[] row) {
		//官方的注释
		//从底层平台的位图中获取一行亮度数据。取值范围为0(黑色)到255(白色)。因为Java没有unsigned字节类型,所以调用者必须按位计算,并且每个值都使用0xff。
		//这个方法的实现最好是只获取这一行而不是整个图像,因为不可能安装2D reader,也不可能调用getMatrix()。
		//y - The row to fetch, which must be in [0,getHeight())
		//row - An optional preallocated array. If null or too small, it will be ignored. Always use the returned object, and ignore the .length of the array.
		
		 if (y < 0 || y >= getHeight()) {
		       throw new IllegalArgumentException("不在图片的高度范围之内: " + y);
		  }
		 int width = getWidth();
	     if (row == null || row.length < width) {
	       row = new byte[width];
	     }
	     
	     image.getRaster().getDataElements(0,0+y, width, 1, row);//返回图片规定矩形的像素数据,然后getRow需要的是一行的,所以高度是1。所以整句话的意思是从标点 0,y开始读取一像素高度的矩形,这不就是一行吗
	     
	     return row;
	
	}
	  
	  
	  


}


完整的调用方法

public class testBarCode {
	
	
	public static void main(String[] args) {
	
		QRReader();
	}

public static void QRReader() {
		
		try {
		 File file = new File("D:\\test\\4.png");
	     BufferedImage image = ImageIO.read(file);
	     ImageLuminanceSource source=new ImageLuminanceSource(image,image.getWidth(),image.getHeight());
	     Binarizer binarizer = new HybridBinarizer(source);
	     BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
	     QRCodeReader reader=new QRCodeReader();
	     
		  Map<DecodeHintType,Object> hints = new LinkedHashMap<DecodeHintType,Object>();
			// 解码设置编码方式为:utf-8,
			hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
			//优化精度
			hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
			//复杂模式,开启PURE_BARCODE模式
			//hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
		
		 Result result = reader.decode(binaryBitmap, null);
		String text=result.getText();
		System.out.println("解析的结果: " + result.getText());
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}catch(NotFoundException e) {
			System.out.println("二维码没找到");
		}catch (FormatException  e)
		{ System.out.println("二维码无法解析");
	
		}catch(ChecksumException e) {
			System.out.println("二维码纠错失败");
			System.out.println(e.toString());
		}
			
	}
}

在这里插入图片描述

代码运行结果
解析的结果: 12

稍微模糊的二维码

上面的示例,针对的是非常清晰的二维码,但是通常情况下,我们要识别的是纸上拍出来的二维码,如图,这个图是手机拍照的,然后又在电脑上进行缩放之后的截图,这种就比较困难了
在这里插入图片描述

新增ORCodeImageUtil

用上面的代码测试,就会发现连二维码都找不到了,这时候就要对传入的图像做一些处理,新增ORCodeImageUtil方法,根据自定义的颜色,以这个颜色作为分界线,比这个白就是白,比这个黑就是黑。

public class ORCodeImageUtil {
	
	static int splitColor = 0xFF7f7f7f;
	
	public static BufferedImage blackAndWhite(BufferedImage  image) {
		for (int y = 0; y < image.getHeight(); y++) {
		       for (int x = 0; x < image.getWidth(); x++) { //读取每个像素点颜色
		    	   //System.out.print(image.getRGB(x, y));
		         if (image.getRGB(x, y) >splitColor) { //我以0xFF700000 颜色为分界线,比这黑当作黑,比这个白当作白
		           image.setRGB(x, y, 0xFFFFFFFF); // = 白
		         }else {
		        	 image.setRGB(x, y, 0xFF000000); // = 黑
		         }
		       }
		     }
		
		return image;
		
	}

}

完整的调用

public class testBarCode {
	
	
	public static void main(String[] args) {
	
		QRReader();
	}

public static void QRReader() {
		
		try {
		 File file = new File("D:\\test\\4.png");
	     BufferedImage image = ImageIO.read(file);
	     ImageLuminanceSource source=new ImageLuminanceSource(ORCodeImageUtil.blackAndWhite(image),image.getWidth(),image.getHeight()); //图像处理之后 在传入
	     Binarizer binarizer = new HybridBinarizer(source);
	     BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
	     QRCodeReader reader=new QRCodeReader();
	     
		  Map<DecodeHintType,Object> hints = new LinkedHashMap<DecodeHintType,Object>();
			// 解码设置编码方式为:utf-8,
			hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
			//优化精度
			hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
			//复杂模式,开启PURE_BARCODE模式
			//hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
		
		 Result result = reader.decode(binaryBitmap, null);
		String text=result.getText();
		System.out.println("解析的结果: " + result.getText());
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}catch(NotFoundException e) {
			System.out.println("二维码没找到");
		}catch (FormatException  e)
		{ System.out.println("二维码无法解析");
	
		}catch(ChecksumException e) {
			System.out.println("二维码纠错失败");
			System.out.println(e.toString());
		}
			
	}
}

手机直接拍照出来的图
在这里插入图片描述

在这里插入图片描述
可以识别

还不行的

借鉴 增加了腐蚀和膨胀
https://blog.csdn.net/StrangeSir/article/details/88384585

ORCodeImageUtil 新增腐蚀和膨胀

package test;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;

public class ORCodeImageUtil {
	
	static int splitColor = 0xFF7f7f7f;	
	
	//进行二值化
	public static BufferedImage blackAndWhite(BufferedImage  image) {
		for (int y = 0; y < image.getHeight(); y++) {
		       for (int x = 0; x < image.getWidth(); x++) { //读取每个像素点颜色
		    	   //System.out.print(image.getRGB(x, y));
		         if (image.getRGB(x, y) >splitColor) { //我以0xFF700000 颜色为分界线,比这黑当作黑,比这个白当作白
		           image.setRGB(x, y, 0xFFFFFFFF); // = 白
		         }else {
		        	 image.setRGB(x, y, 0xFF000000); // = 黑
		         }
		       }
		     }
		
		return image;
		
	}
	
	
	
	/**
	 * 二值化后的图像的开运算:先腐蚀再膨胀(用于去除图像的小黑点)
	 * 
	 * @param filePath 要处理的图片路径
	 * @param fileOutputPath 处理后的图片输出路径
	 * @throws IOException
	 */
	public static BufferedImage opening(BufferedImage  bi) throws IOException {

		// 获取当前图片的高,宽,ARGB
		int h = bi.getHeight();
		int w = bi.getWidth();
		int arr[][] = new int[w][h];
		// 获取图片每一像素点的灰度值
		for (int i = 0; i < w; i++) {
			for (int j = 0; j < h; j++) {
				// getRGB()返回默认的RGB颜色模型(十进制)
				arr[i][j] = getImageGray(bi.getRGB(i, j));// 该点的灰度值
			}
		}
		
		int black = new Color(0, 0, 0).getRGB();
		int white = new Color(255, 255, 255).getRGB();
		BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);
		// 临时存储腐蚀后的各个点的亮度
		int temp[][] = new int[w][h];
		// 1.先进行腐蚀操作
		for (int i = 0; i < w; i++) {
			for (int j = 0; j < h; j++) {
				/*
				 * 为0表示改点和周围8个点都是黑,则该点腐蚀操作后为黑
				 * 由于公司图片态模糊,完全达到9个点全为黑的点太少,最后效果很差,故改为了小于30
				 * (写30的原因是,当只有一个点为白,即总共255,调用getGray方法后得到255/9 = 28)
				 */
				if (getGray(arr, i, j, w, h) ==0) {
					temp[i][j] = 0;
				} else{
					temp[i][j] = 255;
				}
			}
		}
		
		// 2.再进行膨胀操作
		for (int i = 0; i < w; i++) {
			for (int j = 0; j < h; j++) {
				bufferedImage.setRGB(i, j, white);
			}
		}
		
		
		
		for (int i = 0; i < w; i++) {
			for (int j = 0; j < h; j++) {
				// 为0表示改点和周围8个点都是黑,则该点腐蚀操作后为黑
				if (temp[i][j] == 0) {
					bufferedImage.setRGB(i, j, black);
					if(i > 0) {
						bufferedImage.setRGB(i-1, j, black);
					}
					if (j > 0) {
						bufferedImage.setRGB(i, j-1, black);
					}
					if (i > 0 && j > 0) {
						bufferedImage.setRGB(i-1, j-1, black);
					}
					if (j < h-1) {
						bufferedImage.setRGB(i, j+1, black);
					}
					if (i < w-1) {
						bufferedImage.setRGB(i+1, j, black);
					}
					if (i < w-1 && j > 0) {
						bufferedImage.setRGB(i+1, j-1, black);
					}
					if (i < w-1 && j < h-1) {
						bufferedImage.setRGB(i+1, j+1, black);
					}
					if (i > 0 && j < h-1) {
						bufferedImage.setRGB(i-1, j+1, black);
					}
				}
			}
		}
		
		return bufferedImage;
		
	}

	
	
	/**
	 * 自己加周围8个灰度值再除以9,算出其相对灰度值
	 * 
	 * @param gray
	 * @param x 要计算灰度的点的横坐标
	 * @param y 要计算灰度的点的纵坐标
	 * @param w 图像的宽度
	 * @param h 图像的高度
	 * @return
	 */
	public static int getGray(int gray[][], int x, int y, int w, int h) {
		int rs = gray[x][y] + (x == 0 ? 255 : gray[x - 1][y]) + (x == 0 || y == 0 ? 255 : gray[x - 1][y - 1])
				+ (x == 0 || y == h - 1 ? 255 : gray[x - 1][y + 1]) + (y == 0 ? 255 : gray[x][y - 1])
				+ (y == h - 1 ? 255 : gray[x][y + 1]) + (x == w - 1 ? 255 : gray[x + 1][y])
				+ (x == w - 1 || y == 0 ? 255 : gray[x + 1][y - 1])
				+ (x == w - 1 || y == h - 1 ? 255 : gray[x + 1][y + 1]);
		return rs / 9;
	}
	
	
	/**
	 * 图像的灰度处理
	 * 利用浮点算法:Gray = R*0.3 + G*0.59 + B*0.11;
	 * 
	 * @param rgb 该点的RGB值
	 * @return 返回处理后的灰度值
	 */
	private static int getImageGray(int rgb) {
		String argb = Integer.toHexString(rgb);// 将十进制的颜色值转为十六进制
		// argb分别代表透明,红,绿,蓝 分别占16进制2位
		int r = Integer.parseInt(argb.substring(2, 4), 16);// 后面参数为使用进制
		int g = Integer.parseInt(argb.substring(4, 6), 16);
		int b = Integer.parseInt(argb.substring(6, 8), 16);
		int gray = (int) (r*0.3 + g*0.59 + b*0.11);
		return gray;
	}


}

调用

public class testBarCode {
	
	
	public static void main(String[] args) {
	
		QRReader();
	}
	

	
	
	public static void QRReader() {
		
		try {
		 File file = new File("D:\\test\\113.jpg");
	     BufferedImage image = ImageIO.read(file);
	  
	  
	    // image =getSubImage(image);
	 
	   image= ORCodeImageUtil.blackAndWhite(image);
	   image= ORCodeImageUtil.opening(image);
	    
	     
	     ImageLuminanceSource source=new ImageLuminanceSource(image,image.getWidth(),image.getHeight());
	     Binarizer binarizer = new HybridBinarizer(source);
	     BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
	     QRCodeReader reader=new QRCodeReader();
	     
		  Map<DecodeHintType,Object> hints = new LinkedHashMap<DecodeHintType,Object>();
			// 解码设置编码方式为:utf-8,
			hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
			//优化精度
			hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
			//复杂模式,开启PURE_BARCODE模式
			//hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
		
		 Result result = reader.decode(binaryBitmap, null);
		String text=result.getText();
		System.out.println("解析的结果: " + result.getText());
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}catch(NotFoundException e) {
			System.out.println("二维码没找到");
		}catch (FormatException  e)
		{ System.out.println("二维码无法解析");
	
		}catch(ChecksumException e) {
			System.out.println("二维码纠错失败");
			System.out.println(e.toString());
		}
			
	}
	
	public static void QRReaderWithResize() {
		try {
			 File file = new File("D:\\test\\5.png");
		     BufferedImage image = ImageIO.read(file);
		    String str= QRCodeUtil.readToString(image);
		    System.out.println("输出"+str);
		     
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
	
	public static BufferedImage getSubImage(BufferedImage image) {
		     int x=0;
		     int y=220;
		     
		     int w=400;
		     int h=500;
		  if(x>image.getWidth()) {
			  x=image.getWidth();
		  }
		  if(y>image.getHeight()) {
			  y=image.getHeight();
		  }
		     
		  if(w>image.getWidth()-x) {
			  w=image.getWidth()-x;
		  }
		  if(h>image.getHeight()-y) {
			  h=image.getHeight()-y;
		  }
		  
		  return image.getSubimage(x,y,w,h);
	}
}

但是说真的 还是不太行的样子

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值