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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章