Instead of a red blob detection outline on a black background, I turned the webcam feed to black and white (which an adjustable threshold value) by using a brightness detection example in processing, and then after literally hours of trials, got the blob detection on top...and now I'm not even certain I need the blob detection anymore. The first attempt at a blob detection on the image I used a neon green color JUST so I could clearly see it. This is no way was intended to be shown for a final project. It was just for testing. I also set up the "pulse" variable to a keyPressed function just so I could test it quickly without constantly having my pulse sensor running and having to reset arudino and processing for each test. These were two different thresholds I was playing with too, trying to get the outline and image in sync.
Still using the black and white threshold image, I made the outline very faint and set up a very low opacity for the black and white of the webcam image so that it draws slowly. The outline does NOT constantly loop but instead is only drawn ONCE per heart beat.
Essentially, instead of the outline pulsing now, the image clears, so between heart beats a slow drawing of the image appears, and when the heart beat is detected, the image clears so that the fast a person moves with this project and the faster the person's heart beat, the fainter the image.
Arduino code has remained unchanged. Processing code has changed however:
PROCESSING:
import processing.serial.*; // this is how we talk to arduino
Serial port; // create and name the serial port
int heart = 0; // used to time pulsing of heart graphic with your heart beat
int pulseRate = 0; // used to hold pulse rate value sent from arduino (beats per minute)
int Sensor = 0; // used to hold raw sensor data from arduino
int HRV; // time between this current beat and the last beat in mS (used for Heart Rate Variability)
int Ypos; // used to print the new Pulse Sensor data point
int[] pulseY; // used to hold pulse waveform Y positions
int[] rateY; // used to hold bpm waveform Y positions
// these variables will hold pulse window specs
int PulseWindowMin;
int PulseWindowMax;
int PulseWindowW;
int PulseWindowH;
int PulseWindowY;
int PulseWindowX;
int PulseDisplayBaseline = 712; // these variables are used to adjust the pulse window display
int PulseOffset = 712; // the max and min will auto adjust if the waveform drifts beyond the screen
boolean beat = false; // used to advance heart rate graph
boolean newRate = false; // used to update heart rate display
import processing.video.*;
import blobDetection.*;
Capture cam;
BlobDetection theBlobDetection;
PImage img;
boolean newFrame=false;
int z = 0;
int sc = 255;
////////////////////////////////////////////////////////////////////
color black = color(0,0,0,40);
color white = color(255,255,255,5);
int numPixels;
////////////////////////////////////////////////////////////////////
// ==================================================
// setup()
// ==================================================
void setup() {
// Size of applet
size(screen.width, screen.height);
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// find and establish contact with the serial port
println(Serial.list()); // print a list of available serial ports
port = new Serial(this, Serial.list()[0], 115200); // choose the right one in square brackets
port.bufferUntil('\n'); // arduino will end each ascii string with a '\n' at the end (carriage return)
port.clear(); // flush the serial buffer
cam = new Capture(this, width, height, 10); // Capture
// BlobDetection
// img which will be sent to detection (a smaller copy of the cam frame);
numPixels = cam.width * cam.height;
noCursor();
smooth();
img = new PImage(80,60);
theBlobDetection = new BlobDetection(cam.width, cam.height);
theBlobDetection.setPosDiscrimination(true);
theBlobDetection.setThreshold(0.3f); // will detect bright areas whose luminosity > 0.2f;
}
void draw() {
if (cam.available()) {
cam.read();
cam.loadPixels();
int threshold = 95; // Set the threshold value
float pixelBrightness; // Declare variable to store a pixel's color
// Turn each pixel in the video frame black or white depending on its brightness
loadPixels();
for (int i = 0; i < numPixels; i++) {
pixelBrightness = brightness(cam.pixels[i]);
if (pixelBrightness > threshold) { // If the pixel is brighter than the
pixels[i] = white; // threshold value, make it white
}
else { // Otherwise,
pixels[i] = black; // make it black
}
}
updatePixels();
theBlobDetection.computeBlobs(cam.pixels);
drawBlobsAndEdges(true,true);
clearImage();
}
////////////////////////////////////////////////////////////////////
}
// ==================================================
// drawBlobsAndEdges()
// ==================================================
void drawBlobsAndEdges(boolean drawBlobs, boolean drawEdges)
{
noFill();
Blob b;
EdgeVertex eA,eB;
for (int n=0 ; n<theBlobDetection.getBlobNb() ; n++)
{
b=theBlobDetection.getBlob(n);
if (b!=null)
{
// Edges
if (drawEdges)
{
strokeWeight(0.5);
for (int m=0;m<b.getEdgeNb();m++) {
eA = b.getEdgeVertexA(m);
eB = b.getEdgeVertexB(m);
if (eA !=null && eB !=null)
line(eA.x*width, eA.y*height, eB.x*width, eB.y*height);
}
}
}
}
}
void clearImage (){
if (beat == true){ // only move the heart rate line over once every time the heart beats (beat flag set in serialEvent tab)
fill(255);
stroke(80);
strokeWeight(4);
rect(0,0,width,height);
beat = false; // reset beat flag
}
else {
noStroke();
}
}
serialEvent: (tab in processing)
/*
Serial data is sent from arduino with leading ascii character
ascii character tells processing what to do with the data
*/
void serialEvent(Serial port){
String inData = port.readStringUntil('\n');
inData = trim(inData); // trim the \n off the end
// leading 'Q' means heart rate data
if (inData.charAt(0) == 'Q'){ // following string contains current heart rate
inData = inData.substring(1); // cut off the leading 'Q'
pulseRate = int(inData); // convert ascii string to integer
newRate = true; // flag the new heart rate data so it can be mapped onscreen
return;
}
// leading 'S' means sensor data
if (inData.charAt(0) == 'S'){ // following string contains raw sensor data
inData = inData.substring(1); // cut off the leading 'S'
Sensor = int(inData); // convert ascii string to integer
// Set Ypos variable equal to new Sensor data value and fit it into the Pulse Window
Ypos = (PulseWindowY-PulseWindowH/2)+(PulseOffset - Sensor);
// If the new sensor data drifts outside the pulse window, adjust the pulse widnow offset so we can see the waveform
if (Ypos < PulseWindowMin || Ypos > PulseWindowMax){
int selfCalibration = PulseDisplayBaseline - Sensor;
selfCalibration = constrain(selfCalibration-200,-312,312);
PulseOffset = PulseDisplayBaseline - selfCalibration;
}
return;
}
// leading 'B' means arduino found a heart beat
if (inData.charAt(0) == 'B'){ // following strint is time between beats in miliseconds
inData = inData.substring(1); // cut off the leading 'B'
HRV = int(inData); // convert ascii string to integer
beat = true; // set beat flag to advance heart rate graph
heart = 10; // begin heart graphic 'swell' timer
return;
}
}
No comments:
Post a Comment