心電圖的程序解讀-黃

日期:2014年11月1日—2014年11月7日

1.本週已完成任務:解讀使用攝像頭讀取心電圖的程序

2.本週未完成任務:項目整體規劃與可行性論證

3.下週計劃:使用攝像頭讀出具體的心跳數

4.關鍵技術點說明:

程序來源:

http://download.csdn.net/download/u010967074/6023749

簡單地來說測心跳的原理非常簡單,就是使用攝像頭與閃光燈觀察血液顏色的變化,而這個程序只是將顏色變化顯示在屏幕上,並沒有測量實際的心跳數,而且寫得很混亂、多餘。

以下是代碼註釋

package prox.yuv420sp2rgb;

import java.util.concurrent.atomic.AtomicBoolean;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

public class yuv420sp2rgb extends Activity 
{
	SurfaceView showDataSurfaceView=null;
	SurfaceHolder mSurfaceHolder=null;
	
	int centerY;//中心線
    int oldX=0;
    int oldY=250;
    int newX=0;
    int newY=250;//上一個XY 點
    int currentX;//當前繪製到的X 軸上的點
	int ScreenWidth;
	int length=0;
	DisplayMetrics dm;
	private static String msg = "";
	
	private static final String TAG = "HeartRateMonitor";
	private static final AtomicBoolean processing = new AtomicBoolean(false);

	private static SurfaceView preview = null;
	private static SurfaceHolder previewHolder = null;
	private static Camera camera = null;
	private static View image = null;
	private static ImageView resultimageview=null;
	private static TextView text = null;

	private static WakeLock wakeLock = null;
	
	private static int averageIndex = 0;
	private static final int averageArraySize = 4;
	private static final int[] averageArray = new int[averageArraySize];

	public static enum TYPE { GREEN, RED };
	private static TYPE currentType = TYPE.GREEN;
	public static TYPE getCurrent() {
		return currentType;
	}

	private static int beatsIndex = 0;
	private static final int beatsArraySize = 3;
	private static final int[] beatsArray = new int[beatsArraySize];
	private static double beats = 0;
	private static long startTime = 0;
	
	static 
	{
		System.loadLibrary("HeartBeatProcessFun");
	}
	
	public native int DecodeYUV420SP2RGB(int[] bitmapDataBufJava,byte[] rgbBufJava, byte[] yuv420spJava,int width, int height,int[] RGBYValueJava);
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
    	super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        //聯繫控件,這裏使用了兩個SurfaceView,一個用來顯示攝像頭圖像,一個用來顯示心跳曲線
		preview = (SurfaceView)findViewById(R.id.preview);
		previewHolder = preview.getHolder();//獲得SurfaceHolder對象
		previewHolder.addCallback(surfaceCallback);//添加回調函數
		
		//SURFACE_TYPE_PUSH_BUFFERS:表明該Surface不包含原生數據,Surface用到的數據由其他對象提供
		//在Camera圖像預覽中就使用該類型的Surface,有Camera負責提供給預覽Surface數據,這樣圖像預覽會比較流暢。
		//如果設置這種類型則就不能調用lockCanvas來獲取Canvas對象了。
		previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

		resultimageview=(ImageView)findViewById(R.id.resultimageview);
		
		//PowerManager,可以參考http://blog.csdn.net/chenzujie/article/details/12906517
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "DoNotDimScreen");
        
        //顯示心跳曲線的SurfaceView
        showDataSurfaceView=(SurfaceView)findViewById(R.id.showView);
        mSurfaceHolder=showDataSurfaceView.getHolder();
        centerY=150;
        dm = new DisplayMetrics();//DisplayMetrics用來獲取屏幕的分辨率,打點打到結束後返回0
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        ScreenWidth=dm.widthPixels;//屏幕的橫向分辨率
    }
    
    void drawline()//畫線(點)
    {
    	//獲取Canvas,鎖定畫布
    	Canvas canvas = mSurfaceHolder.lockCanvas(new Rect(oldX, 0, newX,dm.heightPixels));
    	Paint mPaint=new Paint();
    	mPaint.setColor(Color.GREEN);//設定爲綠色
    	mPaint.setStrokeWidth(1);//線寬爲1像素
    	canvas.drawLine(oldX, oldY, newX, newY, mPaint);//Canvas繪畫
    	oldX=newX;
    	oldY=newY;
    	mSurfaceHolder.unlockCanvasAndPost(canvas);//結束鎖定畫圖,並提交改變,將圖形顯示
    }
    
    void ClearDraw() //清除畫布
    {
        Canvas canvas = mSurfaceHolder.lockCanvas();
        canvas.drawColor(Color.BLACK);// 清除畫布
        mSurfaceHolder.unlockCanvasAndPost(canvas);
    }
	
    private Handler DrawHandler = new Handler() //畫圖Handler
	{
		public void handleMessage(Message m) 
		{
			//msgEdit.append(msg);
			//Log.d("msg",msg);
			//畫點
			drawline();
			currentX++;
			newX++;
			//newY=Integer.valueOf(msg, 16);
			//Red的值在0-255之間!!!->大概在215左右變化!!!->穩定之後!!!
			//newY=(int) Math.abs(490-((480.0)/255*Integer.valueOf(msg,10)+10));
			//newY=Integer.valueOf(msg,10);
			
			//newY=(int) Math.abs(Integer.valueOf(msg,10)-200)*2;
			//newY=Integer.valueOf(msg,10);
			//Log.d("chenxupro", String.valueOf(newY));
			newY=Integer.valueOf(msg,10)*10-2000;//根據msg的值來確定當前的Y座標
			
			//newY=200;
			
			Log.d("chenxuro", msg);
			if(newX==ScreenWidth-1)//當X座標超過屏幕橫向分辨率的時候清除屏幕,然後返回0
			{
				ClearDraw();
				currentX=0;
				oldX=0;
				newX=0;
			}
		}
	};
	
	//------------------------------------------------------------
	
	@Override
	public void onConfigurationChanged(Configuration newConfig) 
	{
		super.onConfigurationChanged(newConfig);
	}
    
	@Override
	public void onResume() 
	{
		super.onResume();
		wakeLock.acquire();
		camera = Camera.open();//打開攝像頭
		startTime = System.currentTimeMillis();
	}
    
	@Override
	public void onPause() 
	{
		super.onPause();
		wakeLock.release();
		camera.setPreviewCallback(null);
		camera.stopPreview();//停止攝像頭
		camera.release();
		camera = null;
	}
	
	private PreviewCallback previewCallback = new PreviewCallback() //攝像頭回調
	{
		@Override
		public void onPreviewFrame(byte[] data, Camera cam) //攝像頭幀預覽
		{
			if (data == null) 
				throw new NullPointerException();
			Camera.Size size = cam.getParameters().getPreviewSize();
			if (size == null) 
				throw new NullPointerException();
			int width = size.width;
			int height = size.height;
			
	        //關閉攝像頭預覽回調
	        camera.setPreviewCallback(null);
	        
	        int previewWidth=0;
	        int previewHeight=0;
	        
	        int[] bitmapData=null;
	        byte[] rgbBuffer=null;
	        
	        if (data != null) //獲取預覽圖片的信息
	        {
	            previewWidth = camera.getParameters().getPreviewSize().width;   
	            previewHeight = camera.getParameters().getPreviewSize().height;   
	            bitmapData = new int[previewWidth * previewHeight];
	            rgbBuffer = new byte[previewWidth * previewHeight * 3];   //RGB888!!!->3 bytes  
	        }   
	        
	        //int CurrentRedSum=decodeYUV420SP(bitmapData, rgbBuffer, data, previewWidth, previewHeight);   
	        //int CurrentRedAverage=CurrentRedSum/(previewWidth * previewHeight);
	        int[] RGBYValueArray=new int[4];
	        
	        //將圖像轉換成RGB格式,並獲取RGB數組
	        //這裏很奇怪,沒有調用自己寫的函數,反而使用了JNI的。自己寫的函數在上面被註釋掉了。
	        DecodeYUV420SP2RGB(bitmapData,rgbBuffer,data,previewWidth,previewHeight,RGBYValueArray);
	        
			msg  = String.valueOf(RGBYValueArray[0]);//獲取紅色部分的數據
			DrawHandler.sendEmptyMessage(0);//啓動繪圖
	        
	        //Log.d("chenxupro",String.valueOf(CurrentRedSum)+","+String.valueOf(CurrentRedAverage));
	        
	        //取得攝像頭畫面
	        //Bitmap resultImg=Bitmap.createBitmap(w, h, Config.RGB_565);
	        ////Bitmap image = Bitmap.createBitmap(bitmapData, previewWidth, previewHeight, Bitmap.Config.ARGB_8888);
	        
	        ////Log.d("chenxupro", String.valueOf(previewWidth)+","+String.valueOf(previewHeight));
	        //image.setPixels(bitmapData, 0, previewWidth, 0, 0,previewWidth, previewHeight);
	        //resultimageview
	        ////resultimageview.setImageBitmap(image);  
	        
	        //打開攝像頭預覽回調
	        camera.setPreviewCallback(this);   
		}
	};
	
	private SurfaceHolder.Callback surfaceCallback=new SurfaceHolder.Callback() //SurfaceView回調函數
	{    
		@Override
		public void surfaceCreated(SurfaceHolder holder) 
		{
			try 
			{
				camera.setPreviewDisplay(previewHolder);//將camera連接到一個SurfaceView,準備實時預覽
				camera.setPreviewCallback(previewCallback);//添加回調函數
			} 
			catch (Throwable t) 
			{
				Log.e("PreviewDemo-surfaceCallback", "Exception in setPreviewDisplay()", t);
			}
		}
	    
		@Override
		public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
		{
			Camera.Parameters parameters = camera.getParameters();//獲取camera界限
			parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);//打開閃光燈
			Camera.Size size = getSmallestPreviewSize(width, height, parameters);//得到最小尺寸預覽
			if (size!=null) //設置預覽大小
			{
				//parameters.setPreviewSize(size.width, size.height);//這裏應該纔是本意
				parameters.setPreviewSize(352, 288);//這裏這樣前面獲取最小尺寸預覽顯得很莫名其妙
				parameters.setPreviewFrameRate(5);//每秒5幀
				Log.d(TAG, "Using width="+size.width+" height="+size.height);
			}
			camera.setParameters(parameters);//設置camera界限
			camera.startPreview();//開始預覽
		}
	    
		@Override
		public void surfaceDestroyed(SurfaceHolder holder) 
		{
			//Ignore???
			//android.os.Process.killProcess(android.os.Process.myPid());
		}
	};
	//得到最小尺寸預覽
	private static Camera.Size getSmallestPreviewSize(int width, int height, Camera.Parameters parameters) 
	{
		Camera.Size result=null;

		for (Camera.Size size : parameters.getSupportedPreviewSizes()) 
		{
			if (size.width<=width && size.height<=height) 
			{
				if (result==null) 
				{
					result=size;
				} 
				else 
				{
					int resultArea=result.width*result.height;
					int newArea=size.width*size.height;

					if (newArea<resultArea) 
						result=size;
				}
			}
		}
		return result;
	}
    //獲得Red數據和,將yuv420sp轉換爲rgb和ARGB
	static public int decodeYUV420SP(int[] bitmapDataBuf,byte[] rgbBuf, byte[] yuv420sp,int width, int height) 
	{
		int RedSum=0;
		
		final int frameSize = width * height;
		if (rgbBuf == null)
			throw new NullPointerException("buffer 'rgbBuf' is null");
		if (rgbBuf.length < frameSize * 3)
			throw new IllegalArgumentException("buffer 'rgbBuf' size "+ rgbBuf.length + " < minimum " + frameSize * 3);

		if (yuv420sp == null)
			throw new NullPointerException("buffer 'yuv420sp' is null");
		//YUV不是分成3個平面而是分成2個平面。Y數據一個平面,UV數據合用一個平面。UV平面的數據格式是UVUVUV...。
		if (yuv420sp.length < frameSize * 3 / 2)
			throw new IllegalArgumentException("buffer 'yuv420sp' size "
					+ yuv420sp.length + " < minimum " + frameSize * 3 / 2);

		int i = 0, y = 0;
		int uvp = 0, u = 0, v = 0;
		int y1192 = 0, r = 0, g = 0, b = 0;

		for (int j = 0, yp = 0; j < height; j++) {
			uvp = frameSize + (j >> 1) * width;
			u = 0;
			v = 0;
			for (i = 0; i < width; i++, yp++) {
				y = (0xff & ((int) yuv420sp[yp])) - 16;
				if (y < 0)
					y = 0;
				if ((i & 1) == 0) {
					v = (0xff & yuv420sp[uvp++]) - 128;
					u = (0xff & yuv420sp[uvp++]) - 128;
				}

				y1192 = 1192 * y;
				r = (y1192 + 1634 * v);
				g = (y1192 - 833 * v - 400 * u);
				b = (y1192 + 2066 * u);

				if (r < 0)
					r = 0;
				else if (r > 262143)
					r = 262143;
				if (g < 0)
					g = 0;
				else if (g > 262143)
					g = 262143;
				if (b < 0)
					b = 0;
				else if (b > 262143)
					b = 262143;

				rgbBuf[yp * 3] = (byte) (r >> 10);
				rgbBuf[yp * 3 + 1] = (byte) (g >> 10);
				rgbBuf[yp * 3 + 2] = (byte) (b >> 10);
				
				//把原來的rgbbuffer,轉成用於生成bitmap的buffer,格式是ARGB
				bitmapDataBuf[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
				int currentred = (bitmapDataBuf[yp] >> 16) & 0xff;
				RedSum+=currentred;
			}
		}
		return RedSum;
	}
	//獲得Red數據和,相比與上面的函數就只有獲取數據和
	private static int decodeYUV420SPtoRedSum(byte[] yuv420sp, int width, int height) 
	{
		if (yuv420sp==null) 
			return 0;
		final int frameSize = width * height;
		int sum = 0;
	    for (int j = 0, yp = 0; j < height; j++) {
	        int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
	        for (int i = 0; i < width; i++, yp++) {
	            int y = (0xff & ((int) yuv420sp[yp])) - 16;
	            if (y < 0) y = 0;
	            if ((i & 1) == 0) {
	                v = (0xff & yuv420sp[uvp++]) - 128;
	                u = (0xff & yuv420sp[uvp++]) - 128;
	            }
	            int y1192 = 1192 * y;
	            int r = (y1192 + 1634 * v);
	            int g = (y1192 - 833 * v - 400 * u);
	            int b = (y1192 + 2066 * u);

	            if (r < 0) r = 0; else if (r > 262143) r = 262143;
	            if (g < 0) g = 0; else if (g > 262143) g = 262143;
	            if (b < 0) b = 0; else if (b > 262143) b = 262143;

	            int pixel = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
	            int red = (pixel >> 16) & 0xff;
	            sum+=red;
	        }
	    }
	    return sum;
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章