二维码的识别
这种自己琢磨的真的识别率很低啊,还不如去找个百度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);
}
}
但是说真的 还是不太行的样子