Beispiel #1
0
    def chirp_times(self):
        track, chirp = self.track, self.chirp

        SMOOTHED_CORR_THRESHOLD = 0.08
        CORR_THRESHOLD = 0.35

        corr = cross_corr(chirp, track.x)

        smoothed_corr = lowpass_fft(np.abs(corr), track.fps, cf=100.0,
                                    tw=20.0) / np.max(np.abs(corr))

        if self.debug:
            plt.plot(track.t, corr / np.max(np.abs(corr)), label='corr')
            plt.plot(track.t, smoothed_corr, label='smoothed_corr')
            plt.legend()
            plt.show()

        peak_locs = np.where(
            localmax(corr) & (smoothed_corr > SMOOTHED_CORR_THRESHOLD)
            & (corr / np.max(np.abs(corr)) > CORR_THRESHOLD))[0]

        # look in +/- 5 ms of the smoothed_corr peak for real corr peak
        corr_peak_locs = np.unique(
            localmax_climb(corr, peak_locs, hwin=int(track.fps * 0.005)))

        times = track.t[corr_peak_locs]

        self.times, self.idxs = times, corr_peak_locs

        return self.times, self.idxs
Beispiel #2
0
 def upsample(self, ratio=10):
     ratio, fps = int(ratio), self.fps
     xu, iu = np.zeros(len(self.x) * ratio), np.arange(0, len(self.x) * ratio, ratio)
     #xu[iu] = np.array(self.x)
     for i in range(ratio):
         xu[iu+i] = np.array(self.x)
     from hsh_signal.signal import lowpass_fft
     #xu = lowpass_fft(xu, fps * ratio, cf=(fps * ratio / 2.0), tw=(fps * ratio / 16.0)) * ratio
     xu = lowpass_fft(xu, fps * ratio, cf=(fps / 2.0), tw=(fps / 8.0))  # * ratio
     return HeartSeries(xu, self.ibeats * ratio, fps * ratio, self.lpad * ratio)
Beispiel #3
0
 def upsample(self, ratio=10):
     ratio, fps = int(ratio), self.fps
     xu, iu = np.zeros(len(self.x)*ratio), np.arange(0, len(self.x)*ratio, ratio)
     for i in range(ratio):
         xu[iu+i] = np.array(self.x)
     import matplotlib.pyplot as plt
     #plt.plot(xu)
     from hsh_signal.signal import lowpass_fft
     #print fps*ratio
     xu = lowpass_fft(xu, fps*ratio, cf=(fps/2.0), tw=(fps/8.0)) # * ratio
     #plt.plot(xu)
     #plt.show()
     return Series(xu, fps*ratio, self.lpad*ratio)
Beispiel #4
0
def beat_baseline(ppg_feet, ppg_beats):
    """
    :param ppg_feet: raw ppg with beats located at the wave foot.
    :param ppg_beats: ppg with beats located on the edge as usual.
    """
    ppg = ppg_feet
    xu = np.zeros(len(ppg.x))
    fps = ppg.fps
    ibeats = ppg.ibeats.astype(int)

    # where long periods without beats, we need support from raw average (~baseline)
    ibis = np.diff(ppg.tbeats)
    median_ibi = np.median(ibis)
    long_ibis = np.where(ibis > 1.5 * median_ibi)[0]
    #print 'long_ibis', long_ibis
    ib_start = ppg.ibeats[long_ibis]
    ib_end = ppg.ibeats[np.clip(long_ibis + 1, 0, len(ppg.ibeats))]
    if len(ib_end) < len(ib_start): ib_end = np.insert(ib_end, len(ib_end), len(ppg.x) - 1)
    # iterate over long holes and fill them with fake ibis
    # to do: actually use raw average baseline, not some random value at a picked index
    extra_ibeats = []
    for s, e in zip(ib_start, ib_end):
        #print 's,e', s, e, ppg.t[s], ppg.t[e]
        extra_ibeats += list(np.arange(s + fps * median_ibi, e - 0.5 * median_ibi * fps, fps * median_ibi).astype(int))

    #print 'extra_ibeats', extra_ibeats, 'extra_tbeats', ppg.t[extra_ibeats]

    ibeats = np.array(sorted(list(ibeats) + list(extra_ibeats)))
    xu[ibeats] = ppg.x[ibeats]

    lf = interp1d(ibeats, xu[ibeats])
    xul = lf(np.arange(ibeats[0], ibeats[-1]))
    xul = np.pad(xul, (ibeats[0], len(xu) - ibeats[-1]), mode='constant')
    # to do: should we lowpass filter here, to get rid of all higher freq components?

    f_cf = 1.8
    f_tw = f_cf / 2.0
    xu_filtered = lowpass_fft(xul, ppg.fps, cf=f_cf, tw=f_tw)  # * scaling  # * ratio

    if False:
        plt.plot(ppg.t, np.clip(xu_filtered, a_min=220, a_max=270), c='b')
        plt.plot(ppg.t, np.clip(ppg.x, a_min=200, a_max=270), c='r')
        plt.show()

    ppg_detrended = ppg.copy()
    ppg_detrended.x = ppg.x - xu_filtered
    ppg_detrended.tbeats, ppg_detrended.ibeats = ppg_beats.tbeats, ppg_beats.ibeats

    return ppg_detrended
Beispiel #5
0
def ppg_wave_foot(ppg_raw_l, ppg_l):
    """
    :param ppg_raw_l: PPG signal, evenly spaced
    :param ppg_l: highpass filtered, beat detected PPG signal
    """
    #
    # move beats to the wave foot
    #

    # highpass (`ppg()`) -> lowpass -> localmax
    #
    # to do: this is not methodologically thought through.
    # to do: testing (does it work cleanly under noisy conditions?)
    #
    ratio=10  # use same ratio for all!
    ppg_smoothed = ppg_l.upsample(ratio)
    ppg_smoothed.x = lowpass_fft(ppg_smoothed.x, ppg_smoothed.fps, cf=6.0, tw=0.5)

    # fill `ileft_min` with index of next local minimum to the left
    # for noise robustness, use some smoothing before
    #local_min = localmax(ppg.x)
    local_min = localmax(ppg_smoothed.x)
    ileft_min = np.arange(len(local_min)) * local_min
    for i in range(1, len(ileft_min)):
        if ileft_min[i] == 0:
            ileft_min[i] = ileft_min[i - 1]

    ppg_smoothed.ibeats = ileft_min[ppg_smoothed.ibeats.astype(int)]
    ppg_smoothed.tbeats = (ppg_smoothed.ibeats - ppg_smoothed.lpad) / float(ppg_smoothed.fps)

    #
    # hack to get beats onto ppg_raw
    #
    ppg_u = ppg_l.upsample(ratio)
    ppg_raw_tmp = ppg_raw_l.upsample(ratio)
    ppg_raw_u = ppg_l.upsample(ratio)
    ppg_raw_u.x = ppg_raw_tmp.x
    ppg_raw_u.ibeats = ppg_smoothed.ibeats
    ppg_raw_u.tbeats = ppg_smoothed.tbeats

    ppg_baselined = beat_baseline(ppg_raw_u, ppg_u)
    return ppg_baselined
Beispiel #6
0
    def beat_baseline(self):
        xu = np.zeros(len(self.x))
        fps = self.fps
        ibeats = self.ibeats.astype(int)


        # where long periods without beats, we need support from raw average (~baseline)
        ibis = np.diff(self.tbeats)
        median_ibi = np.median(ibis)
        long_ibis = np.where(ibis > 1.5 * median_ibi)[0]
        print 'long_ibis', long_ibis
        ib_start = self.ibeats[long_ibis]
        ib_end = self.ibeats[np.clip(long_ibis+1, 0, len(self.ibeats))]
        if len(ib_end) < len(ib_start): ib_end = np.insert(ib_end, len(ib_end), len(self.x)-1)
        # iterate over long holes and fill them with fake ibis
        # to do: actually use raw average baseline, not some random value at a picked index
        extra_ibeats = []
        for s,e in zip(ib_start, ib_end):
            print 's,e',s,e,self.t[s],self.t[e]
            extra_ibeats += list(np.arange(s + fps * median_ibi, e - 0.5 * median_ibi * fps, fps * median_ibi).astype(int))

        print 'extra_ibeats',extra_ibeats, 'extra_tbeats',self.t[extra_ibeats]

        ibeats = np.array(sorted(list(ibeats) + list(extra_ibeats)))
        xu[ibeats] = self.x[ibeats]

        from scipy.interpolate import interp1d
        #xu = interp1d(ibeats, self.x[ibeats], kind='linear', bounds_error=False, fill_value='extrapolate')(np.arange(len(xu)))
        #for i, j in pairwise(ibeats):
            #xu[i:j] = self.x[i]
        # boundary constants
        xu[0:ibeats[0]] = xu[ibeats[0]]
        xu[ibeats[-1]:] = xu[ibeats[-1]]
        from hsh_signal.signal import lowpass_fft
        scaling = float(len(xu))/len(ibeats)
        xu = lowpass_fft(xu, fps, cf=0.1, tw=0.05) * scaling  # * ratio
        # plt.plot(xu)
        # plt.show()
        return HeartSeries(xu, self.ibeats, self.fps, self.lpad)
Beispiel #7
0
def getrr_v1(data,
             fps=125.0,
             min_rr=300,
             peakfindrange=200,
             slopewidthrange=100,
             smooth_slope_ycenters=0.5,
             discard_short_peaks=False,
             interpolate=True,
             interp_n=50,
             convert_to_ms=False,
             plt=None,
             getslopes=False):
    if data.shape[0] < fps:
        return [], [], []

    data = np.copy(data)
    if convert_to_ms:
        data[:, 0] *= 1000.0

    if fps >= 250:
        factor = int(fps / 250)
        data = data[np.arange(0, len(data), factor), :]

    firstsecond = (data[int(fps), 0] - data[0, 0])
    if firstsecond < 900:
        raise Warning(
            "Warning: FPS set to " + str(fps) +
            ", but timeframe of first FPS data points is " + str(firstsecond) +
            "ms (set convert_to_ms flag to convert between seconds and ms)")

    data[:, 1] /= np.std(data[:, 1])
    data[:, 1] = detrend(data[:, 1])

    # better detrending
    #data[:,1] -= strongsmoothing(data[:,1], 4)
    data[:, 1] = highpass(highpass(data[:, 1], fps), fps)  # highpass detrend
    data[:, 1] = lowpass_fft(data[:, 1], fps, cf=3,
                             tw=0.2)  #slight noise filtering

    # outlier removal
    mn, mx = np.min(data[:, 1]), np.max(data[:, 1])
    m = min(abs(mn), abs(mx))
    containthreshold = 0.001
    N = 100
    step = m / float(N)
    for i in range(N):
        n = len(np.where(data[:, 1] < -m)[0]) + len(
            np.where(data[:, 1] > m)[0])
        if n > containthreshold * len(data[:, 1]):
            break
        m -= step
    mn, mx = -m, m
    data[data[:, 1] < mn, 1] = mn
    data[data[:, 1] > mx, 1] = mx

    data[:, 1] /= np.std(data[:, 1])
    data[:, 1] = detrend(data[:, 1])

    # savgol peaks may be off (lower than the actual peak) - do a local max to deal with that
    maxdata = np.zeros((len(data), ))
    for i in range(len(maxdata)):
        mini = max(0, i - 1)
        maxi = min(len(maxdata), i + 2)
        maxdata[i] = np.max(data[mini:maxi, 1])

    # get initial maxima and minima
    filtered = savgol_filter(data[:, 1], SAVGOL_WINDOWSIZE, SAVGOL_DEGREE)
    mintf = np.array(heartbeat_localmax(-1 * filtered))
    maxtf = np.array(heartbeat_localmax(filtered))

    # get trend of peaks
    hyp_peakidx = np.where(maxtf)[0]
    peakidx = []
    max_window = min_rr
    for p in hyp_peakidx:
        mini = np.max((0, (p - int(fps * max_window / 1e3))))
        maxi = np.min((len(maxdata), (p + int(fps * max_window / 1e3))))
        m = mini + np.argmax(maxdata[mini:maxi])
        while m < len(maxdata) - 1 and maxdata[m + 1] > maxdata[m]:
            m += 1
        while m > 0 and maxdata[m - 1] > maxdata[m]:
            m -= 1
        if m < len(data):
            peakidx.append(m)
    peakidx = np.unique(peakidx)
    f = interp1d(data[peakidx, 0].flatten(),
                 filtered[peakidx].flatten(),
                 bounds_error=False,
                 kind='linear')  # quadratic
    macrotrend = f(data[:, 0].flatten())
    macrotrend[np.where(np.isnan(macrotrend))] = np.mean(
        macrotrend[np.where(~np.isnan(macrotrend))])
    macrotrend = savgol_filter(macrotrend, TREND_SAVGOL_WINDOWSIZE,
                               SAVGOL_DEGREE)

    # delete if not high or low enough
    """"T = macrotrend+MIN_PEAK_DIST_IN_STDEVS*np.std(filtered) # can leave minima - won't matter
    mintf[np.where(maxdata>T)[0]] = False
    T = macrotrend-MIN_PEAK_DIST_IN_STDEVS*np.std(filtered)
    maxtf[np.where(maxdata<T)[0]] = False"""

    minindices = np.where(mintf)[0]
    maxindices = np.where(maxtf)[0]

    if plt:
        plt.plot(data[:, 0],
                 macrotrend - MIN_PEAK_DIST_IN_STDEVS * np.std(filtered))
        #plt.plot(data[:,0], macrotrend+MIN_PEAK_DIST_IN_STDEVS*np.std(filtered), 'k')
        plt.scatter(data[minindices, 0], filtered[minindices], c='g', s=10)
        plt.scatter(data[maxindices, 0], filtered[maxindices], c='b', s=8)

    # ensure we start with min
    i = 0
    while i < len(maxindices) and maxindices[i] < minindices[0]:
        i += 1
    maxindices = maxindices[i:]
    i = len(minindices) - 1
    while i > 1 and minindices[i] > maxindices[-1]:
        i -= 1
    minindices = minindices[:(i + 1)]

    # min,min or max,max
    n = np.min((len(maxindices), len(minindices)))
    for i in range(n):
        needchange = True
        while needchange:
            needchange = False
            if i < len(minindices) - 1 and len(
                    np.where((maxindices > minindices[i]) & (
                        maxindices < minindices[i + 1]))[0]) == 0:  # min,min
                minindices = np.delete(minindices, i)
                needchange = True
            if i < len(maxindices) - 1 and len(
                    np.where((minindices > maxindices[i]) & (
                        minindices < maxindices[i + 1]))[0]) == 0:  # min,min
                maxindices = np.delete(maxindices, i + 1)
                needchange = True
    maxindices = maxindices[:len(minindices)]

    # get rid of too small inter-beat intervals
    maxindices, minindices = remove_double_beats(data,
                                                 filtered,
                                                 maxindices,
                                                 minindices,
                                                 min_rr=min_rr)
    maxindices, minindices = remove_double_beats(data,
                                                 filtered,
                                                 maxindices,
                                                 minindices,
                                                 min_rr=min_rr)

    if plt:
        #plt.plot(data[:,0], data[:,1], '--k', linewidth=3)
        plt.plot(data[:, 0], maxdata)
        plt.plot(data[:, 0], macrotrend, 'r', linewidth=0.2)
        plt.plot(data[:, 0], filtered, 'k', linewidth=0.5)
        #plt.scatter(data[:,0], data[:,1], s=20, c='b')

    mean_slopemid = (np.mean(maxdata[maxindices]) +
                     np.mean(data[minindices, 1])) / 2.0
    max_slopeheight = np.mean(maxdata[maxindices]) - np.mean(data[minindices,
                                                                  1])
    std_slopeheight = np.std(maxdata[maxindices]) + np.std(data[minindices, 1])

    slopebeats = []
    slopes = []
    n = np.min((len(maxindices), len(minindices)))
    # loop through each upslope (min->max) and find an exact beat location in its middle
    for i in range(n - 1):

        if discard_short_peaks:
            slopeheight = maxdata[maxindices[i]] - data[minindices[i], 1]
            if slopeheight < max_slopeheight - 2 * std_slopeheight - MIN_PEAK_DIST_IN_STDEVS * np.std(
                    filtered):
                if plt:
                    print slopeheight, "<", max_slopeheight - 2 * std_slopeheight - MIN_PEAK_DIST_IN_STDEVS * np.std(
                        filtered)
                    plt.scatter(data[maxindices[i], 0],
                                maxdata[maxindices[i]],
                                180,
                                c='k')
                continue

        if interpolate:
            # interpolated
            #ymid = (1-smooth_slope_ycenters)*np.mean([filtered[minindices[i]], filtered[maxindices[i]]])
            ymid = smooth_slope_ycenters * mean_slopemid + (
                1 - smooth_slope_ycenters) * np.mean(
                    [maxdata[minindices[i]], 0.5 * maxdata[maxindices[i]]])
            idxrange = crossing_bracket_indices(data[:, 1],
                                                minindices[i],
                                                maxindices[i],
                                                crossing=ymid)

            mni = max(0, idxrange[0] - 2)
            mxi = idxrange[1] + 2
            if mxi > len(data) - 1:
                mxi = len(data) - 1
            ii = np.linspace(data[mni, 0], data[mxi - 1, 0], interp_n)
            f = interp1d(data[mni:mxi, 0], data[mni:mxi, 1],
                         kind='linear')  # quadratic
            idata = f(ii)
            iidxrange = crossing_bracket_indices(idata, crossing=ymid)
            try:
                k = (idata[iidxrange][-1] - idata[iidxrange][0]) / (
                    ii[iidxrange][-1] - ii[iidxrange][0])
            except:
                k = np.float32.max()
            if plt:
                plt.plot(ii, idata, c='m', linewidth=2)
                plt.scatter(ii[iidxrange], idata[iidxrange], s=40, c='c')
                #k = (idata[iidxrange][-1]-idata[iidxrange][0])/(ii[iidxrange][-1]-ii[iidxrange][0])
                #plt.plot([ii[0]-100, ii[0]+200], [idata[0]-100*k, idata[0]+200*k], '--k', linewidth=2)

            #ymid = (1-smooth_slope_ycenters)*np.mean([filtered[minindices[i]], filtered[maxindices[i]]])
            ymid = smooth_slope_ycenters * mean_slopemid + (
                1 - smooth_slope_ycenters) * np.mean(
                    [maxdata[minindices[i]], 0.5 * maxdata[maxindices[i]]])
            x, y = ii[iidxrange], idata[iidxrange]
            #x, y = ii[iidxrange].reshape(-1,1), idata[iidxrange].reshape(-1,1)
            #model = LinearRegression()
            #model.fit(y.reshape(-1,1), x.reshape(-1,1))
            #xmid = model.predict([[ymid]]) # #xmid2 = [x[0]+y[0]*(x[1]-x[0])/(y[1]-y[0])]
            f = interp1d(y.flatten(),
                         x.flatten(),
                         bounds_error=False,
                         kind='linear')  # quadratic
            xmid = f([ymid])
            if np.isnan(xmid[0]):
                xmid = [x[0] + y[0] * (x[1] - x[0]) / (y[1] - y[0])]
            if xmid < x[0] or xmid >= x[1]:
                xmid = [np.mean([x[0], x[1]])]
            slopebeats.append(xmid[0])
            slopes.append(k)
        else:
            mididx = minindices[i] + (maxindices[i] - minindices[i]) / 2
            if mididx - int(mididx) > 0.5 or int(mididx) >= len(data) - 1:
                idxrange = [int(mididx) - 2, int(mididx)]
            else:
                idxrange = [int(mididx) - 1, int(mididx)]
            x, y = data[idxrange, 0], data[idxrange, 1]
            ymid = (1 - smooth_slope_ycenters) * np.mean(
                [filtered[minindices[i]], filtered[maxindices[i]]])
            f = interp1d(y.flatten(),
                         x.flatten(),
                         bounds_error=False,
                         kind='linear')  # quadratic
            xmid = f([ymid])
            slopebeats.append(xmid[0])

        if plt:
            try:
                plt.scatter(data[idxrange, 0], data[idxrange, 1], s=40, c='y')
                plt.scatter(data[minindices[i], 0],
                            maxdata[minindices[i]],
                            c='g',
                            s=100)
                plt.scatter(data[maxindices[i], 0],
                            maxdata[maxindices[i]],
                            c='b',
                            s=80)
                plt.plot(x, y)
                #yp = data[minindices[i]:maxindices[i], 1]
                #xp = model.predict(yp.reshape(-1,1))
                #plt.plot(xp, yp, '--k', linewidth=2)
                plt.scatter(xmid, ymid, s=80, c='r')
            except Exception, e:
                pass