首先,我們來看看zxing一些基本介紹。
ZXing是一個開放源碼的,用Java實現的多種格式的1D(注1d條碼主要常見的條碼)
/2D條碼(主要是二維碼)
圖像處理庫,它包含了聯系到其他語言的端口。Zxing可以實現使用手機的內置的攝像頭完成條形碼的掃描及解碼。該項目可實現的條形碼編碼和解碼。我們目前支持以下格式:
package com.easyoa.test; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.Hashtable; import javax.imageio.ImageIO; import com.google.zxing.BarcodeFormat; import com.google.zxing.BinaryBitmap; import com.google.zxing.DecodeHintType; import com.google.zxing.LuminanceSource; import com.google.zxing.MultiFormatReader; import com.google.zxing.MultiFormatWriter; import com.google.zxing.Reader; import com.google.zxing.ReaderException; import com.google.zxing.Result; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.ByteMatrix; import com.google.zxing.common.HybridBinarizer; public class Test { private static final int BLACK = 0xff000000; private static final int WHITE = 0xFFFFFFFF; /** * @param args */ public static void main(String[] args) { Test test=new Test(); test.encode(); test.decode(); } //編碼 /** * 在編碼時需要將com.google.zxing.qrcode.encoder.Encoder.java中的 * static final String DEFAULT_BYTE_MODE_ENCODING = "ISO8859-1";修改為UTF-8,否則中文編譯后解析不了 */ public void encode(){ try { String str = "姓名:曾馳文,性別:男,年齡:27,籍貫:湖南長沙,";// 二維碼內容 String path = "D://test.png"; ByteMatrix byteMatrix; byteMatrix= new MultiFormatWriter().encode(str, BarcodeFormat.QR_CODE, 200, 200); File file = new File(path); writeToFile(byteMatrix, "png", file); } catch (Exception e) { e.printStackTrace(); } } public static void writeToFile(ByteMatrix matrix, String format, File file) throws IOException { BufferedImage image = toBufferedImage(matrix); ImageIO.write(image, format, file); } public static BufferedImage toBufferedImage(ByteMatrix matrix) { int width = matrix.getWidth(); int height = matrix.getHeight(); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { image.setRGB(x, y, matrix.get(x, y) == 0 ? BLACK:WHITE); } } return image; } //解碼 public void decode(){ try{ Reader reader = new MultiFormatReader(); String imgPath = "D://test.png"; File file = new File(imgPath); BufferedImage image; try { image = ImageIO.read(file); if (image == null) { System.out.println("Could not decode image"); } LuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); Result result; Hashtable hints= new Hashtable(); hints.put(DecodeHintType.CHARACTER_SET, "utf-8"); //解碼設置編碼方式為:utf-8, result = new MultiFormatReader().decode(bitmap,hints); String resultStr = result.getText(); System.out.println("解析后內容:"+resultStr); } catch (IOException ioe) { System.out.println(ioe.toString()); } catch (ReaderException re) { System.out.println(re.toString()); } }catch(Exception ex){ System.out.println(ex.toString()); } } }
通過代碼,我們可以得出下列的結論:
為了更好的生成相應的二維碼,我們需要將相應的二維碼內容轉換成相應的流對象,將流對象轉換成相應的圖片,這圖片是不同部分變成黑白的圖片。
相應的解析的結果是:姓名:曾馳文,性別:男,年齡:27,籍貫:湖南長沙,
解析二維碼
下面是 二維碼從圖片解析內容的分析與實現
解碼的流程大致分成以下幾個步驟:
1:獲取攝像頭byte[] data
2:對數據進行解析
在zxing客戶端源碼中
PreviewCallback 攝像頭回調 data就是出自這里
PlanarYUVLuminanceSource 繼承與LuminanceSource不同的數據原 YUV RGB
RGBLuminanceSource
AutoFocusCallback 自動對焦。不能自動對焦的手機zxing就不能發威了(這個處理相應的攝像頭的過程中,在android系統下,由於是調用硬件設備,往往系統調度無法處理,從而實現后退鍵反映不及時的結果)
CameraManager 攝像頭管理類。打開,關閉
DecodeThread 線程管理主要利用到了CountDownLatch
DecodeHandler 數據傳輸中樞。我理解DecodeThread控制線程,DecodeHandler發送數據
DecodeFormatManager 這個配置解碼格式。一維碼,二維碼等
CaptureActivityHandler 這個是解碼與avtivity中介。解碼成功,失敗都用她回調
ViewfinderView 我們看到的掃描框,搞花樣就從她入手
同樣,我們來看看源代碼:
public class DecodeImageHandler { private static final String TAG = DecodeImageHandler.class.getSimpleName(); // 解碼格式 private MultiFormatReader multiFormatReader; private static final String ISO88591 = "ISO8859_1"; // private Context mContext; public DecodeImageHandler(Context context) { // 解碼的參數 Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(2); // 能解析的編碼類型 和 解析時使用的編碼。 Vector<BarcodeFormat> decodeFormats = new Vector<BarcodeFormat>(); decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS); decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS); decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS); hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats); hints.put(DecodeHintType.CHARACTER_SET, ISO88591); init(context, hints); } public DecodeImageHandler(Context context, Hashtable<DecodeHintType, Object> hints) { init(context, hints); } private void init(Context context, Hashtable<DecodeHintType, Object> hints) { multiFormatReader = new MultiFormatReader(); multiFormatReader.setHints(hints); // mContext = context; } public Result decode(Bitmap bitmap) { // 首先,要取得該圖片的像素數組內容 int width = bitmap.getWidth(); int height = bitmap.getHeight(); //-------------------------------------------------- //rgb模式 int[] data = new int[width * height]; bitmap.getPixels(data, 0, width, 0, 0, width, height); Result rgbResult = rgbModeDecode(data, width, height); if (rgbResult != null) { data = null; return rgbResult; } //---------------------------------------------------- //yuv byte[] bitmapPixels = new byte[width * height]; bitmap.getPixels(data, 0, width, 0, 0, width, height); // 將int數組轉換為byte數組 for (int i = 0; i < data.length; i++) { bitmapPixels[i] = (byte) data[i]; } // ByteArrayOutputStream baos = new ByteArrayOutputStream(); // bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); Result yuvResult = yuvModeDecode(bitmapPixels, width, height); bitmapPixels = null; return yuvResult; } // public Result decode(String path) throws IOException { // // 解析圖片高和寬 // BitmapFactory.Options options = new BitmapFactory.Options(); // options.inJustDecodeBounds = true; // BitmapFactory.decodeFile(path, options); // // //從圖片直接獲取byte[] // File file = new File(path); // FileInputStream is = new FileInputStream(file); // ByteArrayOutputStream os = new ByteArrayOutputStream(); // int len = -1; // byte[] buf = new byte[512]; // while ((len = is.read(buf)) != -1) { // os.write(buf, 0, len); // } // //關閉流 // try { // is.close(); // } finally { // if (is != null) { // is.close(); // } // } // // //解析 // return decode(os.toByteArray(), options.outWidth, options.outHeight); // } public Result rgbModeDecode(int[] data, int width, int height) { Result rawResult = null; RGBLuminanceSource source = new RGBLuminanceSource(width, height, data); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); try { rawResult = multiFormatReader.decodeWithState(bitmap); } catch (ReaderException re) { // continue } finally { multiFormatReader.reset(); } //轉換亂碼 if (rawResult != null) { return converResult(rawResult); } return rawResult; } public Result yuvModeDecode(byte[] data, int width, int height) { Result rawResult = null; PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); try { rawResult = multiFormatReader.decodeWithState(bitmap); } catch (ReaderException re) { // continue } finally { multiFormatReader.reset(); } //轉換亂碼 if (rawResult != null) { return converResult(rawResult); } return rawResult; } /** * 使用ISO88591進行解碼,然后通過ISO88591在進行轉換亂碼 */ private Result converResult(Result rawResult) { //復制一個Result,並轉碼 String str = rawResult.getText(); String converText = null; try { converText = BarcodeUtils.converStr(str, ISO88591); } catch (UnsupportedEncodingException e) { Logger.getInstance(TAG).debug(e.toString()); } // FIXME 轉化失敗--》1:結果置空 // --》2:把未解碼的內容返回 if (converText != null) { return serResultText(rawResult, converText); } else { return rawResult; } } private Result serResultText(Result rawResult, String converText) { Result resultResult = new Result(converText, rawResult.getRawBytes(), rawResult.getResultPoints(), rawResult.getBarcodeFormat(), System.currentTimeMillis()); resultResult.putAllMetadata(rawResult.getResultMetadata()); return resultResult; } }
我們可以看出:
①指定相應的系統的參數來解碼byte數組中的內容。
②這樣數組往往會出現亂碼,我們需要經過crc等等的編碼格式的校正。
③把相應的文字賦值給對話框。
這就是我對zxing的理解。