最近有個應用須要在服務端解析圖片中的二維碼,網上查了一下,幾乎全是用google zxing的實現。可是測試過程當中發現,這玩意比較坑,不少在微信里長按能識別的圖片,使用zxing卻識別不出來。 因而乎開始了糾結的過程!!!! 搞不懂爲啥網上一直沒解決方法~~~~~(雖然不少人在問!! 國外也有人在問!!!!),你們都是不求甚解,只能copy代碼?? 最終仍是本身動手吧!!android
給張測試圖算法
google了好久, 在stackoveflow上找到了一個描述!!大概是說「圖片不能超過某個大小」, 結果誤導了我!! 將2448x3264的圖片縮小到了1500x2000的尺寸,結果還真識別出來了。 因而乎我覺得問題解決了,識別時我加了一個超出尺寸縮放的邏輯!! 過了一天,再測試,發現仍是不少圖片識別不出來!! 看來歪果仁也不靠譜微信
private static BufferedImage toGrayImage(BufferedImage image) { BufferedImage result = image; if (BufferedImage.TYPE_BYTE_GRAY != image.getType()) { BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY); newImage.getGraphics().drawImage(image, 0, 0, null); result = newImage; } Raster raster = result.getRaster(); DataBufferByte buffer = (DataBufferByte) raster.getDataBuffer(); byte[] data = buffer.getData(); for (int i = 0; i < data.length; i++) { byte value = 0; if (data[i] < 32) { value = -1; } buffer.setElem(i, value); } return result; }
https://zxing.org/w/decode.jspx 這個網站能夠直接上傳圖片,再使用zxing提取結果。 發現這個網站也不行!!! 難道真的有問題? 而後嘗試使用其它的一些API,結果還真沒找出靠譜的API來。app
偶然間下載了一個使用zxing的android app,該app能夠調用攝像頭掃描二維碼。 而這個app掃描電腦上顯示的圖片是沒有問題的, 這間接證實了zxing多是沒有 問題的。 有了對的例子參照就好,照着改就好了。 而後看了一下Android調用攝像頭掃描的代碼,按照裏面的參數都調整了一下,仍是失敗 !!!! jsp
而後我將灰階處理過的黑白圖放大後仔細分析了一下,發現下面的圖片其實有不少「雜點」,而手機掃描時,這些「雜點」卻被模糊看不出來了。 ,思路出來了!!!! 須要將圖片裏面影響識別的「雜點」給模糊處理! 再聯想到stackoverflow上的一次成功例子,,模糊嘛,將圖片縮小不就模糊了。
工具
zxing在識別高清的手機圖時,將圖片逐步縮小,並掃描!! 就這麼簡單!!!!! 測試
下面是代碼,測試了好幾個圖片均可以識別的, 過程有些糾結哎!!!網站
/** * QR 二維碼工具。 * * @author liuyixin */ public class QRCodeUtil { private static final int MAX_QRCODE_SIXE = 1500; public static String readToString(BufferedImage sourceImage) { BufferedImage image = toGrayImage(sourceImage); if (sourceImage.getWidth() > MAX_QRCODE_SIXE && sourceImage.getHeight() > MAX_QRCODE_SIXE) {// second image = resizeToMaxSize(sourceImage); } String result = readDirectly(image); if (StringUtils.isNotBlank(result)) { return result; } int minSize = 170; int imgSize = Math.min(image.getWidth(), image.getHeight()); int level = 1; while (imgSize > minSize) { BufferedImage newImage = new BufferedImage((int) (image.getWidth() * Math.pow(0.9, level)), (int) (image.getHeight() * Math.pow(0.9, level)), image.getType()); newImage.getGraphics().drawImage(image, 0, 0, newImage.getWidth(), newImage.getHeight(), 0, 0, image.getWidth(), image.getHeight(), null); result = readDirectly(newImage); if (StringUtils.isNotBlank(result)) { return result; } imgSize = Math.min(newImage.getWidth(), newImage.getHeight()); level++; } return ""; } /** * 將圖片轉成灰階。 * * @param image * @return */ private static BufferedImage toGrayImage(BufferedImage image) { BufferedImage result = image; if (BufferedImage.TYPE_BYTE_GRAY != image.getType()) { BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY); newImage.getGraphics().drawImage(image, 0, 0, null); result = newImage; } /*黑白處理 Raster raster = result.getRaster(); DataBufferByte buffer = (DataBufferByte) raster.getDataBuffer(); byte[] data = buffer.getData(); for (int i = 0; i < data.length; i++) { byte value = 0; if (data[i] < 32) { value = -1; } buffer.setElem(i, value); }*/ return result; } /** * 圖片若過大,則縮放圖片。 * * @param image * @return */ private static BufferedImage resizeToMaxSize(BufferedImage image) { int height = MAX_QRCODE_SIXE; int width = MAX_QRCODE_SIXE; if (image.getWidth() > image.getHeight()) { width = (int) (height * (((double) image.getWidth()) / image.getHeight())); } else { height = (int) (width * (((double) image.getHeight()) / image.getWidth())); } BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); newImage.getGraphics().drawImage(image, 0, 0, newImage.getWidth() + 1, newImage.getHeight() + 1, 0, 0, image.getWidth(), image.getHeight(), null); return newImage; } private static String readDirectly(BufferedImage image) { LuminanceSource source = new BufferedImageLuminanceSource(image); Binarizer binarizer = new HybridBinarizer(source); BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer); Map<DecodeHintType, Object> hints = new HashMap<DecodeHintType, Object>(); hints.put(DecodeHintType.CHARACTER_SET, "UTF-8"); try { return new MultiFormatReader().decode(binaryBitmap, hints).getText(); } catch (NotFoundException e) { return ""; } } }