Webcam pulsometer (032; 11.10.2012; image processing)
This article show concept of heart rate measurement using webcam. Let's start with video presentation:
Webcam pulsometer from MyInventions on Vimeo.
How it works?
Measurement concept was based on pulse oximetry method. With simple program written in Processing (processing.org) we analyze amount of light passing through finger situated on camera lens. Another inspiration is Eulerian Video Magnification.
First step
Let's start with simple processing code that captures webcam video. Here we limit program to view only red channel (in tests I found it is better than red). Very important is there camera configuration - constant exposure and constant white balance (not auto). For webcam capturing I'm using GSVideo library instead of standard Video library (GSVideo not require QuickTime or WinVDIG installation).
import codeanticode.gsvideo.*; GSCapture webcam; int pixNumber; color Kolor; int A, R, G, B; void setup() { size(640, 480); webcam = new GSCapture(this, 640, 480); webcam.start(); pixNumber = webcam.width*webcam.height; stroke(255, 0, 0); A = 255 << 24; } void draw() { if (webcam.available() == true) { webcam.read(); for (int i=0; i < pixNumber; i++ ) { Kolor = webcam.pixels[i]; B = Kolor & 0xFF; R = 0; G = 0; webcam.pixels[i]=A|R|G|B; } set(0, 0, webcam); } }
Let's start this code, put finger on camera lens (do it gently and not move) and set the light. You should see pulsing color at video.
Second step
Now we will program simple image analysis to automatically extract pulse information from video. My proposition is to sum colors of uniform distributed pixels. To find heart beat on this signal we can differentiate it - that shows us when color has fast changes. That is example program:
import codeanticode.gsvideo.*; GSCapture webcam; int pixNumber; color Kolor; int A, R, G, B, S, pS, D, block=0; float interrupt = 50.0; // amount of color sum that we treat as pulse float scal = 25.0; // scale for sum indicator (red bar) float ms, pms; int ticks = 0; int dT = 10; int dTick = 15; // number of heart beat after which we calc heart rate void setup() { size(160, 350); webcam = new GSCapture(this, 160, 120); // small image is enought webcam.start(); pixNumber = webcam.width*webcam.height; } void draw() { if (webcam.available() == true) { webcam.read(); for (int i = 0; i < pixNumber; i++) { Kolor = webcam.pixels[i]; A = 255 << 24; R = 0; G = 0; B = Kolor & 0xFF; webcam.pixels[i]=A|R|G|B; } background(100); set(0, 0, webcam); // draw camera video // sum of 100 uniform pixels S = 0; for (int i=0; i < pixNumber; i+=pixNumber/100) { S += webcam.pixels[i] & 0xFF; } D = S-pS; // differentiate pS = S; fill(0,0); stroke(0); rect(40, 330, 80, -200); fill(255,0,0); stroke(0,200,0); line(40,230-interrupt,120,230-interrupt); stroke(0); rect(40, 230, 80, (int)((float)D/scal)); // detect pulses if ((-(float)D/scal) > interrupt && (block == 0)) { print("*"); ticks++; block = 3; // help not to detect one pulse as double } if (block > 0) { block--; } // heart rate calculation and draw to console if (ticks == dTick) { ms = millis(); print(60.0*dTick*1000.0/(ms - pms)); pms = ms; ticks = 0; } } }
What next?
At nex step you can create better gui like I did it in video. There is a challenge to make program independent from light intensity changes and try to extract pulse information from face video.