14
$\begingroup$

Hope this isn't too much of a basic question: I'm looking for some Mathematica functions, but I don't know which ones.

Background

I recently read about a simple technique for reading your body's pulse rate using the camera built into the computer (eg an iSight camera at the top of an iMac). The idea is that you put your finger over the camera in a reasonably well-lit room, record a movie (about 15 - 20 seconds), then analyze the frames for varying light intensities, which somehow indicate your pulse. So far the technique seems to be promising. The code is:

getDataFromList[frames_] := 
  Module[{},
   Mean[Flatten[ImageData[#]]] & /@ 
        (ColorSeparate[#][[1]]  (* red channel is best? *)
    & /@ frames)];

AbsoluteTiming[
      testFrames = CurrentImage[200];
      testData = getDataFromList[testFrames]]

ListLinePlot[testData]

The testData starts off like this:

{0.193808, 0.196383, 0.197617, 0.198657, 0.200555, 0.201459,  
 0.201391, 0.1975, 0.197983, 0.198238, 0.200564, 0.201397, 0.202331,  
 0.20271, 0.199668, 0.196999, 0.197857, 0.19826, 0.199329, 0.200624,  
 0.201472, 0.202583, 0.199393, 0.198472, 0.199569, 0.200466, 0.201441,  
 0.203286, etc. 

When plotted, the data for me and my left forefinger looks like it might be working (or could at least be a vaguely human pulse):

my pulse

The data is probably too large to include here - but some of you might have cameras and fingers too, and could generate something similar? In this plot there looks to be about 24 pulses, and the time from AbsoluteTiming was 16.965258, so my pulse rate was about 84. (So holding my finger up to the screen was hard work, obviously...)

Question

Which Mathematica functions can I use to extract the actual pulse rate from this and similar sets of data?

$\endgroup$
9
  • $\begingroup$ Surprisingly, it didn't add a comment when I voted as a duplicate... This is the question, for others: Can one find the beat of a tune with Fourier analysis? $\endgroup$
    – rm -rf
    Aug 2, 2012 at 23:11
  • $\begingroup$ @R.M I don't know, a question on beat detection of a tune seems like a big overkill for this $\endgroup$
    – Rojo
    Aug 2, 2012 at 23:29
  • 2
    $\begingroup$ I suggest to reopen. The other question asked for Fourier, and there are a lot of other possible techniques for doing this (autocorrelation, vanishing derivative, second derivative sign alternating, ...) $\endgroup$ Aug 3, 2012 at 5:23
  • $\begingroup$ I hadn't thought to look for sound-related questions but you're all right, it's only data after all... $\endgroup$
    – cormullion
    Aug 3, 2012 at 6:58
  • 1
    $\begingroup$ @belisarius please show us one now. :-) $\endgroup$
    – Mr.Wizard
    Aug 5, 2012 at 0:46

1 Answer 1

19
$\begingroup$

Let's invent some data. The pulse rate will be 50

pulseRate = 50;
sampleRate = 1000;
data = Range[0, 1, 1/sampleRate] /. 
 t_ :> Sin[2. Pi pulseRate t] + 8 Sinc[Pi t 2.]^2 // # + 
 RandomReal[0.3 {-1, 1}, Length@#] &;
ListLinePlot[data, DataRange -> {0, 200}, PlotRange -> Full]

Mathematica graphics

Plotting Fourier already shows it will be easy to extract the value

ListLinePlot[Abs@Fourier[data], PlotRange -> {{0, 200}, Full}, 
 DataRange -> {0, sampleRate}]

Mathematica graphics

For whatever reason, let's filter it a little bit first

filteredData = data~MovingAverage~10~DerivativeFilter~{1};
ListLinePlot[filteredData]

Mathematica graphics

Now we see more cleary that the peaks are in the pulse rate

ListLinePlot[Abs@Fourier[filteredData], 
 PlotRange -> {{0, 200}, Full}, DataRange -> {0, sampleRate}]

Mathematica graphics

In a general case we could be more careful finding the peaks but, let's just find the 2 maximums and their corresponding frequency

(Ordering[Abs@Fourier[filteredData], -2] - 1) Length@data/sampleRate // N

{50.05, 942.942}

Abs@Mod[%, 1000, -500]

{50.05, 57.058}

It's a start

Another quick approach

This would give an indicator vector with ones where there's a peak (local maximum or minimum)

peaks = Unitize@Differences@Sign@Differences[data~MovingAverage~10];

This would give the number of samples between peaks

peakIntersamples = Differences@Flatten@Position[peaks, 1];

From that we can estimate the pulse rate

sampleRate/Median@peakIntersamples/2

50

$\endgroup$
4
  • $\begingroup$ @cormullion it has now been unclosed :). But even if you feel like accepting, have an open mind about changing the accept in the future: I have the feeling a couple of good answers may be coming now it has been reopen ;D $\endgroup$
    – Rojo
    Aug 3, 2012 at 11:06
  • 1
    $\begingroup$ data~MovingAverage~10~DerivativeFilter~{1} ... damn $\endgroup$ Aug 4, 2012 at 20:39
  • $\begingroup$ @belisarius infix fits those kind of functions perfectly, right? :) $\endgroup$
    – Rojo
    Aug 4, 2012 at 23:52
  • $\begingroup$ @rojo Your answer was obviously the last word in the subject! Thanks - it was very helpful. $\endgroup$
    – cormullion
    Aug 11, 2012 at 7:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.