Improving the Quality of ECGs Collected using Mobile Phones: The PhysioNet/Computing in Cardiology Challenge 2011 1.0.0

File: <base>/sources/george_at_mit.edu-ChallengeEntry.java (3,579 bytes)
package org.physionet.challenge2011;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;

public class ChallengeEntry {
    // ECG data characteristics.  In the general case, these should be read
    // from metadata.  In the Challenge, they are givens, as below.
    final static int sfreq = 500;    // samples per second per signal
    final static int nsig = 12;      // number of signals
    final static int tmax = 5000;    // total number of samples per signal

    // Histogram parameters.  In the general case, these should be calculated
    // from the ADC characteristics.  For the Challenge, the ADC characteristics
    // (16 bit, 5 microvolt steps, signed (two's complement) output) are known.
    final static int nbins = 2048;  // number of bins per histogram
    final static int binwidth = 32; // raw ADC units;  1 unit = 5 microvolts
    final static int zbin = 1024;   // bin number corresponding to 0 V input

    // Classifier parameters and variables.
    int nb;  // number of non-empty bins in a histogram
    final static int nbmax = nbins/100; // acceptable signals have nb <= nbmax
    final static int nbmin = nbins/1000;// acceptable signals have nb >= nbmin
    final static int thr = 2*nsig-3;    // decision threshold
    int score = 0;	// ECG quality (0 - nsig*2).  Each signal with
			// nbmin <= nb contributes 2 to score if nb <= nbmax,
			// or 1 if nb > nbmax; acceptable ECGs have score >= thr
    final static int GOOD = 0;	// acceptable ECG
    final static int BAD =  1;	// unacceptable ECG
    int result = BAD;  // result

    // Raw ECG samples and histograms.  The raw ECG samples are read from iFile
    // as short (16-bit) signed integers, in sets of nsig (12) samples (one
    // from each signal); tmax sets from the input are stored consecutively in
    // data[].  The histograms are constructed by the code below, one for each
    // signal.
    short[] data = new short[tmax*nsig];
    short[][] ah = new short[nsig][nbins]; // amplitude histograms

    synchronized public int get_result(InputStream iFile,
				       final ECG_MetaData m_MetaData)
	throws IOException {
	ObjectInputStream in = new ObjectInputStream(iFile);
	try {
	    data = (short[])in.readObject();
	} catch (ClassNotFoundException e) {
	    e.printStackTrace();
	}
	iFile.close();	
		
	// Build amplitude histograms for each lead.
	// Each histogram has 2048 bins, and each bin covers a range of 160
	// microvolts (32 adu).  Shifting a raw sample right by 5 bits and
	// adding 1024 converts it to a bin number.
	for (int i = 0, t = 0; t < tmax; t++) {
	    for (int s = 0; s < nsig; s++) {
		int n = (data[i++] / binwidth) + zbin;
		ah[s][n]++;
	    }
	}

	score = 0; // initialize the score for this ECG

	// Examine each histogram to determine if the signal is flat (missing),
	// or else if it contains a reasonable range of amplitudes.	
	for (int s = 0; s < nsig; s++) {
	    // Count non-empty bins in histogram for signal s
	    int nb = 0;
	    for (int n = 0; n < nbins; n++) {
		if (ah[s][n] > 0) nb++;
		ah[s][n] = 0;	// reset histogram for the next ECG
	    }
	    // Adjust quality estimate based on signal s
	    if (nbmin <= nb) {
		score++;
		if (nb <= nbmax) { score++; }
	    }
	}

	// Classify this ECG based on the quality estimate.  If the value
	// returned is -9 to 0, the ECG is classified as acceptable; a
	// return value of 1 to 10 indicates it is unacceptable (by the
	// rules of the challenge.
	result = thr - score;
	if (result > 10) {
	    result = 10;
	}
	return result;
    }
}