心电图的程序解读-黄

日期: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;
	}
}


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