二維碼之zxing二維碼解析圖片資源



zxing針對不同開發平臺,都給出瞭解析二維碼的例子,我這裏只聊聊關於android系統的解析。

對於android手機來說,二維碼圖像獲取方式有拍照掃描,以及讀取本地圖片資源。無論是哪種方式,解析過程的核心內容基本是一樣的。關於手機拍照掃描這塊,由於要涉及到很多問題要講,所以我打算把這塊放在下篇文章再細緻講解,這次只講如何對圖片進行解析。

首先,和生成二維碼一樣,我們要告訴系統解析二維碼的設置參數。這裏我選擇了支持主流的三類方式,其中一種爲一維碼(條形碼)。設置解析的字符位UTF8。如果不設置字符解析方式,它會自己去識別內容,然後自己判斷該用哪種方式。

看一下設置參數的代碼:

  1. MultiFormatReader multiFormatReader = new MultiFormatReader();  
  2.   
  3. // 解碼的參數  
  4. Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(  
  5.         2);  
  6. // 可以解析的編碼類型  
  7. Vector<BarcodeFormat> decodeFormats = new Vector<BarcodeFormat>();  
  8. if (decodeFormats == null || decodeFormats.isEmpty()) {  
  9.     decodeFormats = new Vector<BarcodeFormat>();  
  10.   
  11.     // 這裏設置可掃描的類型,我這裏選擇了都支持  
  12.     decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);  
  13.     decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);  
  14.     decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);  
  15. }  
  16. hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);  
  17.   
  18. // 設置繼續的字符編碼格式爲UTF8  
  19. // hints.put(DecodeHintType.CHARACTER_SET, "UTF8");  
  20.   
  21. // 設置解析配置參數  
  22. multiFormatReader.setHints(hints);  
		MultiFormatReader multiFormatReader = new MultiFormatReader();

		// 解碼的參數
		Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(
				2);
		// 可以解析的編碼類型
		Vector<BarcodeFormat> decodeFormats = new Vector<BarcodeFormat>();
		if (decodeFormats == null || decodeFormats.isEmpty()) {
			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);

		// 設置繼續的字符編碼格式爲UTF8
		// hints.put(DecodeHintType.CHARACTER_SET, "UTF8");

		// 設置解析配置參數
		multiFormatReader.setHints(hints);


補一句:zxing源碼中對UTF8的定義字符串內容不是UTF-8,而是UTF8。
private static final String UTF8 = "UTF8";

 

再來就是解析部分:

  1. // 開始對圖像資源解碼  
  2. Result rawResult = null;  
  3. try {  
  4.   
  5.     rawResult = multiFormatReader  
  6.             .decodeWithState(new BinaryBitmap(new HybridBinarizer(  
  7.                     new BitmapLuminanceSource(BitmapFactory  
  8.                             .decodeResource(getResources(),  
  9.                                     R.drawable.weibo)))));  
  10. catch (NotFoundException e) {  
  11.     e.printStackTrace();  
  12. }  
		// 開始對圖像資源解碼
		Result rawResult = null;
		try {

			rawResult = multiFormatReader
					.decodeWithState(new BinaryBitmap(new HybridBinarizer(
							new BitmapLuminanceSource(BitmapFactory
									.decodeResource(getResources(),
											R.drawable.weibo)))));
		} catch (NotFoundException e) {
			e.printStackTrace();
		}


按照zxing的解碼規則,需要傳入一個LuminanceSource類的對象,最後就會得到解析結果result對象,也就是解碼後的信息類。這裏唯一需要自己實現的就是BitmapLuminanceSource類。

BitmapLuminanceSource繼承自LuminanceSource這個抽象類,需要實現它的構造方法,並重載getMatrix()和getRow(int y, byte[] row)方法。其構造方法中需要傳入寬高,這兩個值指的就是圖片的寬和高。getMatrix()方法會返回一個byte數組,這個數組就是圖片的像素數組。getRow(int y, byte[] row)如字面的意義,就是得到圖片像素數組的一行。其中的y就是需要的哪一個行的像素數組。

先看構造方法:

  1. protected BitmapLuminanceSource(Bitmap bitmap) {  
  2.     super(bitmap.getWidth(), bitmap.getHeight());  
  3.   
  4.     // 首先,要取得該圖片的像素數組內容  
  5.     int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];  
  6.     this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];  
  7.     bitmap.getPixels(data, 0, getWidth(), 00, getWidth(), getHeight());  
  8.   
  9.     // 將int數組轉換爲byte數組  
  10.     for (int i = 0; i < data.length; i++) {  
  11.         this.bitmapPixels[i] = (byte) data[i];  
  12.     }  
  13. }  
	protected BitmapLuminanceSource(Bitmap bitmap) {
		super(bitmap.getWidth(), bitmap.getHeight());

		// 首先,要取得該圖片的像素數組內容
		int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];
		this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];
		bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight());

		// 將int數組轉換爲byte數組
		for (int i = 0; i < data.length; i++) {
			this.bitmapPixels[i] = (byte) data[i];
		}
	}

注意:這裏的byte數組是指圖片的像素數組,而不是所謂Bitmap轉換成byte數組,有人出現解析的錯誤,大多是對這個參數用途沒理解造成的。

Bitmap對象的getPixels方法可以取得的像素數組,但它得到是int型數組。根據其api文檔解釋,取得的是color,也就是像素顏色值。每個像素值包含透明度,紅色,綠色,藍色。所以白色就是0xffffffff,黑色就是0xff000000。直接由int型轉成byte型,實現上相當於我們這裏只取其藍色值部分。

 

再來就是getRow方法:

  1. @Override  
  2. public byte[] getRow(int y, byte[] row) {  
  3.     // 這裏要得到指定行的像素數據  
  4.     System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());  
  5.     return row;  
  6. }  
	@Override
	public byte[] getRow(int y, byte[] row) {
		// 這裏要得到指定行的像素數據
		System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());
		return row;
	}


補充:getPixels得到的像素數組是一維的,也就是按照圖片寬度逐行取像素顏色值錄入。如果想得到單行的像素數組內容,通過y*width就可以找該行的第一個像素值,拷貝後面width個就可以得到該行的像素內容。

最後一個就是getMatrix()方法,它用來返回我們的圖像轉換成的像素數組。

  1. @Override  
  2. public byte[] getMatrix() {  
  3.     // 返回我們生成好的像素數據  
  4.     return bitmapPixels;  
  5. }  
	@Override
	public byte[] getMatrix() {
		// 返回我們生成好的像素數據
		return bitmapPixels;
	}


以下是完整的BitmapLuminanceSource類:

  1.   public class BitmapLuminanceSource extends LuminanceSource {  
  2.   
  3.     private byte bitmapPixels[];  
  4.   
  5.     protected BitmapLuminanceSource(Bitmap bitmap) {  
  6.         super(bitmap.getWidth(), bitmap.getHeight());  
  7.   
  8.         // 首先,要取得該圖片的像素數組內容  
  9.         int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];  
  10.         this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];  
  11.         bitmap.getPixels(data, 0, getWidth(), 00, getWidth(), getHeight());  
  12.   
  13.         // 將int數組轉換爲byte數組,也就是取像素值中藍色值部分作爲辨析內容  
  14.         for (int i = 0; i < data.length; i++) {  
  15.             this.bitmapPixels[i] = (byte) data[i];  
  16.         }  
  17.     }  
  18.   
  19.     @Override  
  20.     public byte[] getMatrix() {  
  21.         // 返回我們生成好的像素數據  
  22.         return bitmapPixels;  
  23.     }  
  24.   
  25.     @Override  
  26.     public byte[] getRow(int y, byte[] row) {  
  27.         // 這裏要得到指定行的像素數據  
  28.         System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());  
  29.         return row;  
  30.     }  
  31. }  
  public class BitmapLuminanceSource extends LuminanceSource {

	private byte bitmapPixels[];

	protected BitmapLuminanceSource(Bitmap bitmap) {
		super(bitmap.getWidth(), bitmap.getHeight());

		// 首先,要取得該圖片的像素數組內容
		int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];
		this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];
		bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight());

		// 將int數組轉換爲byte數組,也就是取像素值中藍色值部分作爲辨析內容
		for (int i = 0; i < data.length; i++) {
			this.bitmapPixels[i] = (byte) data[i];
		}
	}

	@Override
	public byte[] getMatrix() {
		// 返回我們生成好的像素數據
		return bitmapPixels;
	}

	@Override
	public byte[] getRow(int y, byte[] row) {
		// 這裏要得到指定行的像素數據
		System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());
		return row;
	}
}


好了,最後就得到了我們想要的Result對象。
這個對象中包含了很多內容,包括內容,編碼方式,解析時間等。
我們想要的內容就放在rawResult.getText()中,你還可以得到它的編碼方式rawResult.getBarcodeFormat()。

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章