Calculating Breaths Per Minute from Video Data

I have been able to process video data of myself sleeping using a Wyze camera to produce fairly solid data that I would like to use to calculate breaths per minute.The actual calculation is trivial. I calculate the percent change an image is different from a keyframe where 0 is the same image, and 1% is N/(Width * Height * 256). I create a threshold for percent difference and trigger a retaking of the keyframe and a slight pause in the data processing.

Sample Signal:

Using a discrete fourier transform I get a good result.

So I can clearly see the FFT is yielding a string signal, but I am unsure how to convert this properly to a breaths per minute calculation. I capture timestamps of the data as I am processing it as a CSV, but when I take windows of the data the position of the spike in the FFT changes, so I think my understanding of what a discrete fourier transform calculates is naive. I know typical sleeping breaths per minute is around 12 ~20 BPM. But the relationship between the actual frequency and the result of the FFT is abstract. I have wanted to attempt this for awhile, but was inspired to give it a try when I say the most recent julia video for DFT

I think my next steps should be using DSP.jl, but I have no idea how to use it. The documentation describes the functions, but I am unable to find examples. Main point being I want to know what methods or libraries I can use to pull the dominant frequencies from the signal in real time domain. I am an amateur programmer and signal processor, and haven’t had to do differential equations for about 10 years, but I find it all fascinating and stimulating. Would appreciate any help.

1 Like

From your time sample rate you calculate the Nyquist frequency, then the frequency sample interval is that divided by half (because of positive and negative frequencies) the length of the FFT signal.

For the very narrow question of ‘given the position of this peak in the FFT coefficients, what is the corresponding frequency?’, you can use the fftfreq function (available in FFTW).

 julia> Ts = 1. /30  # I am assuming your camera takes pictures at 30 FPS

julia> t = 0:Ts:60

julia> Fbreath = 20. / 60. # 20 breaths in 60 seconds

julia> signal = sin.(2π.*t*Fbreath) .+ 0.1*rand(Float64, size(t)); # some fake data

julia> fft_sig = fft(signal);

julia> argmax(abs.(fft_sig))

julia> fftfreq(length(signal), 1/Ts)[21]  # second argument is the sampling rate
1 Like

For the more complicated question, “How do I find the spectral power of the frequencies in this signal”, you might want to look at a periodogram or a short-time-FFT.

There are also techniques to directly estimate the dominant sinusoidal components of a signal it would be fun to see how that performs on your data!

Ohhhh, so fftfreq uses the inverse of the sample rate. That makes sense. I will try this! greatly appreciate the guidance.

sampling rate is usually measured in Hz or 1/s. Sample period is measured in s.

Works great now. I had gone down the path, but the fftfreq function confused me for some reason and I could not figure out the relationship between the result and my expected ranges. That cleared it up for me and looks like fairly accurate data.

I have to ignore the first element and the module noise appears as an insanely high signal. Thank you so much for clearing that up!

@RodBiren May I Ask what the application is here? Maybe monitoring vulnerable or elderly people at home?
Then again people normally sleep under covers! It would be very interesting to hear what the application is, if you can say of course - no commercial secrets should be given.

DC bias? Subtract the mean of your input signal to avoid that.

No real application for commercial use or anything. Breaths per minute can be used to estimate sleep stage/quality. The Wyze camera is cheap as dirt and supports Infrared, so really it is just a cheap, albeit creepy, method of tracking my sleep.

Using covers does not actually present much of an issue. My background is with OpenCV C++ and you would be surprised how accurate the cameras really are at seeing small changes.

Total signal for a few hours sleep looks like this

Being an amateur signal processor I just cap the changes at 1% so I end up with a bunch of floating Sin waves broken up by periods where I clearly readjusted and moved. Each locations has varied amplitude as a result of my position and how much rise and fall the covers allow to be viewed. But at no point was the signal totally gone, so it made for a reasonably accurate measurement device.