Android 处理Camera图像并绘制处理结果

这里为了介绍如何处理Camera的图像并绘制处理的结果,举这样一个例子:
对一帧图像,计算它的清晰度,然后把清晰度显示出来。
假设有一帧1280x960的图像,我们取中间400x400的区域,计算这块区域的清晰度,示意图如下:这里写图片描述

计算清晰度的方法非常简单,它不是我们的重点。计算的方法如图所示:
这里写图片描述
对于图中的这九个点,中间点的像素值*10,其他点的像素值按照途中标注的系数进行加权,然后把所有的结果相加。相加的结果的平方运算就是我们的清晰度。
代码如下:

    private long  clarityCalculator(byte[] data,int width,int height){
        long rest = 0;
        long restF = 0;
        if(width<400 || height< 400)return 0;
        int startX = (width-400)/2;
        int startY = (height-400)/2;
        int startBase = startY*1280+startX;
        for(int i=0;i<100;i+=4){
            for(int j=0;j<100;j+=4){
                rest = 10*data[startBase+j*width+i];
                rest -= 4*data[startBase+j*width+i+1];
                rest -= 4*data[startBase+(j+1)*width+j];
                rest -= data[startBase+(j-1)*width+i+1];
                rest -= data[startBase+(j+1)*width+i+1];
                restF+=rest*rest;
            }
        }
        return restF/10000;
    }

最后除以10000是因为这个数可能很大。
那么我们怎么实现这个项目呢?

第一步,获取一帧图像

获取一帧图像需要做如下事情。

首先,创建一个Surfaceview

mCameraSuface = (SurfaceView) findViewById(R.id.camera_surface);

它在布局文件中占满屏幕的。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.sharenew.cameraclarity.MainActivity">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <SurfaceView
            android:id="@+id/camera_surface"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
</RelativeLayout>

其次,获得它的Surface的holder,并给它设置事件回调接口

        holder = mCameraSuface.getHolder();
        holder.addCallback(this);

addCallback设置好以后,当SurfaceView装备好了以后,就会回调下面的三个方法:

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
    }

    @Override
    public void  surfaceChanged(SurfaceHolder holder, int format, int width, int height){

    }

    }
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
    }

接下来,我们要在这三个方法中打开摄像头并出示参数。一般的,我们会在surfaceCreated方法中打开摄像头, surfaceChanged中设置参数并开启预览,在surfaceDestroyed中关闭摄像头并释放资源。这三个方法可能会写成下面的样子,更详细的细节请参考api文档:

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.i("jw_liu","surfaceCreated");
        if(mCamera==null)
            mCamera = Camera.open();
        try {
            mCamera.setPreviewDisplay(holder);
            initCamera();
            mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                Camera.Size csize;
                @Override
                public void onPreviewFrame(byte[] data, Camera camera) {
                    // Log.i("sharenew","onPreviewFrame");
                    csize = mCamera.getParameters().getPreviewSize();
                    long clarity = clarityCalculator(data,csize.width,csize.height);
                    mDraw.setClarity(clarity);
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.i("jw_liu","surfaceChanged");
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if(success){
                    initCamera();//实现相机的参数初始化
                    camera.cancelAutoFocus();//只有加上了这一句,才会自动对焦。
                }
            }

        });
    }
    //相机参数的初始化设置
    private void initCamera()
    {
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPictureFormat(PixelFormat.JPEG);
        parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP);
        parameters.setColorEffect(Camera.Parameters.EFFECT_MONO);
        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);//1连续对
        parameters.setPreviewSize(1280,960); // ָ
        mCamera.setDisplayOrientation(0);
        mCamera.setParameters(parameters);
        mCamera.startPreview();
        mCamera.cancelAutoFocus();// 2如果要实现连续的自动对焦,这一句必须加上
    }
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.i("jw_liu","surfaceDestroyed");
        mCamera.stopPreview();
        mCamera.release();
    }

很重要的就是我们给camera设置了PreviewCallback回调方法,对于每一帧的图像,都会回调这里的onPreviewFrame方法。我们在这里处理图像。处理的方法就是调用我们一开始介绍的计算清晰度的方法。处理结果保存在一个View的变量中,接下来我们会在这个View中显示清晰度。

更新绘制

我们每隔一秒钟更新一次结果,这使用的是Handler:

    Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what){
                case 11:
                    mDraw.invalidate();
                    mHandler.sendEmptyMessageDelayed(11,1000);
                    break;
            }
            return true;
        }
    });

绘制处理结果

因为我们已经让SurfaceView占满屏幕了,那么我们怎么绘制呢?这里用到了Activity的addContentView方法。这个方法定义如下:

    /**
     * Add an additional content view to the activity.  Added after any existing
     * ones in the activity -- existing views are NOT removed.
     *
     * @param view The desired content to display.
     * @param params Layout parameters for the view.
     */
    public void addContentView(View view, ViewGroup.LayoutParams params) {
        getWindow().addContentView(view, params);
        initWindowDecorActionBar();
    }

他能让Activity显示另一view,和已经存在的views一同被显示。
我们添加的参数如下:

addContentView(mDraw, new ActionBar.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.MATCH_PARENT));

添加的是一个mDraw的View,这个View就是一个普通的View,如下:

public class Draw_Tips extends View {
    long clarity=0;
 public Draw_Tips(Context context) {
    super(context);
 }
 public void setClarity(long clarity){
     this.clarity = clarity;
 }
 @Override
 protected void onDraw(Canvas canvas)
 {
    // TODO Auto-generated method stub
    Paint mPaint = new Paint();
     mPaint.setTextSize(40);
     mPaint.setColor(Color.BLUE);
     canvas.drawText(String.valueOf(clarity),200,60,mPaint);
    super.onDraw(canvas);
 }

}

这样,我们把每一帧图像的清晰度保存在Draw_Tips 类的clarity变量中,然后使用Handler每隔一秒钟绘制一次清晰度。整个界面如下:
这里写图片描述

最后,完整的代码如下:
MainActivity.java



import java.io.IOException;

import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.CameraIrisCallback;
import android.hardware.Camera.Parameters;
import android.util.Log;
import android.view.SurfaceHolder;

public class Iris_Camera {
    private static Camera myCamera;
    private static Iris_Camera myInstance=null;
    private Boolean  isPreview=false;
    private String Tag="IrisCameraManager";

    public static  Iris_Camera getInstance()
    {
        if(myInstance == null)
        {   
            synchronized(Iris_Camera.class)
            {
                myInstance = new Iris_Camera();
            }
        }
        return myInstance;
    }

    @SuppressWarnings("deprecation")
    private Iris_Camera()
    {
        myCamera = Camera.open();
    }



    @SuppressWarnings("deprecation")
    public void InitCamera(int nWorkMode,int nDisplayMode,CameraIrisCallback mIrisCallback)
    {
        if (isPreview && myCamera!=null)
        {
            myCamera.stopPreview();         //stopCamera();
        }

        if(null!= myCamera)
        {
            try
            { 
                 Parameters parameters = myCamera.getParameters();
                 parameters.setPictureFormat(PixelFormat.JPEG);
                 parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP);
                 parameters.setColorEffect(Parameters.EFFECT_MONO); 


                 parameters.setPreviewSize(1280, 960); // ָ

                 if (nDisplayMode==0) 
                 {
                      parameters.set("orientation", "portrait"); //
                      parameters.set("rotation", 90); 
                      myCamera.setDisplayOrientation(90); 
                 }
                 else
                 {
                      parameters.set("orientation", "landscape"); //
                      myCamera.setDisplayOrientation(0); 
                 } 

                 myCamera.setParameters(parameters);  
                 myCamera.setIrisCallback(mIrisCallback);
                 //myCamera.setPreviewCallback(mPreviewCallback);


                 myCamera.startPreview(); 
                 isPreview = true;

                 Camera.Size csize = myCamera.getParameters().getPreviewSize();
            }
            catch (Exception e)
            { 
                e.printStackTrace();
            }
        }
        else
        {
              RefreshCamera();
        }
    }

    @SuppressWarnings("deprecation")
    private void RefreshCamera()
    {
        if(myCamera!=null)
        {
            myCamera.release();
        }
        myCamera = Camera.open();
    }

    @SuppressWarnings("deprecation")
    public void ReleaseCamera()
    {
        if(myCamera!=null)
        {
            myCamera.setIrisCallback(null);
            myCamera.stopPreview();
            Log.i(Tag,"ThomasYang Stop View finish");
            isPreview =false;
            myCamera.release();
            myCamera=null;
        }
    }

    @SuppressWarnings("deprecation")
    public void OpenCamera(SurfaceHolder holder) 
    {
        if(myCamera==null)
        {
            Log.i(Tag,"open camera");
            myCamera = Camera.open();
        }

        try
        {
            if(myCamera!=null)
                myCamera.setPreviewDisplay(holder);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

Draw_Tips.java


import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.View;

public class Draw_Tips extends View {
    long clarity=0;
 public Draw_Tips(Context context) {
    super(context);
 }
 public void setClarity(long clarity){
     this.clarity = clarity;
 }
 @Override
 protected void onDraw(Canvas canvas)
 {
    // TODO Auto-generated method stub
    Paint mPaint = new Paint();
     mPaint.setTextSize(40);
     mPaint.setColor(Color.BLUE);
     canvas.drawText(String.valueOf(clarity),200,60,mPaint);
    super.onDraw(canvas);
 }

}

布局文件-activity_mian.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.sharenew.cameraclarity.MainActivity">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <SurfaceView
            android:id="@+id/camera_surface"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
</RelativeLayout>
发布了116 篇原创文章 · 获赞 247 · 访问量 54万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章