ZXing生成的二維碼為什么會有白邊,如何去除。


別人的代碼

先放一下一份抄到的代碼,一般的ZXing都是差不多這樣寫的

    /**
	 * @param args  
	 */
	public static void main(String[] args) {
		try {
			String content = "{\"server\":4407}";
			Integer width = 100;
			Integer height = 100;
			
			Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
			hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
			hints.put(EncodeHintType.MARGIN, 0);    
			
			QRCodeWriter writer = new QRCodeWriter();
			BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
			MatrixToImageWriter.writeToStream(bitMatrix, "jpg", new FileOutputStream("你好.jpg"));
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

結果圖如下

白邊問題

尺寸為100,100的時候,白邊情況比較嚴重,左右上下都有8個像素的白邊。
先考究一下為什么會產生白邊,能不能去掉。

看一下encode方法。

 @Override
  public BitMatrix encode(String contents,
                          BarcodeFormat format,
                          int width,
                          int height,
                          Map<EncodeHintType,?> hints) throws WriterException {

    if (contents.isEmpty()) {
      throw new IllegalArgumentException("Found empty contents");
    }

    if (format != BarcodeFormat.QR_CODE) {
      throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
    }

    if (width < 0 || height < 0) {
      throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' +
          height);
    }

    ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
    int quietZone = QUIET_ZONE_SIZE;
    if (hints != null) {
      if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {
        errorCorrectionLevel = ErrorCorrectionLevel.valueOf(hints.get(EncodeHintType.ERROR_CORRECTION).toString());
      }
      if (hints.containsKey(EncodeHintType.MARGIN)) {
        quietZone = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());
      }
    }
	//沒有涉及到寬高,僅僅和內容相關,那么我們的內容變成二維碼就是這里
    QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);
    //和寬高、以及剛剛生成的二維碼相關,其中還有一個quietZone參數。
    return renderResult(code, width, height, quietZone);
  }
  // Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
  // 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
  // 假定初始輸出的width為100,height為100
  private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
      ByteMatrix input = code.getMatrix();
      //計算出來的二維碼大小,假設二維碼最小大小為21
      int inputWidth = input.getWidth();
      int inputHeight = input.getHeight();
      //計算留白空間,設的0會在這里生效
      int qrWidth = inputWidth + (quietZone * 2);
      int qrHeight = inputHeight + (quietZone * 2);
      //計算二維碼大小和我們提供給系統的尺寸比較,取最大值。
      int outputWidth = Math.max(width, qrWidth);
      int outputHeight = Math.max(height, qrHeight);
      //計算二維碼與提供尺寸的倍率關系,用於放大縮小。如100/21=4
      int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
      // Padding includes both the quiet zone and the extra white pixels to accommodate the requested
      // dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
      // If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
      // handle all the padding from 100x100 (the actual QR) up to 200x160.
      //上述主要解釋leftPadding和topPadding右邊的計算賦值是什么意思。
      //計算左上分別空多少空間,(100-21*4)=8
      int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
      int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
      //取尺寸最大值的畫布大小
      BitMatrix output = new BitMatrix(outputWidth, outputHeight);
      //從空出的第8個像素點開始,把二維碼點1x1的結果放大至4x4寫入畫布中。
      for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
          // Write the contents of this row of the barcode
          for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
              if (input.get(inputX, inputY) == 1) {
                  output.setRegion(outputX, outputY, multiple, multiple);
              }
          }
      }

    return output;
  }

可以看到,在renderResult方法中,QRCodeWriter直接是將原本二維碼像素值直接以整數倍率擴增,因為內容的大小導致對應尺寸和二維碼像素值無法整除關系,為了適應用戶輸入的尺寸,所以才產生了白邊。

所以去白邊,本質上是將100x100變成84x84這種情況。

那么我們直接重新實現Writer,把其他代碼基本照抄,然后把renderResult改寫。

PS:為什么不是繼承重寫,是因為QRCodeWriter是一個被final修飾的類,不允許繼承。

白邊解決方案

 // Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
  // 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
  // 假定初始輸出的width為100,height為100
  private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
      ByteMatrix input = code.getMatrix();
      int inputWidth = input.getWidth();
      int inputHeight = input.getHeight();
      int qrWidth = inputWidth + (quietZone * 2);
      int qrHeight = inputHeight + (quietZone * 2);
      int outputWidth = Math.max(width, qrWidth);
      int outputHeight = Math.max(height, qrHeight);
      int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
      
	  outputWidth = qrWidth * multiple;// 改動點
	  outputHeight = qrWidth * multiple;// 改動點
      
      // Padding includes both the quiet zone and the extra white pixels to accommodate the requested
      // dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
      // If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
      // handle all the padding from 100x100 (the actual QR) up to 200x160.
      int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
      int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
      leftPadding = 0 ;//改動點
      topPadding = 0 ;//改動點
      BitMatrix output = new BitMatrix(outputWidth, outputHeight);
      for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
          // Write the contents of this row of the barcode
          for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
              if (input.get(inputX, inputY) == 1) {
                  output.setRegion(outputX, outputY, multiple, multiple);
              }
          }
      }

    return output;
  }

如上,添加4行代碼就可以完成去白邊的功能需求。

啟動代碼

public static void main(String[] args) {
		try {
			String content = "{\"server\":4407}";
			Integer width = 100;
			Integer height = 100;
			
			Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
			hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
			hints.put(EncodeHintType.MARGIN, 0);    
			
			QCCodeWriterSelf writer = new QCCodeWriterSelf();//復制QCCodeWriter實現的類,僅僅添加了上述說明的4行代碼
			BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
			MatrixToImageWriter.writeToStream(bitMatrix, "jpg", new FileOutputStream("你好.jpg"));
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
	

文件圖片結果如下所示

結論

二維碼的生成分為兩部分:
第一部分是根據你的內容生成對應的二維碼大小,如上圖二維碼的最小大小為21x21.
第二部分是根據用戶輸入的大小,如100x100就是先擴大成84x84,剩余16x16就變成4個方位的8x8白邊。
因此白邊是可以去除的,但是去除后白邊的二維碼因為是21*21,只能整數倍放大,做不到浮點值放大,因為最小單元像素點不能夠再划分。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM