File size: 5,168 Bytes
151fb28
aa8be0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151fb28
aa8be0a
 
151fb28
aa8be0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151fb28
aa8be0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3dabb6b
aa8be0a
 
 
3dabb6b
aa8be0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3dabb6b
aa8be0a
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import os
import cv2
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow.keras.models import load_model
from flask import Flask, request, render_template
from werkzeug.utils import secure_filename
import biosppy.signals.ecg as ecg

app = Flask(__name__)

UPLOAD_FOLDER = 'uploads'
ALLOWED_EXTENSIONS = {'csv', 'png', 'jpg', 'jpeg'}
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

# Load the pre-trained model
model = load_model('ecgScratchEpoch2.hdf5')

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def image_to_signal(image_path):
    # Read and preprocess the image
    img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
    if img is None:
        raise ValueError("Failed to load image")
    
    # Resize to a standard height for consistency (e.g., 500 pixels)
    img = cv2.resize(img, (1000, 500))
    
    # Apply thresholding to isolate the waveform
    _, binary = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY_INV)
    
    # Find contours of the waveform
    contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if not contours:
        raise ValueError("No waveform detected in the image")
    
    # Assume the largest contour is the ECG waveform
    contour = max(contours, key=cv2.contourArea)
    
    # Extract y-coordinates (signal amplitude) along x-axis
    signal = []
    width = img.shape[1]
    for x in range(width):
        column = contour[contour[:, :, 0] == x]
        if len(column) > 0:
            # Take the average y-coordinate if multiple points exist
            y = np.mean(column[:, :, 1])
            signal.append(y)
        else:
            # Interpolate if no point is found
            signal.append(signal[-1] if signal else 0)
    
    # Normalize signal to match expected amplitude range
    signal = np.array(signal)
    signal = (signal - np.min(signal)) / (np.max(signal) - np.min(signal)) * 1000
    
    # Save to CSV
    csv_path = os.path.join(app.config['UPLOAD_FOLDER'], 'converted_signal.csv')
    df = pd.DataFrame(signal, columns=[' Sample Value'])
    df.to_csv(csv_path, index=False)
    
    return csv_path

def model_predict(uploaded_files, model):
    output = []
    for path in uploaded_files:
        APC, NORMAL, LBB, PVC, PAB, RBB, VEB = [], [], [], [], [], [], []
        output.append(str(path))
        result = {"APC": APC, "Normal": NORMAL, "LBB": LBB, "PAB": PAB, "PVC": PVC, "RBB": RBB, "VEB": VEB}
        
        kernel = np.ones((4,4), np.uint8)
        csv = pd.read_csv(path)
        csv_data = csv[' Sample Value']
        data = np.array(csv_data)
        signals = []
        count = 1
        peaks = ecg.christov_segmenter(signal=data, sampling_rate=200)[0]
        indices = []
        
        for i in peaks[1:-1]:
            diff1 = abs(peaks[count - 1] - i)
            diff2 = abs(peaks[count + 1] - i)
            x = peaks[count - 1] + diff1 // 2
            y = peaks[count + 1] - diff2 // 2
            signal = data[x:y]
            signals.append(signal)
            count += 1
            indices.append((x, y))
        
        for signal, index in zip(signals, indices):
            if len(signal) > 10:
                img = np.zeros((128, 128))
                for i in range(len(signal)):
                    img[i, int(signal[i] / 10)] = 255
                img = cv2.dilate(img, kernel, iterations=1)
                img = img.reshape(128, 128, 1)
                prediction = model.predict(np.array([img])).argmax()
                classes = ['Normal', 'APC', 'LBB', 'PAB', 'PVC', 'RBB', 'VEB']
                result[classes[prediction]].append(index)
        
        output.append(result)
    
    return output

@app.route('/', methods=['GET'])
def index():
    return render_template('index.html')

@app.route('/', methods=['POST'])
def upload_file():
    if 'files[]' not in request.files:
        return render_template('index.html', message='No file part')
    
    files = request.files.getlist('files[]')
    file_paths = []
    
    for file in files:
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
            file.save(file_path)
            
            # If the file is an image, convert to CSV
            if filename.rsplit('.', 1)[1].lower() in {'png', 'jpg', 'jpeg'}:
                try:
                    csv_path = image_to_signal(file_path)
                    file_paths.append(csv_path)
                except Exception as e:
                    return render_template('index.html', message=f'Error processing image: {str(e)}')
            else:
                file_paths.append(file_path)
    
    if not file_paths:
        return render_template('index.html', message='No valid files uploaded')
    
    results = model_predict(file_paths, model)
    return render_template('index.html', prediction=results)

if __name__ == '__main__':
    if not os.path.exists(UPLOAD_FOLDER):
        os.makedirs(UPLOAD_FOLDER)
    app.run(debug=True, host='0.0.0.0', port=5000)