/// <summary> /// searching peaks using frames of 'framesize' width /// </summary> /// <param name="array"></param> /// <param name="framesize"></param> /// <param name="set_status"></param> /// <returns></returns> peak[] extract_peaks_using_frame(double[] array, int framesize, peak_status set_status) { peak[] peaks = null; int i, j, offset, max_i, min_i, min_i2; int n = array.Length; int peaks_n = (n + framesize - 1) / framesize; peaks = new peak[peaks_n]; for (i = 0; i < peaks_n; ++i) { offset = i * framesize; max_i = offset; for (j = 0; (j < framesize) && (j + offset < n); j++) { if (array[max_i] < array[offset + j]) { max_i = offset + j; } } // calculate front's length and amplitude // find previous local minimum for (min_i = max_i - 1; (min_i >= 0) && (array[min_i] <= array[min_i + 1]); min_i--) { } ; if (min_i < 0) { min_i = max_i; } // find next local minimum (for measuring after_fall_magnitude) for (min_i2 = max_i + 1; (min_i2 < array.Length - 1) && (array[min_i2] <= array[min_i2 - 1]); min_i2++) { } ; // we must pass the real minimum value to leave the cycle, so this little fixup is required --min_i2; peaks[i].front_amplitude = array[max_i] - array[min_i]; peaks[i].after_fall_magnitude = array[min_i2] - array[max_i]; peaks[i].front_length = max_i - min_i; peaks[i].peak_length = min_i2 - min_i; peaks[i].value = array[max_i]; peaks[i].position = max_i; peaks[i].status = set_status; } #if DEBUG // write peaks to file for analysis //System.Diagnostics.Debug.Fail("Write peaks to file for further analysis -- NOT IMPLEMENTED!"); #endif return(peaks); }
void peaks_change_status(peak[] peaks, peak_status status_in, peak_status status_out) { for (int i = 0; i < peaks.Length; ++i) { if (peaks[i].status == status_in) { peaks[i].status = status_out; } } }
/// <summary> /// Функция выясняет типичную ширину пика. downsample указывает погрешность ее определения, /// max_width - верхнюю границу поиска. downsample не должно быть меньше среднего расстояния /// между соседними точками, соответствующими одному пику. /// Возвращает ширину, или -1 в случае неудачи. /// </summary> /// <param name="peaks"></param> /// <param name="downsample">tolerance for peak width estimation</param> /// <param name="max_width">search upper boundary</param> /// <param name="status">peak status to be set</param> /// <returns></returns> int peaks_find_peak_width(peak[] peaks, int downsample, int max_width, peak_status status) { int i, j; double w; int n = peaks.Length; int bins_n = max_width / downsample + 1; if (bins_n <= 1) { // silly answer, but at least as good as the question was return(downsample * bins_n); } double[] bins = new double[bins_n]; for (i = bins_n - 1; i >= 0; i--) { bins[i] = 0; } for (i = 0; i < n; i++) { if (peaks[i].status != status) { continue; } for (j = i; (j < n) && (peaks[j].position - peaks[i].position <= max_width); j++) { if (peaks[j].status == status) { bins[(peaks[j].position - peaks[i].position) / downsample] += 1; //peaks[j].value*peaks[i].value; } } } #if DEBUG array_print_indexed_to_file(bins, bins_n, 0, "width_stat.dat"); #endif for (i = 0; i < bins_n - 1; i++) { if (bins[i] < bins[0] * 0.2) { break; } } w = i * 1.2f * bins[0] / (bins[0] - bins[i]) * downsample + 0.4f * downsample + 1; #if DEBUG System.Console.Error.WriteLine("w={0}", w); #endif return((int)w); }
void peaks_threshold(peak[] peaks, double threshold, peak_status status, peak_status set_status) { int n = peaks.Length; for (n--; n >= 0; n--) { if ((peaks[n].value > threshold) && (peaks[n].status == status)) { peaks[n].status = set_status; } } }
/// <summary> /// Return number of peaks with spcecified status /// </summary> /// <param name="peaks"></param> /// <param name="status"></param> /// <returns></returns> int peaks_count(peak[] peaks, peak_status status) { int c = 0; for (int n = peaks.Length - 1; n >= 0; --n) { if (peaks[n].status == status) { ++c; } } return(c); }
/// <summary> /// для каждого пика вычисляется средняя амплитуда ближайших пиков N пиков /// и пики с амплитудой, меньшей 0.3 средней величины удаляются. /// </summary> /// <param name="peaks"></param> /// <param name="source_status"></param> /// <param name="target_status"></param> private void refine_front_amplitude(peak[] peaks, peak_status source_status, peak_status target_status, double min_peak_to_average_ratio) { // усредняем по 'ave_count' пикам _ДО_ и 'ave_count - 1' пикам _ПОСЛЕ_ данного пика int ave_count = 4; // смещение для корректной обработки первых и последних интервалов int offset = 0; // сумма амплитуд пиков double sum_front_amp = 0; double[] average_amplitudes = new double[peaks.Length]; for (int i = 0; i < peaks.Length; ++i) { if (i < ave_count) { offset = ave_count - i; } else if (i > peaks.Length - ave_count) { offset = peaks.Length - i - ave_count; } else { offset = 0; } sum_front_amp = 0; int averaged_count = 0; for (int j = i - ave_count + offset; (j < i + ave_count + offset) && (j < peaks.Length); ++j) { sum_front_amp += peaks[j].front_amplitude; ++averaged_count; } average_amplitudes[i] = sum_front_amp / averaged_count; } for (int i = 0; i < peaks.Length; ++i) { if (peaks[i].front_amplitude > (average_amplitudes[i] * min_peak_to_average_ratio)) { if (peaks[i].status == source_status) { peaks[i].status = target_status; } } } }
double[] peaks_repackage(peak[] peaks, peak_status status) { int i, j, n; n = peaks_count(peaks, status); double[] result = new double[n]; for (i = 0, j = 0; i < peaks.Length; i++) { if (peaks[i].status == status) { result[j++] = peaks[i].position; } } return(result); }
peak[] repackage_peaks(peak[] peaks, peak_status status) { int i, j, n; n = peaks_count(peaks, status); peak[] result = new peak[n]; for (i = 0, j = 0; i < peaks.Length; i++) { if (peaks[i].status == status) { result[j++] = new peak(peaks[i]); } } return(result); }
void refine_front_after_fall_magnitude(peak[] peaks, peak_status source_status, peak_status target_status, int min_after_fall_magnitude, double min_relative_after_fall_magnitude) { for (int i = 0; i < peaks.Length; ++i) { // не требовать мощного спада после пика -- этот метод плохо себя проявил на некоторых записях if ( //(Math.Abs(peaks[i].after_fall_magnitude) >= min_after_fall_magnitude) && //( (Math.Abs(peaks[i].after_fall_magnitude) / peaks[i].front_amplitude ) >= min_relative_after_fall_magnitude ) // если спад в 2,5 и более раза больше пика -- значит, это точно инцезура // и этот фронт нельзя считать соответствующим сердечному сокращению (СС) Math.Abs(peaks[i].after_fall_magnitude) < (2.5 * Math.Abs(peaks[i].front_amplitude)) ) { if (peaks[i].status == source_status) { peaks[i].status = target_status; } } } }
private void refine_front_length(peak[] peaks, peak_status source_status, peak_status target_status, int min_front_length, int max_front_length) { double average_front_length = 0.0; for (int i = 0; i < peaks.Length; ++i) { average_front_length += ((double)peaks[i].front_length); } average_front_length /= ((double)peaks.Length); for (int i = 0; i < peaks.Length; ++i) { if ((peaks[i].front_length > min_front_length) && (peaks[i].front_length < max_front_length)) { if (peaks[i].status == source_status) { peaks[i].status = target_status; } } } }
/// <summary> /// помечает как "хорошие" все "последние" пики на каждом фронте. /// </summary> /// <param name="peaks"></param> /// <param name="source_status"></param> /// <param name="target_status"></param> private void refine_distance(peak[] peaks, peak_status source_status, peak_status target_status) { for (int i = 0; i < peaks.Length - 1; ++i) { if (peaks[i].status == source_status) { for (int j = i + 1; j < peaks.Length; ++j) { if (peaks[j].position - peaks[j].front_length < peaks[i].position) { // peaks #i is not so good to update its status break; } else { // peak #i is ok (not overlapped by the following peak's front) peaks[i].status = target_status; break; } } } } }
void peaks_reduce_close(peak[] peaks, int min_distance, peak_status status, peak_status set_status) { int i, j = 0; bool is_first = true; for (i = 0; i < peaks.Length; i++) { if (peaks[i].status != status) { continue; } if (is_first) { is_first = false; j = i; continue; } if (peaks[i].position - peaks[j].position < min_distance) { if (peaks[i].value > peaks[j].value) { peaks[j].status = set_status; j = i; } else { peaks[i].status = set_status; } } else { j = i; } } }
int peaks_find_threshold(peak[] peaks, ref double t, peak_status status) { int i, j; double[] stat; double sum, suml, s_max, s, p1, p2; int i_max, n; n = peaks_count(peaks, status); System.Diagnostics.Debug.Assert(n >= 2); // FIXME stat = new double[n]; for (i = 0, j = 0; i < n; i++) { if (peaks[i].status == status) { stat[j++] = peaks[i].value; } } // we need to sort stat[] array... System.Array.Sort(stat); #if DEBUG array_print_indexed_to_file(stat, stat.Length, 0, "stat.dat"); for (i = 0; i < n; i++) { System.Console.Error.WriteLine("{0}", stat[i]); } #endif sum = 0; for (i = 0; i < n; i++) { sum += stat[i]; } // now we have sum of all peak values suml = stat[0]; i_max = 1; s_max = 0; for (i = 1; i < n - 1; i++) { p1 = suml * suml / i; p2 = (sum - suml) * (sum - suml) / (n - i); s = p1 + p2; #if DEBUG System.Console.Error.WriteLine("s = {0} {1} {2} {3}", i, s, p1, p2); #endif if (s > s_max) { s_max = s; i_max = i; } suml += stat[i]; } #if DEBUG System.Console.Error.WriteLine("t = {0} {1}", i_max, stat[i_max]); #endif t = (stat[i_max] + stat[i_max - 1]) * 0.5f; return(0); }
private void peaks_refine_afterfall_to_front_ration(peak[] peaks, double max_after_fall_to_front_ratio, peak_status source_status, peak_status target_status) { for (int i = 0; i < peaks.Length; ++i) { if ((peaks[i].after_fall_magnitude / peaks[i].front_amplitude) > max_after_fall_to_front_ratio) { if (peaks[i].status == source_status) { peaks[i].status = target_status; } } } }
private void peaks_with_no_preceeding_local_minimum(double[] array, peak[] peaks, peak_status source_status, peak_status target_status) { for (int i = 0; i < peaks.Length; ++i) { if (PeakHasPreceedingLocalMinimum(array, peaks[i])) { if (peaks[i].status == source_status) { peaks[i].status = target_status; } } } }