教你打开二维码和条形码的正确识别方式

今天老师给了张图片,里面有很多二维码和条形码,然后说不管大家用什么办法,试试看能不能用个代码尽可能多的把里面的二维码和条形码信息读出来!脑海突然闪现出之前做过的微信自动回复过程中自动生成的二维码。但似乎没太大关系(略略略),二维码不也是一张图片嘛,那就图像识别?通过解析来提取里面的信息??那啥今天去超市买快乐肥宅茶的时候,小姐姐滴一下扫了瓶子上的条形码电脑上就获取到了快乐肥宅茶的相关信息(编号,价格....),不也正是读取了条形码的信息嘛。

 首先,我是这样想的:图片其实就是一个二维数组,由无数个像素点构成,那么可以把每个像素点都读取出来,存储到一个文件当中,然后通过各种算法把它们解析成文件(这个过程就是把一堆0101字节解析出我们能看懂的字符数字等,就好像是把一种一看不懂的语言翻译成中文),然后就能获得二维码或条形码上面的信息了。重点是在解析的过程中,该如何解析呢?最快的办法,当然是赶快去看看哪个库可以用?

 

以下是解析代码,调用的是java中的QRCodeDecoder.jar接口

然后获取图片大小和像素点,必要时还可以将图片放大。



import java.awt.image.BufferedImage;  
import java.io.File;  
import java.io.IOException;  
  
import javax.imageio.ImageIO;  
  
import jp.sourceforge.qrcode.QRCodeDecoder;  
import jp.sourceforge.qrcode.data.QRCodeImage;  
import jp.sourceforge.qrcode.exception.DecodingFailedException;  
  
public class ErWeiMaJieMa {  
    /**  
     * 解码二维码  
     * @param imgPath  
     * @return String  
     */    
    public String decoderQRCode(String imgPath) {    
    
        // QRCode 二维码图片的文件    
        File imageFile = new File(imgPath);    
    
        BufferedImage bufImg = null;    
        String decodedData = null;    
        try {    
            bufImg = ImageIO.read(imageFile);    
    
            QRCodeDecoder decoder = new QRCodeDecoder();    
            decodedData = new String(decoder.decode(new J2SEImage(bufImg)));    
    
            // try {    
            // System.out.println(new String(decodedData.getBytes("gb2312"),    
            // "gb2312"));    
            // } catch (Exception e) {    
            // // TODO: handle exception    
            // }    
        } catch (IOException e) {    
            System.out.println("Error: " + e.getMessage());    
            e.printStackTrace();    
        } catch (DecodingFailedException dfe) {    
            System.out.println("Error: " + dfe.getMessage());    
            dfe.printStackTrace();    
        }    
        return decodedData;    
    }    
    
    /**  
     * @param args the command line arguments  
     */    
    public static void main(String[] args) {    
        ErWeiMaJieMa handler = new ErWeiMaJieMa();    
        String imgPath = "F:\\Desktop\\2.png";    
        String decoderContent = handler.decoderQRCode(imgPath);    
        System.out.println("解析结果如下:");    
        System.out.println(decoderContent);    
        System.out.println("========decoder success!!!");    
    }    
    
    class J2SEImage implements QRCodeImage {    
        BufferedImage bufImg;    
    
        public J2SEImage(BufferedImage bufImg) {    
            this.bufImg = bufImg;    
        }    
    //获取宽
        public int getWidth() {    
            return bufImg.getWidth();    
        }    
    //获取高
        public int getHeight() {    
            return bufImg.getHeight();    
        }    
    //获取像素点
        public int getPixel(int x, int y) {    
            return bufImg.getRGB(x, y);    
        }    
    
    }    
}  

 

图片:

 

 

我只截取了里面的一个二维码来解析,跑出来的结果:

 

其实可以打开QRCodeDecoder类中的decode()方法看,发现它也是把图片转成点来解析,最后返回一个byte类型的数组。

	public byte[] decode(QRCodeImage qrCodeImage) throws DecodingFailedException {
    //获取调整的点
		Point[] adjusts = getAdjustPoints();
		Vector results = new Vector();
    numTryDecode = 0;
		while (numTryDecode < adjusts.length) {
			try {
				DecodeResult result = decode(qrCodeImage, adjusts[numTryDecode]);
				if (result.isCorrectionSucceeded()) {
					return result.getDecodedBytes();
				}
				else {
					results.addElement(result);
					canvas.println("Decoding succeeded but could not correct");
					canvas.println("all errors. Retrying..");
				}
			} catch (DecodingFailedException dfe) {
				if (dfe.getMessage().indexOf("Finder Pattern") >= 0)
					throw dfe;
			} finally {
				numTryDecode += 1;
			}
		}
		
		if (results.size() == 0)
			throw new DecodingFailedException("Give up decoding");
		
		int minErrorIndex = -1;
		int minError = Integer.MAX_VALUE;
		for (int i = 0; i < results.size(); i++) {
			DecodeResult result = (DecodeResult)results.elementAt(i);
			if (result.getNumCorrectuionFailures() < minError) {
				minError = result.getNumCorrectuionFailures();
				minErrorIndex = i;
			}
		}
		canvas.println("All trials need for correct error");
		canvas.println("Reporting #" + (minErrorIndex)+" that,");
		canvas.println("corrected minimum errors (" +minError + ")");
		canvas.println("Decoding finished.");
		return ((DecodeResult)results.elementAt(minErrorIndex)).getDecodedBytes();
	}



	Point[] getAdjustPoints() {
		// note that adjusts affect dependently
		// i.e. below means (0,0), (2,3), (3,4), (1,2), (2,1), (1,1), (-1,-1)
		
//		Point[] adjusts = {new Point(0,0), new Point(2,3), new Point(1,1), 
//				new Point(-2,-2), new Point(1,-1), new Point(-1,0), new Point(-2,-2)};
		Vector adjustPoints = new Vector();
		for (int d = 0; d < 4; d++)
			adjustPoints.addElement(new Point(1, 1));
		int lastX = 0, lastY = 0;
		for (int y = 0; y > -4; y--) {
			for (int x = 0; x > -4; x--) {
				if (x != y && ((x+y) % 2 == 0)) {
					adjustPoints.addElement(new Point(x-lastX, y-lastY));
					lastX = x;
					lastY = y;
				}
			}
		}
		Point[] adjusts = new Point[adjustPoints.size()];
		for (int i = 0; i < adjusts.length; i++)
			adjusts[i] = (Point)adjustPoints.elementAt(i);
		return adjusts;
	}


DecodeResult decode(QRCodeImage qrCodeImage, Point adjust) 
		throws DecodingFailedException {
		try {
			if (numTryDecode == 0) {
				canvas.println("Decoding started");
                //将图片转为二维数组
				int[][] intImage = imageToIntArray(qrCodeImage);
                //读取图片
				imageReader = new QRCodeImageReader();
                //获取二维码特征
				qrCodeSymbol = imageReader.getQRCodeSymbol(intImage);
			} else {
				canvas.println("--");
				canvas.println("Decoding restarted #" + (numTryDecode));
				qrCodeSymbol = imageReader.getQRCodeSymbolWithAdjustedGrid(adjust);
			}
		} catch (SymbolNotFoundException e) {
			throw new DecodingFailedException(e.getMessage());
		}
		canvas.println("Created QRCode symbol.");
		canvas.println("Reading symbol.");
		canvas.println("Version: " + qrCodeSymbol.getVersionReference());		
		canvas.println("Mask pattern: " + qrCodeSymbol.getMaskPatternRefererAsString());
		// blocks contains all (data and RS) blocks in QR Code symbol
		int[] blocks = qrCodeSymbol.getBlocks();
		canvas.println("Correcting data errors.");
		// now blocks turn to data blocks (corrected and extracted from original blocks)
    blocks = correctDataBlocks(blocks);

		try {
			byte[] decodedByteArray = 
				getDecodedByteArray(blocks, qrCodeSymbol.getVersion(), qrCodeSymbol.getNumErrorCollectionCode());
			return new DecodeResult(decodedByteArray, numLastCorrectionFailures);
		} catch (InvalidDataBlockException e) {
			canvas.println(e.getMessage());
			throw new DecodingFailedException(e.getMessage());
		}
	}

将图片转为二维数组的函数:

int[][] imageToIntArray(QRCodeImage image) {
		int width = image.getWidth();
		int height = image.getHeight();
		int[][] intImage = new int[width][height];
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				intImage[x][y] = image.getPixel(x,y);
			}
		}
		return intImage;
	}

获取二维码特征的函数:大致思路是通过与样式校准来得到特征的。

public QRCodeSymbol getQRCodeSymbol(int[][] image) 
			throws SymbolNotFoundException {
		int longSide = (image.length < image[0].length) ? image[0].length : image.length;
		QRCodeImageReader.DECIMAL_POINT = 23 - QRCodeUtility.sqrt(longSide / 256);
		bitmap = filterImage(image);			
		canvas.println("Drawing matrix.");
		canvas.drawMatrix(bitmap);

		canvas.println("Scanning Finder Pattern.");
		FinderPattern finderPattern = null;
		try {
			finderPattern = FinderPattern.findFinderPattern(bitmap);
		} catch (FinderPatternNotFoundException e) {
			canvas.println("Not found, now retrying...");
			bitmap = applyCrossMaskingMedianFilter(bitmap, 5);
			canvas.drawMatrix(bitmap);
			try {
				finderPattern = FinderPattern.findFinderPattern(bitmap);
			} catch (FinderPatternNotFoundException e2) {
				throw new SymbolNotFoundException(e2.getMessage());
			} catch (VersionInformationException e2) {
				throw new SymbolNotFoundException(e2.getMessage());
			}
		} catch (VersionInformationException e) {
			throw new SymbolNotFoundException(e.getMessage());
		}


		canvas.println("FinderPattern at");
		String finderPatternCoordinates = 
			finderPattern.getCenter(FinderPattern.UL).toString() + 
			finderPattern.getCenter(FinderPattern.UR).toString() +
			finderPattern.getCenter(FinderPattern.DL).toString();
		canvas.println(finderPatternCoordinates);
 		int[] sincos = finderPattern.getAngle();
		canvas.println("Angle*4098: Sin " + Integer.toString(sincos[0]) + "  " + "Cos " + Integer.toString(sincos[1]));

		int version = finderPattern.getVersion();
		canvas.println("Version: " + Integer.toString(version));
		if (version < 1 || version > 40)
			throw new InvalidVersionException("Invalid version: " + version);
		
		AlignmentPattern alignmentPattern = null;
		try {
			alignmentPattern = AlignmentPattern.findAlignmentPattern(bitmap, finderPattern);
		} catch (AlignmentPatternNotFoundException e) {
			throw new SymbolNotFoundException(e.getMessage());
		}
		
		int matrixLength = alignmentPattern.getCenter().length;
		canvas.println("AlignmentPatterns at");
		for (int y = 0; y < matrixLength; y++) {
			String alignmentPatternCoordinates = "";
			for (int x = 0; x < matrixLength; x++) {
				alignmentPatternCoordinates += alignmentPattern.getCenter()[x][y].toString();
			}
			canvas.println(alignmentPatternCoordinates);
		}
		//for(int i = 0; i < 500000; i++) System.out.println("");

		canvas.println("Creating sampling grid.");
		//[TODO] need all-purpose method
		//samplingGrid = getSamplingGrid2_6(finderPattern, alignmentPattern);
		samplingGrid = getSamplingGrid(finderPattern, alignmentPattern);
		canvas.println("Reading grid.");
		boolean[][] qRCodeMatrix = null;
		try {
			qRCodeMatrix = getQRCodeMatrix(bitmap, samplingGrid);
		} catch (ArrayIndexOutOfBoundsException e) {
			throw new SymbolNotFoundException("Sampling grid exceeded image boundary");
		}
		//canvas.drawMatrix(qRCodeMatrix);
		return new QRCodeSymbol(qRCodeMatrix);
	}
	

我们只需要调用几个函数就能搞定的事情,其实是因为底层帮我们实现了很多的细节与处理过程。看似简单的图像识别与人脸识别应用程序,其实如果只是单纯的获取每个像素点来处理和识别,那么上亿的图片集是很难处理的。所以对于每一张图片的识别都需要计算得到特征值。

特征值分解可以得到特征值与特征向量,特征值表示的是这个特征到底有多重要,而特征向量表示这个特征是什么。不过,特征值分解也有很多的局限,比如说变换的矩阵必须是方阵。这时就要奇异值分解上场才能搞定了。

给你一副图像,要从图像库中得到匹配的图像,怎么弄?如果是两两做像素点比较是不可能完成的任务,耗时好空间。如果用其他特征点代替也许可以,但容易漏检吧。我们必须对图像数据的协方差矩阵进行降维,所以用到了PCA。

主成分分析(PrincipalComponents Analysis。即PCA,也称为K-L变换),是图像压缩中的一种最优正交变换。PCA用于统计特征提取构成了子空间法模式识别的基础。它从图像整体代数特征出发,基于图像的总体信息进行分类识别。PCA的核心思想是利用较少数量的特征对样本进行描述以达到降低特征空间维数的目的。(思考:较少数量的特征来描述会不会让识别不准确?其实是会的,所以这里也需要一定的权衡。)

而具体如何实现PCA呢?关键是特征值及相应特征向量的求取。matlab有个eig函数,opencv也有相应的函数,QR算法也可以用来求实对称矩阵的全部特征值和特征向量。

 

更多关于奇异值分解、PCA理论和OR算法实现可以参考下面文章:

特征值分解、奇异值分解、PCA概念整理

机器学习中的数学(5)-强大的矩阵奇异值分解(SVD)及其应用

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这里提供一种Python的实现方法,需要安装`cv2`和`pyzbar`两个库。 ```python import cv2 from pyzbar import pyzbar # 读取图像并解码二维码/条形码 def decode(image): # 转换为灰度图像 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 解码二维码/条形码 barcodes = pyzbar.decode(gray) # 返回解码结果 return barcodes # 计算二维码/条形码值的差 def calculate_difference(code1, code2): # 如果解码结果不一致,返回None if code1[0].data != code2[0].data: return None # 将解码结果转换为数值 value1 = int(code1[0].data) value2 = int(code2[0].data) # 计算差值并返回 return abs(value1 - value2) # 手工输入数值 manual_input = 1234 # 读取图像并解码二维码/条形码 cap = cv2.VideoCapture(0) codes = [] while len(codes) < 2: ret, frame = cap.read() cv2.imshow('frame', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break barcodes = decode(frame) for barcode in barcodes: codes.append(barcode) cap.release() cv2.destroyAllWindows() # 计算差值并与手工输入的数值比对 diff = calculate_difference(codes[0], codes[1]) if diff is not None: if diff == manual_input: print('一致') else: print('不一致') else: print('解码结果不一致') ``` 该代码通过摄像头读取两张二维码/条形码,并解码得到数值,然后计算两个数值的差,最后将差值与手工输入的数值进行比对。如果差值与手工输入的数值一致,则输出“一致”,否则输出“不一致”。注意,在使用摄像头读取图像时,需要先安装`opencv-python`库,并根据自己的摄像头设置正确的参数。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值