1. 程式人生 > >Android開源二維碼識別專案zxing橫屏改為豎屏識別解決方案

Android開源二維碼識別專案zxing橫屏改為豎屏識別解決方案

在網上找了很多方法,但最後都有問題,自己除錯了好幾個小時,最後終於完美解決了豎屏識別。

首先你需要有zxing專案的簡化版程式碼。

使用簡化版可以免去許多不必要的程式碼,方便學習研究,更好定位核心功能。

如果你除錯成功後,就可以著手修改將其變為豎屏識別了。

第1步:

在AndroidManifest中將CaptureActivity的screenOrientation屬性做如下修改:

android:screenOrientation="portrait" 

第2步:

我們要把攝像頭預覽景調為豎向

CameraConfigurationManager類中的setDesiredCameraParameters()方法中新增如下程式碼:

// 使攝像頭旋轉90度    
setDisplayOrientation(camera, 90);

然後在CameraConfigurationManager類的最後新增setDisplayOrientation()方法:

/*改變照相機成像的方向的方法*/ protected void setDisplayOrientation(Camera camera, int angle) {      Method downPolymorphic = null;              
try {       
downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new
 Class[] { int.class });       

if (downPolymorphic != null)                   
downPolymorphic.invoke(camera, new Object[]{angle});            
catch (NoSuchMethodException e) {        
e.printStackTrace();  
  } catch (IllegalArgumentException e) {       
e.printStackTrace();  
  } catch (IllegalAccessException e) { 

e.printStackTrace();    
catch (InvocationTargetException e) { 
   e.printStackTrace();    }  }


最後在CameraConfigurationManager中的initFromCameraParameters()方法的Log.d(TAG, "Screen resolution: " + screenResolution);句後面新增如下程式碼,這段程式碼是為了解決攝像頭豎過來後圖像拉伸的問題:

//為豎屏新增    
Point screenResolutionForCamera = new Point();    
screenResolutionForCamera.x = screenResolution.x;   
screenResolutionForCamera.y = screenResolution.y;    
if (screenResolution.x < screenResolution.y) {       
screenResolutionForCamera.x = screenResolution.y;       
screenResolutionForCamera.y = screenResolution.x;    
}    // 下句第二引數要根據豎屏修改    
cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);


第3步:

CameranManager類中getFramingRectInPreview()方法將:

// 下面為橫屏模式
rect.left = rect.left * cameraResolution.x / screenResolution.x;      
rect.right = rect.right * cameraResolution.x / screenResolution.x;      
rect.top = rect.top * cameraResolution.y / screenResolution.y;      
rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;

替換為:

/// 下面為豎屏模式      
rect.left = rect.left * cameraResolution.y / screenResolution.x;           
rect.right = rect.right * cameraResolution.y / screenResolution.x;            
rect.top = rect.top * cameraResolution.x / screenResolution.y;           
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;

第4步:

PlanarYUVLuminanceSource類中的getRow()方法為識別條形碼部分,

getMatrix()方法為識別二維碼部分

renderCroppedGreyscaleBitmap()方法為生成獲取的碼圖部分

將getRow()中的:

int offset = (y + top) * dataWidth + left;

getMatrix()中的:

int inputOffset = top * dataWidth + left;
inputOffset += dataWidth;

renderCroppedGreyscaleBitmap()中的:

int inputOffset = top * dataWidth + left;
inputOffset += dataWidth;

這些語句中dataWidth全部替換為dataHeight

同時將PlanarYUVLuminanceSource構造方法中:

if (left + width > dataWidth || top + height > dataHeight) {      thrownew IllegalArgumentException("Crop rectangle does not fit within image data.");    }

dataWidth與dateHeight中互換位置即可。

此時,你的程式豎屏識別碼圖應該沒有任何問題了。至於取景框的樣式,大家可以在自定義的ViewfinderView中修改成自己喜歡的樣式。

http://407827531.iteye.com/blog/1488676

解決方法:

1.在DecodeHandler.java中,修改decode方法
  PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(data, width, height);

    byte[] rotatedData = new byte[data.length];
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++)
            rotatedData[x * height + height - y - 1] = data[x + y * width];
    }
    int tmp = width; // Here we are swapping, that's the difference to #11
    width = height;
    height = tmp;

    PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);

2.在CameraManager.java中,註釋程式碼:
            // rect.left = rect.left * cameraResolution.x / screenResolution.x;
            // rect.right = rect.right * cameraResolution.x / screenResolution.x;
            // rect.top = rect.top * cameraResolution.y / screenResolution.y;
            // rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
修改為
            rect.left = rect.left * cameraResolution.y / screenResolution.x;
            rect.right = rect.right * cameraResolution.y / screenResolution.x;
            rect.top = rect.top * cameraResolution.x / screenResolution.y;
            rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;

3.在CameraConfigurationManager.java中,在setDesiredCameraParameters方法中新增一句
  camera.setDisplayOrientation(90);

4.在AndroidManifest.xml中,把Activity的屬性android:screenOrientation="landscape"
改為
  android:screenOrientation="portrait"

編譯執行即可!


參考:

http://code.google.com/p/zxing/issues/detail?id=178#c46


程式碼:
https://github.com/pplante/zxing-android

按照以上做法,可以實現。

使用簡化版可以免去許多不必要的程式碼,方便學習研究,更好定位核心功能。

如果你除錯成功後,就可以著手修改將其變為豎屏識別了。

第1步:

在AndroidManifest中將CaptureActivity的screenOrientation屬性做如下修改:

android:screenOrientation="portrait" 

第2步:

我們要把攝像頭預覽景調為豎向

CameraConfigurationManager類中的setDesiredCameraParameters()方法中新增如下程式碼:

// 使攝像頭旋轉90度
    setDisplayOrientation(camera, 90);

然後在CameraConfigurationManager類的最後新增setDisplayOrientation()方法:

/*改變照相機成像的方向的方法*/
  protected void setDisplayOrientation(Camera camera, int angle) {
      Method downPolymorphic = null;        
      try {
        downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class });
        if (downPolymorphic != null)     
              downPolymorphic.invoke(camera, new Object[]{angle});        
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }

  }

最後在CameraConfigurationManager中的initFromCameraParameters()方法的Log.d(TAG, "Screen resolution: " + screenResolution);句後面新增如下程式碼,這段程式碼是為了解決攝像頭豎過來後圖像拉伸的問題:

//為豎屏新增
    Point screenResolutionForCamera = new Point();
    screenResolutionForCamera.x = screenResolution.x;
    screenResolutionForCamera.y = screenResolution.y;
    if (screenResolution.x < screenResolution.y) {
        screenResolutionForCamera.x = screenResolution.y;
        screenResolutionForCamera.y = screenResolution.x;
    }
    // 下句第二引數要根據豎屏修改
    cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);

第3步:

CameranManager類中getFramingRectInPreview()方法將:

// 下面為橫屏模式
      rect.left = rect.left * cameraResolution.x / screenResolution.x;
      rect.right = rect.right * cameraResolution.x / screenResolution.x;
      rect.top = rect.top * cameraResolution.y / screenResolution.y;
      rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;

替換為:

// 下面為豎屏模式
      rect.left = rect.left * cameraResolution.y / screenResolution.x;      
      rect.right = rect.right * cameraResolution.y / screenResolution.x;      
      rect.top = rect.top * cameraResolution.x / screenResolution.y;      
      rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;   

第4步:

PlanarYUVLuminanceSource類中的getRow()方法為識別條形碼部分,

getMatrix()方法為識別二維碼部分

renderCroppedGreyscaleBitmap()方法為生成獲取的碼圖部分

將getRow()中的:

int offset = (y + top) * dataWidth + left;

getMatrix()中的:

int inputOffset = top * dataWidth + left;
inputOffset += dataWidth;

renderCroppedGreyscaleBitmap()中的:

int inputOffset = top * dataWidth + left;
inputOffset += dataWidth;

這些語句中dataWidth全部替換為dataHeight

同時將PlanarYUVLuminanceSource構造方法中:

if (left + width > dataWidth || top + height > dataHeight) {
      throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
    }

dataWidth與dateHeight中互換位置即可。

此時,你的程式豎屏識別碼圖應該沒有任何問題了。至於取景框的樣式,大家可以在自定義的ViewfinderView中修改成自己喜歡的樣式。

——————————————————————————————

測試,可以豎屏,但是取景還是得橫著來。

解決方法:

1.在DecodeHandler.java中,修改decode方法
  PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(data, width, height);

    byte[] rotatedData = new byte[data.length];
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++)
            rotatedData[x * height + height - y - 1] = data[x + y * width];
    }
    int tmp = width; // Here we are swapping, that's the difference to #11
    width = height;
    height = tmp;

    PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);

2.在CameraManager.java中,註釋程式碼:
            // rect.left = rect.left * cameraResolution.x / screenResolution.x;
            // rect.right = rect.right * cameraResolution.x / screenResolution.x;
            // rect.top = rect.top * cameraResolution.y / screenResolution.y;
            // rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
修改為
            rect.left = rect.left * cameraResolution.y / screenResolution.x;
            rect.right = rect.right * cameraResolution.y / screenResolution.x;
            rect.top = rect.top * cameraResolution.x / screenResolution.y;
            rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;

3.在CameraConfigurationManager.java中,在setDesiredCameraParameters方法中新增一句
  camera.setDisplayOrientation(90);

4.在AndroidManifest.xml中,把Activity的屬性android:screenOrientation="landscape"
改為
  android:screenOrientation="portrait"

編譯執行即可!

參考:

http://code.google.com/p/zxing/issues/detail?id=178#c46

程式碼:

https://github.com/pplante/zxing-android

————————

按照下篇的解決方案,成功解決問題。

實現zxing多次掃描問題:

private void continuePreview()

 {
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
        initCamera(surfaceHolder);
        if (handler != null)
            handler.restartPreviewAndDecode();   
 }

CaptureActivityHandler中restartPreviewAndDecode屬性由private 設定成public

——————————————————————————————

掃描二維碼圖片時候,正方形的二維碼圖片,顯示會發現變拉長、拉伸。

找著的解決途徑如下:

http://www.apkbus.com/android-94078-1-1.html

更改CameraConfigurationManager.java檔案

在 Log.d(TAG, "Screen resolution: " + screenResolution);這句之後增加
Point screenResolutionForCamera = new Point();
        screenResolutionForCamera.x = screenResolution.x;
        screenResolutionForCamera.y = screenResolution.y;
        // preview size is always something like 480*320, other 320*480
        if (screenResolution.x < screenResolution.y) {
        screenResolutionForCamera.x = screenResolution.y;
        screenResolutionForCamera.y = screenResolution.x;
        }
再更改cameraResolution = getCameraResolution(parameters, screenResolution);為cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);

PS:有時間一定要讀zxing原始碼的。