I'm dealing with a light sensor tracking a bursting LED. Unfortunately signal is weak and there are other light sources in environment. I've used optical filters on sensor but there is still some noise.

The LED burst with a fixed duration but the distance and time vary. So I'm interested in finding those 5 pixels wide pulses (the height of pulse may be different). The noise is often a square wave with a much lower frequency.

What algorithm can be used to detect those pulse?


sensor signal


3 Answers 3


For peak detection a nice method is the following: apply a maximal filter to the data and find the places where the filtered data equals to the original one.

A maximal filter is simply sliding through the data and selecting the maximal element from the sliding window. Formally:

$$g_w[x] = \max\left(f[x-w], f[x-w+1], \dots , f[x+w-1], f[x+w]\right)$$

where $f[x]$ is the original data, $w$ is the half of the window size and $g_w[x]$ is the filtered data. Selecting places where $g_w[x] = f[x]$ are either the local maxima of $f$ or plateaus of local maxima. Obviously we don't want to have plateaus, a single point per peak would be desirable.

The first trick we can do is to apply a minimal filter to the data, select the local minima, then choose places where we detected local maximum (based on the maximal filter) but not a local minimum. This will eliminate long plateaus.

The other thing we can do is to select 'up edges' so places where a non local maximum is followed by a maximum.

To make it clear I generated numeric data from the image you presented and wrote a short code. It uses two constants, one is the window size, the other is the minimum jump we require from a peak.

the result

import numpy as np
from matplotlib import pyplot as plt
from skimage import feature
from scipy.ndimage.filters import maximum_filter

# put your data here
#data = np.array([0, 79, 75, 69, 69, 7, ...])

filter_win_size = 12
peak_intensity_threshold = 10

max_data = maximum_filter(data, filter_win_size)
min_data = -maximum_filter(-data, filter_win_size)

# select places where we detect maximum but not minimum -> we dont want long plateaus
peak_mask = np.logical_and(max_data == data, min_data != data)
# select peaks where we have enough elevation
peak_mask = np.logical_and(peak_mask, max_data - min_data > peak_intensity_threshold)
# a trick to convert True to 1, False to -1
peak_mask = peak_mask * 2 - 1
# select only the up edges to eliminate multiple maximas in a single peak
peak_mask = np.correlate(peak_mask, [-1, 1], mode='same') == 2

max_places = np.where(peak_mask)[0]

fig, ax = plt.subplots()
r = range(data.shape[0])
ax.plot(r, data, 'k')
ax.plot(max_places, data[max_places], 'xr')
ax.axis((0, 409, 0, 130))

a very simple, and therefore dangerous, algorithm to apply, based on your example image, is the following: (it's from the family of Change Detectors)

on the time axis, keep track of the current values for a number of samples, and look for their behaviour. If their change is below a certain small threshold, then deduce that that level corresponds to square-noise wave pulse. Then subtract that value from the overall signal. What will remain is your simple pulses on the local time domain. Do this with a sliding window on the data. You will need some memory.

This is a highly nonlinear and simple algorithm, you must select thresholds and local widths very carefully to fit into your prior noise charactherisics otherwise you will distort your own signal.

There may be more sophisticated detectors. But what is your processing environment ?

  • $\begingroup$ Thanks, I'm using a computer so memory and CPU time is not a constrain. $\endgroup$
    – Deqing
    Commented Jan 27, 2015 at 18:22

One of standard way is using "matching filter" - http://en.wikipedia.org/wiki/Matched_filter


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.