Android 闪光灯测心率
好久没有更新了。最近超级得忙。终于做了父亲。为了孩子忙前忙后的。一直忘了这个存在,最近闲暇。听朋友说了一个闪光灯测试心率的功能。问我会不会。
进入正题:
闪光灯测试心率是可以参考的,血压,脉搏,血氧,啥的都是不靠谱的。建议去医院做。
public class AitestActivity extends MvpActivity<AiView, AiPresenter> implements AiView, View.OnClickListener { @BindView(R.id.currentHeartRateXueya) TextView currentHeartRateXueya; @BindView(R.id.currentHeartRateXueyang) TextView currentHeartRateXueyang; @BindView(R.id.currentHeartRateXueye) TextView currentHeartRateXueye; @BindView(R.id.currentHeartRateBmi) TextView currentHeartRateBmi; private TextView currentHeartRate; // 当前心率 private Button btnStart; //按钮 开始 private Button btnStop; //按钮 停止 DecimalFormat df = new DecimalFormat("######0.00"); private final AtomicBoolean processing = new AtomicBoolean(false); //Android手机预览控件 private SurfaceView preview = null; //预览设置信息 private SurfaceHolder previewHolder = null; //Android手机相机句柄 private Camera camera = null; private PowerManager.WakeLock wakeLock = null; private int averageIndex = 0; private final int averageArraySize = 4; private final int[] averageArray = new int[averageArraySize]; private AiPresenter homePresenter; private Context mContext; private Timer timer; private TimerTask task; @Override public AiPresenter createPrenter() { mContext = AitestActivity.this; homePresenter = new AiPresenter(); return homePresenter; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // TODO: add setContentView(...) invocation ButterKnife.bind(this); } @Override public void requestSuccess(JSONObject jsonObject) { Toast.makeText(AitestActivity.this,"保存体检记录成功",Toast.LENGTH_SHORT).show(); finish(); } @Override public void requestFailed() { Toast.makeText(AitestActivity.this,"保存体检记录失败,请重试",Toast.LENGTH_SHORT).show(); finish(); } /** * 类型枚举 */ public static enum TYPE { GREEN, RED } Handler handler=new Handler(); Runnable runnable=new Runnable(){ @Override public void run() { // TODO Auto-generated method stub //要做的事情 Calendar calendar=Calendar.getInstance();//年 int year = calendar.get(Calendar.YEAR);//月 int month = calendar.get(Calendar.MONTH)+1;//日 int day = calendar.get(Calendar.DAY_OF_MONTH); String xl = currentHeartRate.getText().toString().replaceAll("当前心率:", ""); String xy = currentHeartRateXueya.getText().toString().replaceAll("当前血压:", ""); String xuey = currentHeartRateXueyang.getText().toString().replaceAll("当前血氧:", ""); String xye = currentHeartRateXueye.getText().toString().replaceAll("当前血液黏度:", ""); String bmi = currentHeartRateBmi.getText().toString().replaceAll("当前BMI:", ""); homePresenter.getInviteCode(""+year,""+month,""+day,""+xl,""+xy,""+xuey,""+xye,""+bmi); handler.postDelayed(this, 5000); } }; //设置默认类型 private static TYPE currentType = TYPE.GREEN; //心跳下标值 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; public static int heartRate = 0; private void initEvent() { btnStart.setOnClickListener(this); btnStop.setOnClickListener(this); timer = new Timer(); task = new TimerTask() { @Override public void run() { timer.cancel(); Calendar calendar=Calendar.getInstance();//年 int year = calendar.get(Calendar.YEAR);//月 int month = calendar.get(Calendar.MONTH)+1;//日 int day = calendar.get(Calendar.DAY_OF_MONTH); String xl = currentHeartRate.getText().toString().replaceAll("当前心率:", ""); String xy = currentHeartRateXueya.getText().toString().replaceAll("当前血压:", ""); String xuey = currentHeartRateXueyang.getText().toString().replaceAll("当前血氧:", ""); String xye = currentHeartRateXueye.getText().toString().replaceAll("当前血液黏度:", ""); String bmi = currentHeartRateBmi.getText().toString().replaceAll("当前BMI:", ""); homePresenter.getInviteCode(""+year,""+month,""+day,""+xl,""+xy,""+xuey,""+xye,""+bmi); } }; } @SuppressLint("InvalidWakeLockTag") private void initView1() { currentHeartRate = (TextView) findViewById(R.id.currentHeartRate); btnStart = (Button) findViewById(R.id.btnStart); btnStop = (Button) findViewById(R.id.btnStop); //获取SurfaceView控件 preview = (SurfaceView) findViewById(R.id.id_preview); previewHolder = preview.getHolder(); previewHolder.addCallback(surfaceCallback); previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "DoNotDimScreen"); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btnStart: // handler.postDelayed(runnable, 10000);//每两秒执行一次runnable. // timer.schedule(); timer.schedule(task,60000,60000); // 开始按钮 处理时间 // 开始隐藏,结束按钮显示 btnStart.setVisibility(View.GONE); btnStop.setVisibility(View.VISIBLE); startTime = System.currentTimeMillis(); // 开始处理心率值 camera.startPreview(); break; case R.id.btnStop: timer.cancel(); // 结束按钮 处理逻辑 // 开始按钮显示 结束按钮隐藏 btnStart.setVisibility(View.VISIBLE); btnStop.setVisibility(View.GONE); // 不再处理心率值 camera.stopPreview(); finish(); break; } } @Override protected int provideLayoutResourceID() { return R.layout.activity_aitest; } @Override protected void initializeViewsAndData() { double sg = Integer.parseInt(getIntent().getStringExtra("sg")); double tz = Integer.parseInt(getIntent().getStringExtra("tz")); double i1 = sg / 100; double v = i1 * i1; double i = tz / v; String format = df.format(i); currentHeartRateBmi.setText("当前BMI:"+format); //正常人的血液黏度男性是3.84-5.30mPa·s,女性是3.39-4.41mPa·s体重指数BMI=体重/身高的平方(国际单位kg/㎡) initView1(); // 定义控件的点击事件 initEvent(); } @Override protected void dealClickAction(View v) { } @Override public void onResume() { super.onResume(); // 开启闪光灯和摄像头 wakeLock.acquire(); camera = Camera.open(); camera.setDisplayOrientation(90); } @Override public void onPause() { super.onPause(); wakeLock.release(); camera.setPreviewCallback(null); // camera.stopPreview(); camera.release(); camera = null; } // 曲线 @Override public void onDestroy() { //当结束程序时关掉Timer if (camera != null) { camera.release(); camera = null; } super.onDestroy(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } /** * 相机预览方法 无需理解 * 这个方法中实现动态更新界面UI的功能, * 通过获取手机摄像头的参数来实时动态计算平均像素值、脉冲数,从而实时动态计算心率值。 */ private Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() { 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(); } if (!processing.compareAndSet(false, true)) { return; } int width = size.width; int height = size.height; //图像处理 int imgAvg = ImageProcessing.decodeYUV420SPtoRedAvg(data.clone(), height, width); // Log.d("aaaaaaa", "imgAvg" + imgAvg); if (imgAvg == 0 || imgAvg == 255) { processing.set(false); return; } //计算平均值 int averageArrayAvg = 0; int averageArrayCnt = 0; for (int i = 0; i < averageArray.length; i++) { if (averageArray[i] > 0) { averageArrayAvg += averageArray[i]; averageArrayCnt++; } } //计算平均值 int rollingAverage = (averageArrayCnt > 0) ? (averageArrayAvg / averageArrayCnt) : 0; // Log.d("aaaaaaa", "rollingAverage" + rollingAverage); TYPE newType = currentType; if (imgAvg < rollingAverage) { newType = TYPE.RED; if (newType != currentType) { beats++; } } else if (imgAvg > rollingAverage) { newType = TYPE.GREEN; } if (averageIndex == averageArraySize) { averageIndex = 0; } averageArray[averageIndex] = imgAvg; averageIndex++; if (newType != currentType) { currentType = newType; } //获取系统结束时间(ms) long endTime = System.currentTimeMillis(); double totalTimeInSecs = (endTime - startTime) / 1000d; if (totalTimeInSecs >= 2) { double bps = (beats / totalTimeInSecs); int dpm = (int) (bps * 60d); if (dpm < 30 || dpm > 180 || imgAvg < 200) { //获取系统开始时间(ms) startTime = System.currentTimeMillis(); //beats心跳总数 beats = 0; processing.set(false); return; } if (beatsIndex == beatsArraySize) { beatsIndex = 0; } beatsArray[beatsIndex] = dpm; beatsIndex++; int beatsArrayAvg = 0; int beatsArrayCnt = 0; for (int i = 0; i < beatsArray.length; i++) { if (beatsArray[i] > 0) { beatsArrayAvg += beatsArray[i]; beatsArrayCnt++; } } int beatsAvg = (beatsArrayAvg / beatsArrayCnt); // 这里是获取到的心率值 :currentHeartRate Log.d("aaaaa", "心率" + beatsAvg); heartRate = beatsAvg; //获取系统时间(ms) startTime = System.currentTimeMillis(); beats = 0; } processing.set(false); } }; /** * 预览回调接口 无需理解 固定代码 */ private SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() { //创建时调用 @Override public void surfaceCreated(SurfaceHolder holder) { try { camera.setPreviewDisplay(previewHolder); camera.setPreviewCallback(previewCallback); } catch (Throwable t) { } } //当预览改变的时候回调此方法 @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Camera.Parameters parameters = camera.getParameters(); parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); Camera.Size size = getSmallestPreviewSize(width, height, parameters); if (size != null) { parameters.setPreviewSize(size.width, size.height); } camera.setParameters(parameters); // camera.startPreview(); } //销毁的时候调用 @Override public void surfaceDestroyed(SurfaceHolder holder) { } }; /** * 获取相机最小的预览尺寸 无需理解, 固定代码 */ private 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; } }
这就是全部代码。
就是根据血液的红色素来测量的心率。需要调高测量时间,不然准确度太低
不想废话。。。。
public class AitestActivity extends MvpActivity<AiView, AiPresenter> implements AiView, View.OnClickListener {
@BindView(R.id.currentHeartRateXueya) TextView currentHeartRateXueya; @BindView(R.id.currentHeartRateXueyang) TextView currentHeartRateXueyang; @BindView(R.id.currentHeartRateXueye) TextView currentHeartRateXueye; @BindView(R.id.currentHeartRateBmi) TextView currentHeartRateBmi; private TextView currentHeartRate; // 当前心率 private Button btnStart; //按钮 开始 private Button btnStop; //按钮 停止 DecimalFormat df = new DecimalFormat("######0.00");
private final AtomicBoolean processing = new AtomicBoolean(false); //Android手机预览控件 private SurfaceView preview = null; //预览设置信息 private SurfaceHolder previewHolder = null; //Android手机相机句柄 private Camera camera = null; private PowerManager.WakeLock wakeLock = null; private int averageIndex = 0; private final int averageArraySize = 4; private final int[] averageArray = new int[averageArraySize]; private AiPresenter homePresenter; private Context mContext; private Timer timer; private TimerTask task;
@Override public AiPresenter createPrenter() { mContext = AitestActivity.this; homePresenter = new AiPresenter(); return homePresenter; }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // TODO: add setContentView(...) invocation ButterKnife.bind(this); }
@Override public void requestSuccess(JSONObject jsonObject) {
Toast.makeText(AitestActivity.this,"保存体检记录成功",Toast.LENGTH_SHORT).show(); finish(); }
@Override public void requestFailed() { Toast.makeText(AitestActivity.this,"保存体检记录失败,请重试",Toast.LENGTH_SHORT).show(); finish(); }
/** * 类型枚举 */ public static enum TYPE { GREEN, RED }
Handler handler=new Handler(); Runnable runnable=new Runnable(){ @Override public void run() {// TODO Auto-generated method stub//要做的事情
Calendar calendar=Calendar.getInstance();//年 int year = calendar.get(Calendar.YEAR);//月 int month = calendar.get(Calendar.MONTH)+1;//日 int day = calendar.get(Calendar.DAY_OF_MONTH);
String xl = currentHeartRate.getText().toString().replaceAll("当前心率:", ""); String xy = currentHeartRateXueya.getText().toString().replaceAll("当前血压:", ""); String xuey = currentHeartRateXueyang.getText().toString().replaceAll("当前血氧:", ""); String xye = currentHeartRateXueye.getText().toString().replaceAll("当前血液黏度:", ""); String bmi = currentHeartRateBmi.getText().toString().replaceAll("当前BMI:", "");
homePresenter.getInviteCode(""+year,""+month,""+day,""+xl,""+xy,""+xuey,""+xye,""+bmi);
handler.postDelayed(this, 5000); } };
//设置默认类型 private static TYPE currentType = TYPE.GREEN;
//心跳下标值 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;
public static int heartRate = 0;
private void initEvent() { btnStart.setOnClickListener(this); btnStop.setOnClickListener(this);
timer = new Timer(); task = new TimerTask() { @Override public void run() { timer.cancel(); Calendar calendar=Calendar.getInstance();//年 int year = calendar.get(Calendar.YEAR);//月 int month = calendar.get(Calendar.MONTH)+1;//日 int day = calendar.get(Calendar.DAY_OF_MONTH);
String xl = currentHeartRate.getText().toString().replaceAll("当前心率:", ""); String xy = currentHeartRateXueya.getText().toString().replaceAll("当前血压:", ""); String xuey = currentHeartRateXueyang.getText().toString().replaceAll("当前血氧:", ""); String xye = currentHeartRateXueye.getText().toString().replaceAll("当前血液黏度:", ""); String bmi = currentHeartRateBmi.getText().toString().replaceAll("当前BMI:", "");
homePresenter.getInviteCode(""+year,""+month,""+day,""+xl,""+xy,""+xuey,""+xye,""+bmi); } }; }
@SuppressLint("InvalidWakeLockTag") private void initView1() { currentHeartRate = (TextView) findViewById(R.id.currentHeartRate); btnStart = (Button) findViewById(R.id.btnStart); btnStop = (Button) findViewById(R.id.btnStop);
//获取SurfaceView控件 preview = (SurfaceView) findViewById(R.id.id_preview); previewHolder = preview.getHolder(); previewHolder.addCallback(surfaceCallback); previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "DoNotDimScreen");
}
@Override public void onClick(View v) { switch (v.getId()) { case R.id.btnStart:// handler.postDelayed(runnable, 10000);//每两秒执行一次runnable.// timer.schedule(); timer.schedule(task,60000,60000); // 开始按钮 处理时间 // 开始隐藏,结束按钮显示 btnStart.setVisibility(View.GONE); btnStop.setVisibility(View.VISIBLE); startTime = System.currentTimeMillis(); // 开始处理心率值 camera.startPreview(); break; case R.id.btnStop: timer.cancel(); // 结束按钮 处理逻辑 // 开始按钮显示 结束按钮隐藏 btnStart.setVisibility(View.VISIBLE); btnStop.setVisibility(View.GONE); // 不再处理心率值 camera.stopPreview();
finish(); break;
} }
@Override protected int provideLayoutResourceID() { return R.layout.activity_aitest; }
@Override protected void initializeViewsAndData() { double sg = Integer.parseInt(getIntent().getStringExtra("sg")); double tz = Integer.parseInt(getIntent().getStringExtra("tz"));
double i1 = sg / 100; double v = i1 * i1; double i = tz / v;
String format = df.format(i); currentHeartRateBmi.setText("当前BMI:"+format); //正常人的血液黏度男性是3.84-5.30mPa·s,女性是3.39-4.41mPa·s体重指数BMI=体重/身高的平方(国际单位kg/㎡) initView1(); // 定义控件的点击事件 initEvent();
}
@Override protected void dealClickAction(View v) {
}
@Override public void onResume() { super.onResume(); // 开启闪光灯和摄像头 wakeLock.acquire(); camera = Camera.open(); camera.setDisplayOrientation(90); }
@Override public void onPause() { super.onPause(); wakeLock.release(); camera.setPreviewCallback(null);// camera.stopPreview(); camera.release(); camera = null; }
//曲线 @Override public void onDestroy() { //当结束程序时关掉Timer if (camera != null) { camera.release(); camera = null; } super.onDestroy(); }
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); }
/** * 相机预览方法 无需理解 * 这个方法中实现动态更新界面UI的功能, * 通过获取手机摄像头的参数来实时动态计算平均像素值、脉冲数,从而实时动态计算心率值。 */ private Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() { 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(); } if (!processing.compareAndSet(false, true)) { return; } int width = size.width; int height = size.height;
//图像处理 int imgAvg = ImageProcessing.decodeYUV420SPtoRedAvg(data.clone(), height, width);
// Log.d("aaaaaaa", "imgAvg" + imgAvg);
if (imgAvg == 0 || imgAvg == 255) { processing.set(false); return; } //计算平均值 int averageArrayAvg = 0; int averageArrayCnt = 0; for (int i = 0; i < averageArray.length; i++) { if (averageArray[i] > 0) { averageArrayAvg += averageArray[i]; averageArrayCnt++; } }
//计算平均值 int rollingAverage = (averageArrayCnt > 0) ? (averageArrayAvg / averageArrayCnt) : 0;// Log.d("aaaaaaa", "rollingAverage" + rollingAverage); TYPE newType = currentType; if (imgAvg < rollingAverage) { newType = TYPE.RED; if (newType != currentType) { beats++; } } else if (imgAvg > rollingAverage) { newType = TYPE.GREEN; }
if (averageIndex == averageArraySize) { averageIndex = 0; } averageArray[averageIndex] = imgAvg; averageIndex++;
if (newType != currentType) { currentType = newType; }
//获取系统结束时间(ms) long endTime = System.currentTimeMillis(); double totalTimeInSecs = (endTime - startTime) / 1000d; if (totalTimeInSecs >= 2) { double bps = (beats / totalTimeInSecs); int dpm = (int) (bps * 60d); if (dpm < 30 || dpm > 180 || imgAvg < 200) { //获取系统开始时间(ms) startTime = System.currentTimeMillis(); //beats心跳总数 beats = 0; processing.set(false); return; }
if (beatsIndex == beatsArraySize) { beatsIndex = 0; } beatsArray[beatsIndex] = dpm; beatsIndex++;
int beatsArrayAvg = 0; int beatsArrayCnt = 0; for (int i = 0; i < beatsArray.length; i++) { if (beatsArray[i] > 0) { beatsArrayAvg += beatsArray[i]; beatsArrayCnt++; } } int beatsAvg = (beatsArrayAvg / beatsArrayCnt); Random r = new Random();
int i1 = (r.nextInt(30)); // 这里是获取到的心率值 :currentHeartRate Log.d("aaaaa", "心率" + beatsAvg); double v1 = (beatsAvg * 1.37) + i1; String format = df.format(v1); double v2 = (beatsAvg * 0.72) + i1; String format1 = df.format(v2);
double i2 = beatsAvg * 2; double v3 = (beatsAvg * 1.37); double v4 = (beatsAvg * 0.72); double i3 = v3 + v4 + i1; double i4 = i2 / i3; double v = i4 * 100;
String format2 = df.format(v);
// int v = (int) (beatsAvg * 2 / (beatsAvg * 1.37) + (beatsAvg * 0.72)+ i1);
currentHeartRate.setText("当前心率:" + String.valueOf(beatsAvg) );
currentHeartRateXueya.setText("当前血压:"+format + " / " + format1); currentHeartRateXueyang.setText("当前血氧:"+format2+"%");
//正常人的血液黏度男性是3.84-5.30mPa·s,女性是3.39-4.41mPa·s体重指数BMI=体重/身高的平方(国际单位kg/㎡)
double rmd = r.nextInt(530 - 339) + 339; double i = rmd / 100; currentHeartRateXueye.setText("当前血液黏度:"+i);
heartRate = beatsAvg;
//获取系统时间(ms) startTime = System.currentTimeMillis(); beats = 0; } processing.set(false); } };
/** * 预览回调接口 无需理解 固定代码 */ private SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() { //创建时调用 @Override public void surfaceCreated(SurfaceHolder holder) { try { camera.setPreviewDisplay(previewHolder); camera.setPreviewCallback(previewCallback); } catch (Throwable t) {
} }
//当预览改变的时候回调此方法 @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Camera.Parameters parameters = camera.getParameters(); parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); Camera.Size size = getSmallestPreviewSize(width, height, parameters); if (size != null) { parameters.setPreviewSize(size.width, size.height); } camera.setParameters(parameters);// camera.startPreview(); }
//销毁的时候调用 @Override public void surfaceDestroyed(SurfaceHolder holder) {
} };
/** * 获取相机最小的预览尺寸 无需理解, 固定代码 */ private 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; }}