Android 基于Zxing的二维码扫描优化

本文探讨了Android应用在户外环境下扫描二维码遇到的问题,如二维码不清晰、有污渍或破损,以及不同手机配置的影响。作者分析了问题原因,并分享了通过改进Zxing库实现的优化方案,使得在多数情况下能成功识别二维码,尽管破损二维码仍无法识别。文中还提及了其他可参考的开源库,但最终选择自行调整以满足需求。
摘要由CSDN通过智能技术生成

最近公司项目App中要集成二维码扫描来适应在户外工作的时候,对码头集装箱等上面贴的A4纸张打印的二维码进行识别, 一般App二维码集成后,能扫出来就不管了,但是我们在集成成功后,根据用户反馈,在户外的环境下,很多二维码识别不了,或者识别速度慢,我们自己也是适用了一下,发现也确实是这样.

  一般造成这个识别不出来的原因,我们总结了以下几点:

  1. A4纸张打印的标签二维码, 本来打印就不是特别清晰,有些像素点,不一定都打印了出来,
  2. 户外环境,多变,灰尘,粉尘影响
  3. 日晒雨淋,还有各种划痕,造成了二维码破损和有污渍
  4. Android手机配置不一样,有的手机好一点,像素高,有的用户的像素低

大概就是这些,但是用基于QBar(在Zxing上做了优化)的微信,却能很快的识别出上面几种情况造成的二维码; 基于libqrencode 库集成的支付宝或者钉钉二维码扫描,一样也能识别出来;还有IOS也就是调用系统的扫描,也一样能够扫描出来,为啥我们大android不行?

老板不管这些,只是说了,别人的可以,为啥你的不可以,那就是你的问题.......

网上找了很多各种几千个赞的第三方集成的二维码,根本就不能满足上面的需求,当时感觉真心不知道怎么办好了.

  唯独网上找的这个还可以一点:https://github.com/vondear/RxTool 但是破损一些的还是扫描不出来;那个网上几千个赞的一片枫叶的 ,这种环境下的二维码扫描根本边都摸不到.

  还有郭林推荐的:https://github.com/al4fun/SimpleScanner 这个库,虽然是大神推荐的,但是比上面的这个,还要差那么好几点,郭林的推荐的二维码扫描链接:https://mp.weixin.qq.com/s/aPqSK1FlsPiENzSE48BVUA

 没办法,最后只能自己动手,网上找的,没有找到合适的,目前我们修改的二维码扫描基本可以做到:除了破损的识别不了,其他的都能识别,就是有时候速度慢了点,要多对一下焦,勉强能够比上面好那么一点点而已.

代码如下: (这里面很多类都在原有类的基础上有改动,虽然类名相同,但是里面的方法有些有变动!)

build.gradle

dependencies{
    api fileTree(include: ['*.jar'], dir: 'libs')
    api files('libs/core-3.3.0.jar')
    //    provided 'com.android.support:appcompat-v7:26.1.0'
    compileOnly 'com.android.support:design:26.1.0'
    compileOnly 'com.android.support:support-vector-drawable:26.1.0'
}

文件目录如下:

ZxingConfig.java
public class ZxingConfig implements Serializable {


    /*是否播放声音*/
    private boolean isPlayBeep = true;
    /*是否震动*/
    private boolean isShake = false;
    /*是否显示下方的其他功能布局*/
    private boolean isShowbottomLayout = true;
    /*是否显示闪光灯按钮*/
    private boolean isShowFlashLight = true;
    /*是否显示相册按钮*/
    private boolean isShowAlbum = true;



    public boolean isPlayBeep() {
        return isPlayBeep;
    }

    public void setPlayBeep(boolean playBeep) {
        isPlayBeep = playBeep;
    }

    public boolean isShake() {
        return isShake;
    }

    public void setShake(boolean shake) {
        isShake = shake;
    }

    public boolean isShowbottomLayout() {
        return isShowbottomLayout;
    }

    public void setShowbottomLayout(boolean showbottomLayout) {
        isShowbottomLayout = showbottomLayout;
    }

    public boolean isShowFlashLight() {
        return isShowFlashLight;
    }

    public void setShowFlashLight(boolean showFlashLight) {
        isShowFlashLight = showFlashLight;
    }

    public boolean isShowAlbum() {
        return isShowAlbum;
    }

    public void setShowAlbum(boolean showAlbum) {
        isShowAlbum = showAlbum;
    }
}
CameraFacing.java
public enum CameraFacing {

  BACK,  // must be value 0!
  FRONT, // must be value 1!

}
OpenCamera.java
public final class OpenCamera {
  
  private final int          index;
  private final Camera       camera;
  private final CameraFacing facing;
  private final int          orientation;
  
  public OpenCamera(int index, Camera camera, CameraFacing facing, int orientation) {
    this.index = index;
    this.camera = camera;
    this.facing = facing;
    this.orientation = orientation;
  }

  public Camera getCamera() {
    return camera;
  }

  public CameraFacing getFacing() {
    return facing;
  }

  public int getOrientation() {
    return orientation;
  }

  @Override
  public String toString() {
    return "Camera #" + index + " : " + facing + ',' + orientation;
  }

}
OpenCameraInterface.java
public final class OpenCameraInterface {

    private static final String TAG = OpenCameraInterface.class.getName();

    private OpenCameraInterface() {
    }

    /**
     * For {@link #open(int)}, means no preference for which camera to open.
     */
    public static final int NO_REQUESTED_CAMERA = -1;

    /**
     * Opens the requested camera with {@link Camera#open(int)}, if one exists.
     *
     * @param cameraId camera ID of the camera to use. A negative value
     *                 or {@link #NO_REQUESTED_CAMERA} means "no preference", in which case a rear-facing
     *                 camera is returned if possible or else any camera
     * @return handle to {@link OpenCamera} that was opened
     */
    public static OpenCamera open(int cameraId) {

        int numCameras = Camera.getNumberOfCameras();
        if (numCameras == 0) {
            Log.w(TAG, "No cameras!");
            return null;
        }

        boolean explicitRequest = cameraId >= 0;

        Camera.CameraInfo selectedCameraInfo = null;
        int index;
        if (explicitRequest) {
            index = cameraId;
            selectedCameraInfo = new Camera.CameraInfo();
            Camera.getCameraInfo(index, selectedCameraInfo);
        } else {
            index = 0;
            while (index < numCameras) {
                Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
                Camera.getCameraInfo(index, cameraInfo);
                CameraFacing reportedFacing = CameraFacing.values()[cameraInfo.facing];
                if (reportedFacing == CameraFacing.BACK) {
                    selectedCameraInfo = cameraInfo;
                    break;
                }
                index++;
            }
        }

        Camera camera;
        if (index < numCameras) {
            Log.i(TAG, "Opening camera #" + index);
            camera = Camera.open(index);
        } else {
            if (explicitRequest) {
                Log.w(TAG, "Requested camera does not exist: " + cameraId);
                camera = null;
            } else {
                Log.i(TAG, "No camera facing " + CameraFacing.BACK + "; returning camera #0");
                camera = Camera.open(0);
                selectedCameraInfo = new Camera.CameraInfo();
                Camera.getCameraInfo(0, selectedCameraInfo);
            }
        }

        if (camera == null) {
            return null;
        }
        return new OpenCamera(index,
                              camera,
                              CameraFacing.values()[selectedCameraInfo.facing],
                              selectedCameraInfo.orientation);
    }

}
AutoFocusManager.java
final class AutoFocusManager implements Camera.AutoFocusCallback {

  private static final String TAG = AutoFocusManager.class.getSimpleName();

  private static final long AUTO_FOCUS_INTERVAL_MS = 2000L;
  private static final Collection<String> FOCUS_MODES_CALLING_AF;
  static {
    FOCUS_MODES_CALLING_AF = new ArrayList<>(2);
    FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO);
    FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO);
  }

  private boolean stopped;
  private boolean focusing;
  private final boolean useAutoFocus;
  private final Camera camera;
  private AsyncTask<?,?,?> outstandingTask;

  AutoFocusManager(Context context, Camera camera) {
    this.camera = camera;
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
    String currentFocusMode = camera.getParameters().getFocusMode();
    useAutoFocus =
            sharedPrefs.getBoolean(QRConstants.KEY_AUTO_FOCUS, true) &&
            FOCUS_MODES_CALLING_AF.contains(currentFocusMode);
    Log.i(TAG, "Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus);
    start();
  }

  @Override
  public synchronized void onAutoFocus(boolean success, Camera theCamera) {
    focusing = false;
    autoFocusAgainLater();
  }

  private synchronized void autoFocusAgainLater() {
    if (!stopped && outstandingTask == null) {
      AutoFocusTask newTask = new AutoFocusTask();
      try {
        newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        outstandingTask = newTask;
      } catch (RejectedExecutionException ree) {
        Log.w(TAG, "Could not request auto focus", ree);
      }
    }
  }

  synchronized void start() {
//    if (useAutoFocus) {
      outstandingTask = null;
      if (!stopped && !focusing) {
        try {
          camera.autoFocus(this);
          focusing = true;
        } catch (RuntimeException re) {
          // Have heard RuntimeException reported in Android 4.0.x+; continue?
          Log.w(TAG, "Unexpected exception while focusing", re);
          // Try again later to keep cycle going
          autoFocusAgainLater();
        }
      }
//    }
  }

  private synchronized void cancelOutstandingTask() {
    if (outstandingTask != null) {
      if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) {
        outstandingTask.cancel(true);
      }
      outstandingTask = null;
    }
  }

  synchronized void stop() {
    stopped = true;
//    if (useAutoFocus) {
      cancelOutstandingTask();
      // Doesn't hurt to call this even if not focusing
      try {
        camera.cancelAutoFocus();
      } catch (RuntimeException re) {
        // Have heard RuntimeException reported in Android 4.0.x+; continue?
        Log.w(TAG, "Unexpected exception while cancelling focusing", re);
      }
//    }
  }

  private final class AutoFocusTask extends AsyncTask<Object,Object,Object> {
    @Override
    protected Object doInBackground(Object... voids) {
      try {
        Thread.sleep(AUTO_FOCUS_INTERVAL_MS);
      } catch (InterruptedException e) {
        // continue
      }
      start();
      return null;
    }
  }

}
CameraConfigurationManager.java
final class CameraConfigurationManager {

    private static final String TAG = "CameraConfiguration";

    private final Context context;
    private int cwNeededRotation;
    private int cwRotationFromDisplayToCamera;
    private Point screenResolution;
    private Point cameraResolution;
    private Point bestPreviewSize;
    private Point previewSizeOnScreen;

    CameraConfigurationManager(Context context) {
        this.context = context;
    }

    /**
     * Reads, one time, values from the camera that are needed by the app.
     */
    void initFromCameraParameters(OpenCamera camera) {
        Camera.Parameters parameters = camera.getCamera().getParameters();
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = manager.getDefaultDisplay();

        int displayRotation = display.getRotation();
        int cwRotationFromNaturalToDisplay;
        switch (displayRotation) {
            case Surface.ROTATION_0:
                cwRotationFromNaturalToDisplay = 0;
                break;
            case Surface.ROTATION_90:
                cwRotationFromNaturalToDisplay = 90;
                break;
            case Surface.ROTATION_180:
                cwRotationFromNaturalToDisplay = 180;
                break;
            case Surface.ROTATION_270:
                cwRotationFromNaturalToDisplay = 270;
                break;
            default:
                // Have seen this return incorrect values like -90
                if (displayRotation % 90 == 0) {
                    cwRotationFromNaturalToDisplay = (360 + displayRotation) % 360;
                } else {
                    throw new IllegalArgumentException("Bad rotation: " + displayRotation);
                }
        }
        Log.i(TAG, "Display at: " + cwRotationFromNaturalToDisplay);

        int cwRotationFromNaturalToCamera = camera.getOrientation();
        Log.i(TAG, "Camera at: " + cwRotationFromNaturalToCamera);

        // Still not 100% sure about this. But acts like we need to flip this:
        if (camera.getFacing() == CameraFacing.FRONT) {
            cwRotationFromNaturalToCamera = (360 - cwRotationFromNaturalToCamera) % 360;
            Log.i(TAG, "Front camera overriden to: " + cwRotationFromNaturalToCamera);
        }

    /*
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
    String overrideRotationString;
    if (camera.getFacing() == CameraFacing.FRONT) {
      overrideRotationString = prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION_FRONT, null);
    } else {
      overrideRotationString = prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION, null);
    }
    if (overrideRotationString != null && !"-".equals(overrideRotationString)) {
      Log.i(TAG, "Overriding camera manually to " + overrideRotationString);
      cwRotationFromNaturalToCamera = Integer.parseInt(overrideRotationString);
    }
     */

        cwRotationFromDisplayToCamera =
                (360 + cwRotationFromNaturalToCamera - cwRotationFromNaturalToDisplay) % 360;
        Log.i(TAG, "Final display orientation: " + cwRotationFromDisplayToCamera);
        if (camera.getFacing() == CameraFacing.FRONT) {
            Log.i(TAG, "Compensating rotation for front camera");
            cwNeededRotation = (360 - cwRotationFromDisplayToCamera) % 360;
        } else {
            cwNeededRotation = cwRotationFromDisplayToCamera;
        }
        Log.i(TAG, "Clockwise rotation from display to camera: " + cwNeededRotation);

        Point theScreenResolution = new Point();
        display.getSize(theScreenResolution);
        screenResolution = theScreenResolution;
        Log.i(TAG, "Screen resolution in current orientation: " + screenResolution);
        cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
        Log.i(TAG, "Camera resolution: " + cameraResolution);
        bestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
        Log.i(TAG, "Best available preview size: " + bestPreviewSize);

        boolean isScreenPortrait = screenResolution.x < screenResolution.y;
        boolean isPreviewSizePortrait = bestPreviewSize.x < bestPreviewSize.y;

        if (isScreenPortrait == isPreviewSizePortrait) {
            previewSizeOnScreen = bestPreviewSize;
        } else {
            previewSizeOnScreen = new Point(bestPreviewSize.y, bestPreviewSize.x);
        }
        Log.i(TAG, "Preview size on screen: " + previewSizeOnScreen);
    }

    void setDesiredCameraParameters(OpenCamera camera, boolean safeMode) {

        Camera theCamera = camera.getCamera();
        Camera.Parameters parameters = theCamera.getParameters();

        if (parameters == null) {
            Log.w(TAG, "Device error: no camera parameters are available. Proceeding without configuration.");
            return;
        }

        Log.i(TAG, "Initial camera parameters: " + parameters.flatten());

        if (safeMode) {
            Log.w(TAG, "In camera config safe mode -- most settings will not be honored");
        }

//        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);

        initializeTorch(parameters, safeMode, QRConstants.disableExposure);


        CameraConfigurationUtils.setFocus(
                parameters,
                //是否支持自动对焦
                QRConstants.autoFocus,
                true,
                safeMode);

        if (!safeMode) {
            //
            //CameraConfigurationUtils.setInvertColor(parameters);

            CameraConfigurationUtils.setBarcodeSceneMode(parameters);

            CameraConfigurationUtils.setVideoStabilization(parameters);
            CameraConfigurationUtils.setFocusArea(parameters);
            CameraConfigurationUtils.setMetering(parameters);

        }

        parameters.setPreviewSize(bestPreviewSize.x, bestPreviewSize.y);

        theCamera.setParameters(parameters);

        theCamera.setDisplayOrientation(cwRotationFromDisplayToCamera);

        Camera.Parameters afterParameters = theCamera.getParameters();
        Camera.Size afterSize = afterParameters.getPreviewSize();
        if (afterSize != null && (bestPreviewSize.x != afterSize.width || bestPreviewSize.y != afterSize.height)) {
            Log.w(TAG, "Camera said it supported preview size " + bestPreviewSize.x + 'x' + bestPreviewSize.y +
                    ", but after setting it, preview size is " + afterSize.width + 'x' + afterSize.height);
            bestPreviewSize.x = afterSize.width;
            bestPreviewSize.y = afterSize.height;
        }
    }

    Point getBestPreviewSize() {
        return bestPreviewSize;
    }

    Point getPreviewSizeOnScreen() {
        return previewSizeOnScreen;
    }

    Point getCameraResolution() {
        return cameraResolution;
    }

    Point getScreenResolution() {
        return screenResolution;
    }

    int getCWNeededRotation() {
        return cwNeededRotation;
    }

    boolean getTorchState(Camera camera) {
        if (camera != null) {
            Camera.Parameters parameters = camera.getParameters();
            if (parameters != null) {
                String flashMode = camera.getParameters().getFlashMode();
                return flashMode != null &&
                        (Camera.Parameters.FLASH_MODE_ON.equals(flashMode) ||
                                Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode));
            }
        }
        return false;
    }

    void setTorch(Camera camera, boolean newSetting) {
        Camera.Parameters parameters = camera.getParameters();
        doSetTorch(parameters, newSetting, false, QRConstants.disableExposure);
        camera.setParameters(parameters);
    }

    private void initializeTorch(Camera.Parameters parameters, boolean safeMode, boolean disableExposure) {
        boolean currentSetting = QRConstants.frontLightMode == FrontLightMode.ON;
        doSetTorch(parameters, currentSetting, safeMode, disableExposure);
    }

    private void doSetTorch(Camera.Parameters parameters, boolean newSetting, boolean safeMode, boolean disableExposure) {
        CameraConfigurationUtils.setTorch(parameters, newSetting);
        if (!safeMode && !disableExposure) {
            CameraConfigurationUtils.setBestExposure(parameters, newSetting);
        }
    }

}
CameraConfigurationUtils.java
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
public final class CameraConfigurationUtils {

  private static final String TAG = "CameraConfiguration";

  private static final Pattern SEMICOLON = Pattern.compile(";");

  private static final int MIN_PREVIEW_PIXELS = 480 * 320; // normal screen
  private static final float MAX_EXPOSURE_COMPENSATION = 1.5f;
  private static final float MIN_EXPOSURE_COMPENSATION = 0.0f;
  private static final double MAX_ASPECT_DISTORTION = 0.15;
  private static final int MIN_FPS = 10;
  private static final int MAX_FPS = 20;
  private static final int AREA_PER_1000 = 400;

  private CameraConfigurationUtils() {
  }

  public static void setFocus(Camera.Parameters parameters,
                              boolean autoFocus,
                              boolean disableContinuous,
                              boolean safeMode) {
    List<String> supportedFocusModes = parameters.getSupportedFocusModes();
    String focusMode = null;
    if (autoFocus) {
      if (safeMode || disableContinuous) {
        focusMode = findSettableValue("focus mode",
                supportedFocusModes,
                Camera.Parameters.FOCUS_MODE_AUTO);
      } else {
        focusMode = findSettableValue("focus mode",
                supportedFocusModes,
                Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
                Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
                Camera.Parameters.FOCUS_MODE_AUTO);
      }
    }
    // Maybe selected auto-focus but not available, so fall through here:
    if (!safeMode && focusMode == null) {
      focusMode = findSettableValue("focus mode",
                                    supportedFocusModes,
                                    Camera.Parameters.FOCUS_MODE_MACRO,
                                    Camera.Parameters.FOCUS_MODE_EDOF);
    }
    if (focusMode != null) {
      if (focusMode.equals(parameters.getFocusMode())) {
        Log.i(TAG, "Focus mode already set to " + focusMode);
      } else {
        parameters.setFocusMode(focusMode);
      }
    }
  }

  public static void setTorch(Camera.Parameters parameters, boolean on) {
    List<String> supportedFlashModes = parameters.getSupportedFlashModes();
    String flashMode;
    if (on) {
      flashMode = findSettableValue("flash mode",
                                    supportedFlashModes,
                                    Camera.Parameters.FLASH_MODE_TORCH,
                                    Camera.Parameters.FLASH_MODE_ON);
    } else {
      flashMode = findSettableValue("flash mode",
                                    supportedFlashModes,
                                    Camera.Parameters.FLASH_MODE_OFF);
    }
    if (flashMode != null) {
      if (flashMode.equals(parameters.getFlashMode())) {
        Log.i(TAG, "Flash mode already set to " + flashMode);
      } else {
        Log.i(TAG, "Setting flash mode to " + flashMode);
        parameters.setFlashMode(flashMode);
      }
    }
  }

  public static void setBestExposure(Camera.Parameters parameters, boolean lightOn) {
    int minExposure = parameters.getMinExposureCompensation();
    int maxExposure = parameters.getMaxExposureCompensation();
    float step = parameters.getExposureCompensationStep();
    if ((minExposure != 0 || maxExposure != 0) && step > 0.0f) {
      // Set low when light is on
      float targetCompensation = lightOn ? MIN_EXPOSURE_COMPENSATION : MAX_EXPOSURE_COMPENSATION;
      int compensationSteps = Math.round(targetCompensation / step);
      float actualCompensation = step * compensationSteps;
      // Clamp value:
      compensationSteps = Math.max(Math.min(compensationSteps, maxExposure), minExposure);
      if (parameters.getExposureCompensation() == compensationSteps) {
        Log.i(TAG, "Exposure compensation already set to " + compensationSteps + " / " + actualCompensation);
      } else {
        Log.i(TAG, "Setting exposure compensation to " + compensationSteps + " / " + actualCompensation);
        parameters.setExposureCompensation(compensationSteps);
      }
    } else {
      Log.i(TAG, "Camera does not support exposure compensation");
    }
  }

  public static void setBestPreviewFPS(Camera.Parameters parameters) {
    setBestPreviewFPS(parameters, MIN_FPS, MAX_FPS);
  }

  public static void setBestPreviewFPS(Camera.Parameters parameters, int minFPS, int maxFPS) {
    List<int[]> supportedPreviewFpsRanges = parameters.getSupportedPreviewFpsRange();
    Log.i(TAG, "Supported FPS ranges: " + toString(supportedPreviewFpsRanges));
    if (supportedPreviewFpsRanges != null && !supportedPreviewFpsRanges.isEmpty()) {
      int[] suitableFPSRange = null;
      for (int[] fpsRange : supportedPreviewFpsRanges) {
        int thisMin = fpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
        int thisMax = fpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
        if (thisMin >= minFPS * 1000 && thisMax <= maxFPS * 1000) {
          suitableFPSRange = fpsRange;
          break;
        }
      }
      if (suitableFPSRange == null) {
        Log.i(TAG, "No suitable FPS range?");
      } else {
        int[] currentFpsRange = new int[2];
        parameters.getPreviewFpsRange(currentFpsRange);
        if (Arrays.equals(currentFpsRange, suitableFPSRange)) {
          Log.i(TAG, "FPS range already set to " + Arrays.toString(suitableFPSRange));
        } else {
          Log.i(TAG, "Setting FPS range to " + Arrays.toString(suitableFPSRange));
          parameters.setPreviewFpsRange(suitableFPSRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
                                        suitableFPSRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
        }
      }
    }
  }

  public static void setFocusArea(Camera.Parameters parameters) {
    if (parameters.getMaxNumFocusAreas() > 0) {
      Log.i(TAG, "Old focus areas: " + toString(parameters.getFocusAreas()));
      List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);
      Log.i(TAG, "Setting focus area to : " + toString(middleArea));
      parameters.setFocusAreas(middleArea);
    } else {
      Log.i(TAG, "Device does not support focus areas");
    }
  }

  public static void setMetering(Camera.Parameters parameters) {
    if (parameters.getMaxNumMeteringAreas() > 0) {
      Log.i(TAG, "Old metering areas: " + parameters.getMeteringAreas());
      List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);
      Log.i(TAG, "Setting metering area to : " + toString(middleArea));
      parameters.setMeteringAreas(middleArea);
    } else {
      Log.i(TAG, "Device does not support metering areas");
    }
  }

  private static List<Camera.Area> buildMiddleArea(int areaPer1000) {
    return Collections.singletonList(
        new Camera.Area(new Rect(-areaPer1000, -areaPer1000, areaPer1000, areaPer1000), 1));
  }

  public static void setVideoStabilization(Camera.Parameters parameters) {
    if (parameters.isVideoStabilizationSupported()) {
      if (parameters.getVideoStabilization()) {
        Log.i(TAG, "Video stabilization already enabled");
      } else {
        Log.i(TAG, "Enabling video stabilization...");
        parameters.setVideoStabilization(true);
      }
    } else {
      Log.i(TAG, "This device does not support video stabilization");
    }
  }

  public static void setBarcodeSceneMode(Camera.Parameters parameters) {
    if (Camera.Parameters.SCENE_MODE_BARCODE.equals(parameters.getSceneMode())) {
      Log.i(TAG, "Barcode scene mode already set");
      return;
    }
    String sceneMode = findSettableValue("scene mode",
                                         parameters.getSupportedSceneModes(),
                                         Camera.Parameters.SCENE_MODE_BARCODE);
    if (sceneMode != null) {
      parameters.setSceneMode(sceneMode);
    }
  }

  public static void setZoom(Camera.Parameters parameters, double targetZoomRatio) {
    if (parameters.isZoomSupported()) {
      Integer zoom = indexOfClosestZoom(parameters, targetZoomRatio);
      if (zoom == null) {
        return;
      }
      if (parameters.getZoom() == zoom) {
        Log.i(TAG, "Zoom is already set to " + zoom);
      } else {
        Log.i(TAG, "Setting zoom to " + zoom);
        parameters.setZoom(zoom);
      }
    } else {
      Log.i(TAG, "Zoom is not supported");
    }
  }

  private static Integer indexOfClosestZoom(Camera.Parameters parameters, double targetZoomRatio) {
    List<Integer> ratios = parameters.getZoomRatios();
    Log.i(TAG, "Zoom ratios: " + ratios);
    int maxZoom = parameters.getMaxZoom();
    if (ratios == null || ratios.isEmpty() || ratios.size() != maxZoom + 1) {
      Log.w(TAG, "Invalid zoom ratios!");
      return null;
    }
    double target100 = 100.0 * targetZoomRatio;
    double smallestDiff = Double.POSITIVE_INFINITY;
    int closestIndex = 0;
    for (int i = 0; i < ratios.size(); i++) {
      double diff = Math.abs(ratios.get(i) - target100);
      if (diff < smallestDiff) {
        smallestDiff = diff;
        closestIndex = i;
      }
    }
    Log.i(TAG, "Chose zoom ratio of " + (ratios.get(closestIndex) / 100.0));
    return closestIndex;
  }

  public static void setInvertColor(Camera.Parameters parameters) {
    if (Camera.Parameters.EFFECT_NEGATIVE.equals(parameters.getColorEffect())) {
      Log.i(TAG, "Negative effect already set");
      return;
    }
    String colorMode = findSettableValue("color effect",
                                         parameters.getSupportedColorEffects(),
                                         Camera.Parameters.EFFECT_NEGATIVE);
    if (colorMode != null) {
      parameters.setColorEffect(colorMode);
    }
  }

  public static Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {

    List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
    if (rawSupportedSizes == null) {
      Log.w(TAG, "Device returned no supported preview sizes; using default");
      Camera.Size defaultSize = parameters.getPreviewSize();
      if (defaultSize == null) {
        throw new IllegalStateException("Parameters contained no preview size!");
      }
      return new Point(defaultSize.width, defaultSize.height);
    }

    // Sort by size, descending
    List<Camera.Size> supportedPreviewSizes = new ArrayList<>(rawSupportedSizes);
    Collections.sort(supportedPreviewSizes, new Comparator<Camera.Size>() {
      @Override
      public int compare(Camera.Size a, Camera.Size b) {
        int aPixels = a.height * a.width;
        int bPixels = b.height * b.width;
        if (bPixels < aPixels) {
          return -1;
        }
        if (bPixels > aPixels) {
          return 1;
        }
        return 0;
      }
    });

    if (Log.isLoggable(TAG, Log.INFO)) {
      StringBuilder previewSizesString = new StringBuilder();
      for (Camera.Size supportedPreviewSize : supportedPreviewSizes) {
        previewSizesString.append(supportedPreviewSize.width).append('x')
            .append(supportedPreviewSize.height).append(' ');
      }
      Log.i(TAG, "Supported preview sizes: " + previewSizesString);
    }

    double screenAspectRatio = (double) screenResolution.x / (double) screenResolution.y;

    // Remove sizes that are unsuitable
    Iterator<Camera.Size> it = supportedPreviewSizes.iterator();
    while (it.hasNext()) {
      Camera.Size supportedPreviewSize = it.next();
      int realWidth = supportedPreviewSize.width;
      int realHeight = supportedPreviewSize.height;
      if (realWidth * realHeight < MIN_PREVIEW_PIXELS) {
        it.remove();
        continue;
      }

      boolean isCandidatePortrait = realWidth < realHeight;
      int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
      int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
      double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
      double distortion = Math.abs(aspectRatio - screenAspectRatio);
      if (distortion > MAX_ASPECT_DISTORTION) {
        it.remove();
        continue;
      }

      if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {
        Point exactPoint = new Point(realWidth, realHeight);
        Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint);
        return exactPoint;
      }
    }

    // If no exact match, use largest preview size. This was not a great idea on older devices because
    // of the additional computation needed. We're likely to get here on newer Android 4+ devices, where
    // the CPU is much more powerful.
    if (!supportedPreviewSizes.isEmpty()) {
      Camera.Size largestPreview = supportedPreviewSizes.get(0);
      Point largestSize = new Point(largestPreview.width, largestPreview.height);
      Log.i(TAG, "Using largest suitable preview size: " + largestSize);
      return largestSize;
    }

    // If there is nothing at all suitable, return current preview size
    Camera.Size defaultPreview = parameters.getPreviewSize();
    if (defaultPreview == null) {
      throw new IllegalStateException("Parameters contained no preview size!");
    }
    Point defaultSize = new Point(defaultPreview.width, defaultPreview.height);
    Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize);
    return defaultSize;
  }

  private static String findSettableValue(String name,
                                          Collection<String> supportedValues,
                                          String... desiredValues) {
    Log.i(TAG, "Requesting " + name + " value from among: " + Arrays.toString(desiredValues));
    Log.i(TAG, "Supported " + name + 
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值