Zxing中的camera

作者: 暴风雨1024 | 来源:发表于2017-05-18 10:23 被阅读0次

    现在的工作需要用到camera模块,所以打算分析Zxing中的camera实现,来了解android的camera。zxing有完整的camera控制。

    废话不多说,开始camera的分析之旅!

    一.源码下载  https://github.com/zxing/zxing

    二.CameraManager类,就是我们要分析的第一个类。

    接下来看一下CameraManager这个类为我们提供了什么方法

    1.openDriver(SurfaceHolder holder)  打开摄像头

    2.isOpen()  判断摄像头是否打开

    3.closeDriver() 关闭摄像头

    4.startPreview() 启动预览

    5.stopPreview() 关闭预览

    6.setTorch(boolean newSetting) 设置对焦

    7.requestPreviewFrame(Handler handler, int message) 获取预览的图片(重要的一个方法)

    8.getFramingRect() 获取矩形框(根据屏幕分辨率然后按照一个固定的比例来设置方框大小)

    9.getFramingRectInPreview() 获取扫描区域的矩形框(getFramingRect是UI的显示,这是扫描的区域,其实竖屏扫描的时候,形式是正方形的框,但是获取到的图片确实长方形的)

    10.setManualCameraId(int cameraId) 设置camera 的Id

    11.setManualFramingRect(int width, int height)  设置矩形框的宽高(UI)

    12.buildLuminanceSource(byte[] data, int width, int height) 对byte[] Source进行统一的处理(不同设备,相机返回的Source是不一样的)

    三.关键代码分析

    1.打开摄像头OpenCamera

    OpenCameraInterface打开打开摄像头的控制类,OpenCamera类存放打开摄像头的对象,CameraFacing类,枚举,两个参数,前置和后置摄像头。staticOpenCameraopen方法对各种摄像头的情况做了判断,例如相判断该设备有没有摄像头,然后判断打开的摄像头是不是空,空就启动后置摄像头等,具体看下面的代码。

    public staticOpenCameraopen(intcameraId) {

    intnumCameras = Camera.getNumberOfCameras();//获取摄像头的个数

    if(numCameras ==0) {

    return null;

    }

    boolean explicitRequest = cameraId >=0;

    Camera.CameraInfo selectedCameraInfo =null;

    intindex;

    if(explicitRequest) {

    index = cameraId;

    selectedCameraInfo =newCamera.CameraInfo();

    Camera.getCameraInfo(index,selectedCameraInfo);

    }else{//启动后置摄像头

    index =0;

    while(index < numCameras) {//不同的设备,摄像头的个数不一样

    Camera.CameraInfo cameraInfo =newCamera.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 =newCamera.CameraInfo();

    Camera.getCameraInfo(0,selectedCameraInfo);

    }

    }

    if(camera ==null) {

    return null;

    }

    return newOpenCamera(index,

    camera,

    CameraFacing.values()[selectedCameraInfo.facing],

    selectedCameraInfo.orientation);

    }

    2.CameraManager类获取预览图片setOneShotPreviewCallback这个接口就是获取预览图片,previewCallback是回调接口

    public synchronized voidrequestPreviewFrame(Handler handler, intmessage) {

    OpenCamera theCamera =camera;

    if(theCamera !=null&&previewing) {

    previewCallback.setHandler(handler,message);

    theCamera.getCamera().setOneShotPreviewCallback(previewCallback);

    }

    }

    3.CameraConfigurationManager类,相机的配置管理类initFromCameraParameters初始化方法。根据不同的设备,还有横竖屏的方向,给Camera计算出最好的size。然后通过setDesiredCameraParameters方法设置Camera的配置参数,例如设置聚焦模式,照片的大小,场景,特效等等。

    一些常用的配置:

    1、setPictureFormat()方法用于设置相机照片的格式,其参数是一个字符型参数,位于PixelFormat类中,我们在这里选择PixelFormat.JPEG。)

    2、setSceneMode()方法用于设置相机场景类型,其参是是一个字符型参数,位于Parameters类中,以SCENE_MODE_开头。

    3、setZoom()方法用于设置相机焦距,其参数是一个整型的参数,该参数的范围是0到Camera.getParameters().getMaxZoom()。

    4、setPictureSize()方法用于设置相机照片的大小,参数为整型。

    5、setWhiteBalance(),方法用于设置相机照片白平衡,其参数是一个字符型参数,位于Parameters类中,以WHITE_BALANCE开头。

    6、setJpegQuality()方法用于设置相机照片的质量,其参数是一个整型参数,取值范围为1到100。

    7、setFlashMode()方法用于设置闪光灯的类型,其参数是一个字符型参数,位于Parameters类中,以FLASH_MODE_开头。

    8、setColorEffect()方法用于设置照片颜色特效的类型,其参数是一个字符型参数,位于Parameters类中,以EFFECT_开头。

    接下来看一下代码:

    void initFromCameraParameters(OpenCamera camera) {//初始化配置

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

    WindowManager manager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);

    Display display = manager.getDefaultDisplay();

    intdisplayRotation = display.getRotation();

    intcwRotationFromNaturalToDisplay;

    switch(displayRotation) { //手机的方向

    caseSurface.ROTATION_0:

    cwRotationFromNaturalToDisplay =0;

    break;

    caseSurface.ROTATION_90:

    cwRotationFromNaturalToDisplay =90;

    break;

    caseSurface.ROTATION_180:

    cwRotationFromNaturalToDisplay =180;

    break;

    ......

    cameraResolution= CameraConfigurationUtils.findBestPreviewSizeValue(parameters,screenResolution);               //寻找最好的PreviewSize

    Log.i(TAG,"Camera resolution: "+cameraResolution);

    bestPreviewSize= CameraConfigurationUtils.findBestPreviewSizeValue(parameters,screenResolution);

    ......

    if(isScreenPortrait == isPreviewSizePortrait) { //根据横竖屏给previewSizeOnScreen赋值

    previewSizeOnScreen=bestPreviewSize;

    }else{

    previewSizeOnScreen=newPoint(bestPreviewSize.y,bestPreviewSize.x);

    }

    ......

    }

    CameraConfigurationUtils.findBestPreviewSizeValue这个方法的实现逻辑:(代码不贴了)

    首先,查找手机支持的预览尺寸集合,如果集合为空,就返回默认的尺寸;否则,对尺寸集合根据尺寸的像素从小到大进行排序;

    其次,移除不满足最小像素要求的所有尺寸;

    在者,在剩余的尺寸集合中,剔除预览宽高比与屏幕分辨率宽高比之差的绝对值大于0.15的所有尺寸;

    最后,寻找能够精确的与屏幕宽高匹配上的预览尺寸,如果存在则返回该宽高比;如果不存在,则使用尺寸集合中最大的那个尺寸。如果说尺寸集合已经在前面的过滤中被全部排除,则返回相机默认的尺寸值。

    附加:UI的简单分析

    1.CaptureActivity是扫描的UI实现,数据的传递都是通过Handler实现的,设置都保存到Preference里面,PreferenceManager就是设置保存的管理类,ViewfinderView继承View,camera的UI显示就是通过这个View。

    CaptureActivity类的部分代码

    public voidonCreate(Bundle icicle) {

    super.onCreate(icicle);

    Window window = getWindow();

    window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//设置常亮

    ......

    PreferenceManager.setDefaultValues(this,R.xml.preferences, false);//设置默认的配置,文件放在xml文件夹下

    }

    看一下onResume方法

    protected void onResume() {

    //...   省略初始化的操作

    //根据配置文件来和传感器切换横竖屏的显示

    if(prefs.getBoolean(PreferencesActivity.KEY_DISABLE_AUTO_ORIENTATION, true)) {

    getCurrentOrientation();

    }else{

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);

    }

    resetStatusView();

    //...

    if(Intents.Scan.ACTION.equals(action)) {

    // 判断跳转过来的时候是否带上了宽度,高度

    if(intent.hasExtra(Intents.Scan.WIDTH) && intent.hasExtra(Intents.Scan.HEIGHT)) {

    int width = intent.getIntExtra(Intents.Scan.WIDTH,0);

    int height = intent.getIntExtra(Intents.Scan.HEIGHT,0);

    if(width >0&& height >0) {

    cameraManager.setManualFramingRect(width,height);

    }

    }

    //... 省略一些传参和赋值

    SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);

    SurfaceHolder surfaceHolder = surfaceView.getHolder();

    if(hasSurface) {

    initCamera(surfaceHolder);

    //最后初始化相机(主要就是这两个方法,打开相机和初始化Handler

    //cameraManager.openDriver(surfaceHolder);

    //handler=new CaptureActivityHandler(this,decodeFormats,decodeHints,characterSet,cameraManager);)

    }

    2.其他还有对图片的处理类PlanarYUVLuminanceSource和HybridBinarizer类,不同的设备获取的照片是不一样的,所以必须要做处理

    总结:camera部分就分析到这里,如果上述有错误,请指出,谢谢!

    此文由暴风雨1024原创, 转载,请注明出处,谢谢!

    相关文章

      网友评论

        本文标题:Zxing中的camera

        本文链接:https://www.haomeiwen.com/subject/elabtxtx.html