def _find_peak(siginfo): """ Obtains an estimation of the peak situation of a QRS complex, from the energy interval that forms the base evidence, a fragment of signal evidence, a reference time point, and the interval of valid points for the peak. """ dist = lambda p : 1.0 + 2.0 * abs(p - C.QRS_BANN_DMAX)/ms2sp(150) dist = np.vectorize(dist) peak = None #For each lead, the peak will be the maximum deviation point wrt the #baseline, and applying the distance function just defined. We give more #importance to the first leads, as they supposedly have more quality. for _, sig, points, baseline, _ in siginfo: if len(points) < 3: continue peaks = points[sig_meas.get_peaks(sig[points])] if len(peaks) == 0: continue peakscore = abs(sig[peaks]-baseline)/dist(peaks) lpeak = peaks[peakscore.argmax()] if peak is None: peak = lpeak elif abs(peak-lpeak) <= C.TMARGIN: peak = lpeak if lpeak < peak else peak return peak
def delineate_qrs(siginfo): """ Performs the multi-lead delineation of a QRS complex enclosed in a specific time interval, returning an instance of the QRS class. Parameters ---------- siginfo: List-like structure containing all the necessary information of the ECG signal in the searching time interval. Each entry in this list is assumed to be a tuple of the **LeadInfo** class, and the list is assumed to be ordered by the quality of the signal in each lead. Returns ------- out: QRS object with all the attributes properly set. If the delineation cannot be performed, an InconsistencyError is raised. """ verify(siginfo) qrs = QRS() #Peak point estimation. peak = _find_peak(siginfo) verify(peak is not None) #QRS start and end estimation #For each lead, we first check if it is a paced beat, whose delineation #process is different. In case of failure, we perform common delineation. limits = OrderedDict() for lead, sig, points, baseline, _ in siginfo: endpoints = _paced_qrs_delineation(sig, points, peak, baseline) if endpoints is None: endpoints = _qrs_delineation(sig, points, peak) if endpoints is None: continue limits[lead] = (False, endpoints) else: limits[lead] = (True, endpoints) #Now we combine the limits in all leads. start, end = _combine_limits(limits, siginfo, peak) verify(start is not None and end > start) #QRS waveform extraction for each lead. for lead, sig, points, baseline, _ in siginfo: #We constrain the area delineated so far. sig = sig[start:end+1] points = points[np.logical_and(points >= start, points <= end)] - start if len(points) == 0: continue if points[0] != 0: points = np.insert(points, 0, 0) if points[-1] != len(sig) - 1: points = np.append(points, len(sig) - 1) if len(points) < 3: continue #We define a distance function to evaluate the peaks dist = (lambda p : 1.0 + 2.0 * abs(start + p - C.QRS_BANN_DMAX) /ms2sp(150)) dist = np.vectorize(dist) #We get the peak for this lead pks = points[sig_meas.get_peaks(sig[points])] if len(pks) == 0: continue peakscore = abs(sig[pks]-baseline)/dist(pks) peak = pks[peakscore.argmax()] #Now we get the shape of the QRS complex in this lead. shape = None #If there is a pace detection in this lead if lead in limits and limits[lead][0]: endpoints = limits[lead][1] shape = _get_paced_qrs_shape(sig, points, endpoints.start - start, min(endpoints.end-start,len(sig))) if shape is None: limits[lead] = (False, endpoints) if shape is None: shape = _get_qrs_shape(sig, points, peak, baseline) if shape is None: continue qrs.shape[lead] = shape #There must be a recognizable QRS waveform in at least one lead. verify(qrs.shape) #The detected shapes may constrain the delineation area. llim = min(qrs.shape[lead].waves[0].l for lead in qrs.shape) if llim > 0: start = start + llim for lead in qrs.shape: qrs.shape[lead].move(-llim) ulim = max(qrs.shape[lead].waves[-1].r for lead in qrs.shape) if ulim < end-start: end = start + ulim #The definitive peak is assigned to the first relevant wave #(each QRS shapeform has a specific peak point.) peak = start + min(s.waves[_reference_wave(s)].m for s in qrs.shape.itervalues()) #Segmentation points set qrs.paced = any(v[0] for v in limits.itervalues()) qrs.start, qrs.peak, qrs.end = start, peak, end ################################################################### #Amplitude conditions (between 0.5mV and 6.5 mV in at least one #lead or an identified pattern in most leads). ################################################################### verify(len(qrs.shape) > len(siginfo)/2.0 or C.QRS_MIN_AMP <= max(s.amplitude for s in qrs.shape.itervalues()) <= C.QRS_MAX_AMP) return qrs