二维码:二维码识别
1. WEB项目实现二维码识别
1.1. 问题
一般实现二维码扫描都是app调用手机,或者调用微信的库即可。但是由于web项目直接调用app的库,而调用微信的扫一扫功能必须在微信上使用。因此要在web项目中实现二维码识别的话,就要自己重新搞一下了。不弄不知道,发现二维码识别涉及的东西还是蛮多的。
1.2. 实现思路
在WEB项目上面实现二维码识别思路,前端用h5实现调用手机摄像头拍照,然后将照片上传到后端。在后端将二维码图片识别,再将相关信息返回处理。
1.3. 前端
1.3.1. html
1 <!DOCTYPE html> 2 3 <html> 4 5 <head> 6 7 <meta charset="utf-8"> 8 9 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, 10 11 maximum-scale=1.0, minimum-scale=1.0"> 12 13 <title>二维码识别</title> 14 15 <style> 16 17 *{ margin: 0; padding: 0;} 18 19 </style> 20 21 </head> 22 23 <body> 24 25 <button id="Sys" type="button" class="layui-btn" style="margin-left: 22px;" > 26 27 <img style="width:26px;" alt="扫一扫" src="/images/erweima.png"> 扫一扫 28 29 </button> 30 31 </div> 32 33 </div> 34 35 <!-- </form> --> 36 37 </div> 38 39 40 41 <input type="file" id='p_image' accept="image/*" capture='camera' style="opacity: 0"/> 42 43 <img id="tempImage" src="" style="display:none;"/> 44 45 46 47 <script src="/jquery/jquery-1.11.3.js"></script> <!-- 你必须先引入jQuery1.8或以上版本 --> 48 49 <script type="text/javascript" src="/js/exif.js"></script> 50 51 <script type="text/javascript" src="/js/sys.js"></script> 52 53 </body> 54 55 </html>
1.3.2. sys.js
上传图片需要对图片进行压缩,转化成base64字符串
1 // 扫一扫 2 3 $("#Sys").on('click', function(){ 4 5 $("#p_image").click(); 6 7 }); 8 9 10 11 //处理扫一扫 12 13 $(function () { 14 15 $("#p_image").change(function (e) { 16 17 var file = e.currentTarget.files[0]; 18 19 20 21 //创建一个文件读取的工具类 22 23 var reader = new FileReader(); 24 25 //这里利用了闭包的特性,来保留文件名 26 27 (function (x) { 28 29 reader.onload = function (e) { 30 31 //调用压缩文件的方法,具体实现逻辑见下面 32 33 render(this.result, x); 34 35 } 36 37 })(file.name); 38 39 //告诉文件读取工具类读取那个文件 40 41 reader.readAsDataURL(file); 42 43 }); 44 45 46 47 }); 48 49 50 51 //压缩图片 52 53 function render(src) { 54 55 // 需要压缩的最大尺寸 56 57 var MAX_SIZE = 500; 58 59 //创建Image对象 60 61 var image = new Image(); 62 63 //图片方向角 64 65 var orientation = null; 66 67 image.src = src; 68 69 image.onload = function () { 70 71 var canvas = document.createElement("canvas"); 72 73 //获取2d画布 74 75 var ctx = canvas.getContext("2d"); 76 77 canvas.width = image.width; 78 79 canvas.height = image.height; 80 81 ctx.clearRect(0, 0, canvas.width, canvas.height); 82 83 //绘制图片 84 85 ctx.drawImage(image, 0, 0, image.width, image.height); 86 87 //获取照片方向角属性,用户旋转控制 88 89 EXIF.getData(image, function() { 90 91 EXIF.getAllTags(this); 92 93 orientation = EXIF.getTag(this,'Orientation'); 94 95 }); 96 97 98 99 //通过固定的宽高比压缩 100 101 //宽大于高的情况 102 103 if (image.width > MAX_SIZE && image.width >= image.height) { 104 105 image.height *= MAX_SIZE / image.width; 106 107 image.width = MAX_SIZE; 108 109 } else if (image.height > MAX_SIZE && image.height > image.width) {//宽小于高的情况 110 111 image.width *= MAX_SIZE / image.height; 112 113 image.height = MAX_SIZE; 114 115 } 116 117 canvas.width = image.width; 118 119 canvas.height = image.height; 120 121 ctx.clearRect(0, 0, canvas.width, canvas.height); 122 123 //绘制图片 124 125 ctx.drawImage(image, 0, 0, image.width, image.height); 126 127 //生成base64码 128 129 var base64Code = canvas.toDataURL("image/png"); 130 131 $("#tempImage").attr("src", base64Code); 132 133 }; 134 135 136 137 138 139 var img = document.getElementById('tempImage'); 140 141 img.onload = function(){ 142 143 var canvas = document.createElement("canvas"); 144 145 //获取2d画布 146 147 var ctx = canvas.getContext("2d"); 148 149 canvas.width = img.width; 150 151 canvas.height = img.height; 152 153 ctx.clearRect(0, 0, canvas.width, canvas.height); 154 155 //绘制图片 156 157 ctx.drawImage(img, 0, 0, img.width, img.height); 158 159 //如果方向角不为1,都需要进行旋转 added by lzk 160 161 if(orientation != "" && orientation != 1){ 162 163 switch(orientation){ 164 165 case 6://需要顺时针(向左)90度旋转 166 167 rotateImg(img,'left',canvas); 168 169 break; 170 171 case 8://需要逆时针(向右)90度旋转 172 173 rotateImg(img,'right',canvas); 174 175 break; 176 177 case 3://需要180度旋转 178 179 rotateImg(img,'right',canvas);//转两次 180 181 rotateImg(img,'right',canvas); 182 183 break; 184 185 } 186 187 } 188 189 var base64Code = canvas.toDataURL("image/png"); 190 191 //$("#myImage1").attr("src", base64Code); 192 193 //调用上传图片方法 194 195 send(base64Code); 196 197 } 198 199 200 201 } 202 203 //上传图片 204 205 function send(baseData){ 206 207 var index = layer.load(1, {time: 15*1000}); //加载提示弹窗,并且设定最长等待15秒 208 209 $.ajax({ 210 211 url: '/scanFile.action', 212 213 type: 'post', 214 215 data: {"baseData":baseData}, 216 217 dataType: 'json', 218 219 success: function (data) { 220 221 if(data.resultcode == "success"){ 222 223 $("#FIRMNAME").val(data["ENTERPRISENAME"]); 224 225 $("#USCCODE").val(data["USCCODE"]); 226 227 layer.close(index); 228 229 } else { 230 231 layer.msg(data["resultcontent"]); 232 233 layer.close(index); 234 235 } 236 237 } 238 239 }); 240 241 } 242 243 //旋转图片 244 245 function rotateImg(img, direction,canvas) { 246 247 //最小与最大旋转方向,图片旋转4次后回到原方向 248 249 var min_step = 0; 250 251 var max_step = 3; 252 253 if (img == null)return; 254 255 //img的高度和宽度不能在img元素隐藏后获取,否则会出错 256 257 var height = img.height; 258 259 var width = img.width; 260 261 var step = 2; 262 263 if (step == null) { 264 265 step = min_step; 266 267 } 268 269 if (direction == 'right') { 270 271 step++; 272 273 //旋转到原位置,即超过最大值 274 275 step > max_step && (step = min_step); 276 277 } else { 278 279 step--; 280 281 step < min_step && (step = max_step); 282 283 } 284 285 //旋转角度以弧度值为参数 286 287 var degree = step * 90 * Math.PI / 180; 288 289 var ctx = canvas.getContext('2d'); 290 291 switch (step) { 292 293 case 0: 294 295 canvas.width = width; 296 297 canvas.height = height; 298 299 ctx.drawImage(img, 0, 0); 300 301 break; 302 303 case 1: 304 305 canvas.width = height; 306 307 canvas.height = width; 308 309 ctx.rotate(degree); 310 311 ctx.drawImage(img, 0, -height); 312 313 break; 314 315 case 2: 316 317 canvas.width = width; 318 319 canvas.height = height; 320 321 ctx.rotate(degree); 322 323 ctx.drawImage(img, -width, -height); 324 325 break; 326 327 case 3: 328 329 canvas.width = height; 330 331 canvas.height = width; 332 333 ctx.rotate(degree); 334 335 ctx.drawImage(img, -width, 0); 336 337 break; 338 339 } 340 341 }
1.4. 后端解析二维码
1.4.1. java解析二维码
使用Zxing.jar直接对二维码图片进行解析。
1 import java.awt.image.BufferedImage; 2 3 import java.io.ByteArrayOutputStream; 4 5 import java.io.File; 6 7 import java.io.IOException; 8 9 import java.util.HashMap; 10 11 import java.util.Map; 12 13 14 15 import javax.imageio.ImageIO; 16 17 18 19 import com.google.zxing.BarcodeFormat; 20 21 import com.google.zxing.BinaryBitmap; 22 23 import com.google.zxing.ChecksumException; 24 25 import com.google.zxing.DecodeHintType; 26 27 import com.google.zxing.EncodeHintType; 28 29 import com.google.zxing.FormatException; 30 31 import com.google.zxing.MultiFormatWriter; 32 33 import com.google.zxing.NotFoundException; 34 35 import com.google.zxing.Result; 36 37 import com.google.zxing.WriterException; 38 39 import com.google.zxing.client.j2se.BufferedImageLuminanceSource; 40 41 import com.google.zxing.client.j2se.MatrixToImageWriter; 42 43 import com.google.zxing.common.BitMatrix; 44 45 import com.google.zxing.common.HybridBinarizer; 46 47 import com.google.zxing.qrcode.QRCodeReader; 48 49 import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; 50 51 52 53 /** 54 55 * 二维码工具类 56 57 * @author limingcheng 58 59 * 60 61 */ 62 63 public class QrCodeUtil { 64 65 66 67 /** 68 69 * 生成一个二维码图片 70 71 * @param width 72 73 * @param height 74 75 * @param content 76 77 * @return 78 79 * @throws WriterException 80 81 * @throws IOException 82 83 */ 84 85 public static byte[] createQRCode(int width, int height, String content) throws WriterException, IOException { 86 87 // 二维码基本参数设置 88 89 Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>(); 90 91 hints.put(EncodeHintType.CHARACTER_SET, "utf-8");// 设置编码字符集utf-8 92 93 hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);// 设置纠错等级L/M/Q/H,纠错等级越高越不易识别,当前设置等级为最高等级H 94 95 hints.put(EncodeHintType.MARGIN, 0);// 可设置范围为0-10,但仅四个变化0 1(2) 3(4 5 6) 7(8 9 10) 96 97 // 生成图片类型为QRCode 98 99 BarcodeFormat format = BarcodeFormat.QR_CODE; 100 101 // 创建位矩阵对象 102 103 BitMatrix bitMatrix = new MultiFormatWriter().encode(content, format, width, height, hints); 104 105 // 设置位矩阵转图片的参数 106 107 // MatrixToImageConfig config = new MatrixToImageConfig(Color.black.getRGB(), Color.white.getRGB()); 108 109 // 位矩阵对象转流对象 110 111 ByteArrayOutputStream os = new ByteArrayOutputStream(); 112 113 MatrixToImageWriter.writeToStream(bitMatrix, "png", os); 114 115 return os.toByteArray(); 116 117 } 118 119 120 121 /** 122 123 * 解析二维码 124 125 * @throws FormatException 126 127 * @throws ChecksumException 128 129 * @throws NotFoundException 130 131 * @throws IOException 132 133 */ 134 135 public static void parsingQrCode() throws NotFoundException, ChecksumException, FormatException, IOException { 136 137 QRCodeReader formatReader = new QRCodeReader(); 138 139 BufferedImage image; 140 141 File file = new File("E:\\bestme.png"); 142 143 // 识别图片中的二维码内容 144 145 image = ImageIO.read(file); 146 147 // image = ImageUtil.binarization(image); 148 149 BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(image))); 150 151 152 153 //定义二维码参数 154 155 HashMap hints = new HashMap(); 156 157 // 解码设置编码方式为:utf-8 158 159 hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); 160 161 // 优化精度 162 163 hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); 164 165 //复杂模式,开启PURE_BARCODE模式 166 167 // hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE); 168 169 Result result = formatReader.decode(binaryBitmap,hints); 170 171 172 173 System.out.println("解析结果"+result.toString()); 174 175 System.out.println("二维码类型"+result.getBarcodeFormat()); 176 177 System.out.println("二维码内容"+result.getText()); 178 179 } 180 181 182 183 public static void main(String[] args) throws WriterException, IOException, NotFoundException, ChecksumException, FormatException { 184 185 parsingQrCode(); 186 187 188 189 // byte[] b = createQRCode(100, 100, "遇见最好的自己!"); 190 191 // OutputStream os = new FileOutputStream("E:\\bestme.png"); 192 193 // os.write(b); 194 195 // os.close(); 196 197 } 198 199 }
输出结果:
解析结果遇见最好的自己!
二维码类型QR_CODE
二维码内容遇见最好的自己!
注意:使用复杂模式会导致图片识别不了,具体原因暂没清晰。