IFMedTechdemo commited on
Commit
0bfb42f
·
verified ·
1 Parent(s): b2dc4cd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +36 -19
app.py CHANGED
@@ -5,6 +5,7 @@ from collections import deque
5
  import time
6
  import matplotlib.pyplot as plt
7
  from scipy import signal
 
8
 
9
  MAX_LEN = 500
10
  FPS = 30
@@ -56,19 +57,6 @@ def calculate_signal_quality(signal_data):
56
  quality = 0
57
  return quality
58
 
59
- def find_peaks_simple(signal_data, fps=30, min_distance=0.4):
60
- if len(signal_data) < int(fps * 1.0):
61
- return []
62
- data = np.array(signal_data)
63
- data_norm = (data - data.mean()) / (data.std() + 1e-9)
64
- min_dist = int(min_distance * fps)
65
- peaks = []
66
- for i in range(min_dist, len(data_norm) - min_dist):
67
- if data_norm[i] > data_norm[i-1] and data_norm[i] > data_norm[i+1]:
68
- if not peaks or i - peaks[-1] >= min_dist:
69
- peaks.append(i)
70
- return peaks
71
-
72
  def process_frame(frame, state):
73
  if frame is None:
74
  return None, state["red"], state["green"], state["time"]
@@ -108,14 +96,22 @@ def process_frame(frame, state):
108
  return img, red_list, green_list, time_list
109
 
110
  def make_plots(time_list, red_list, green_list):
111
- """2-subplot layout like app_old.py"""
 
 
 
112
  if len(time_list) < 10:
113
  return None
 
114
  time_array = np.array(time_list)
115
  red_array = np.array(red_list)
116
  green_array = np.array(green_list)
 
 
117
  red_norm = (red_array - red_array.mean()) / (red_array.std() + 1e-9)
118
  green_norm = (green_array - green_array.mean()) / (green_array.std() + 1e-9)
 
 
119
  nyquist = FPS / 2
120
  low = 0.67 / nyquist
121
  high = 3.33 / nyquist
@@ -126,27 +122,45 @@ def make_plots(time_list, red_list, green_list):
126
  else:
127
  red_filt = red_norm
128
  green_filt = green_norm
 
 
 
 
 
 
 
129
  hr_bpm, _ = estimate_heart_rate(green_array, fps=FPS, window_secs=WINDOW_SIZE)
130
- peaks_idx = find_peaks_simple(green_filt, fps=FPS)
 
131
  fig = plt.figure(figsize=(12, 4))
 
 
132
  ax1 = fig.add_subplot(1, 2, 1)
133
- ax2 = fig.add_subplot(1, 2, 2)
134
  ax1.plot(time_array, red_filt, label='Red (filtered)', color='red', linewidth=1.5)
135
  ax1.plot(time_array, green_filt, label='Green (filtered)', color='green', linewidth=1.5)
136
  ax1.set_xlabel('Time (s)')
137
  ax1.set_ylabel('Normalized Intensity')
138
- ax1.set_title('Filtered PPG Signals (Last 5s)')
139
  ax1.legend(loc='upper right')
140
  ax1.grid(True, alpha=0.3)
 
 
 
141
  ax2.plot(time_array, green_filt, label='Green (filtered)', color='green', linewidth=1.5)
142
  if len(peaks_idx) > 0:
143
  ax2.scatter(time_array[peaks_idx], green_filt[peaks_idx], color='red', s=50, label='Peaks', zorder=5)
144
- hr_title = f'Peak Detection (HR ≈ {hr_bpm:.1f} bpm)' if hr_bpm is not None else 'Peak Detection'
 
 
 
 
 
 
145
  ax2.set_xlabel('Time (s)')
146
  ax2.set_ylabel('Normalized Intensity')
147
- ax2.set_title(hr_title)
148
  ax2.legend(loc='upper right')
149
  ax2.grid(True, alpha=0.3)
 
150
  fig.tight_layout()
151
  return fig
152
 
@@ -156,12 +170,15 @@ def snap(frame, state):
156
  red_quality = calculate_signal_quality(state["red"])
157
  green_quality = calculate_signal_quality(state["green"])
158
  plot_fig = make_plots(time_list, red_list, green_list)
 
159
  hr_text = f'{bpm:.1f} bpm' if bpm is not None else 'Measuring...'
160
  red_quality_text = f'Red: {red_quality:.0f}%'
161
  green_quality_text = f'Green: {green_quality:.0f}%'
162
  stats_text = f'Frames: {state["frame_count"]} | Duration: {state["session_duration"]:.1f}s | Samples: {len(state["red"])}/{MAX_LEN}'
 
163
  return img, plot_fig, hr_text, red_quality_text, green_quality_text, stats_text, state
164
 
 
165
  with gr.Blocks(title="Finger Vital Sign Monitor") as demo:
166
  gr.Markdown("# Real-time Finger Vital Sign Monitoring")
167
  gr.Markdown("Place your finger on the camera and keep it steady. The system will continuously monitor PPG signals and estimate heart rate.")
 
5
  import time
6
  import matplotlib.pyplot as plt
7
  from scipy import signal
8
+ from scipy.signal import find_peaks
9
 
10
  MAX_LEN = 500
11
  FPS = 30
 
57
  quality = 0
58
  return quality
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  def process_frame(frame, state):
61
  if frame is None:
62
  return None, state["red"], state["green"], state["time"]
 
96
  return img, red_list, green_list, time_list
97
 
98
  def make_plots(time_list, red_list, green_list):
99
+ """Create 2-subplot plot matching app_old.py layout:
100
+ Left subplot: Red and Green filtered signals overlay
101
+ Right subplot: Green signal with peak detection
102
+ """
103
  if len(time_list) < 10:
104
  return None
105
+
106
  time_array = np.array(time_list)
107
  red_array = np.array(red_list)
108
  green_array = np.array(green_list)
109
+
110
+ # Normalize signals
111
  red_norm = (red_array - red_array.mean()) / (red_array.std() + 1e-9)
112
  green_norm = (green_array - green_array.mean()) / (green_array.std() + 1e-9)
113
+
114
+ # Apply bandpass filter
115
  nyquist = FPS / 2
116
  low = 0.67 / nyquist
117
  high = 3.33 / nyquist
 
122
  else:
123
  red_filt = red_norm
124
  green_filt = green_norm
125
+
126
+ # Detect peaks on green channel
127
+ min_dist = int(0.35 * FPS)
128
+ prom = 0.3 * np.std(green_filt) if np.std(green_filt) > 0 else 0.1
129
+ peaks_idx, _ = find_peaks(green_filt, distance=min_dist, prominence=prom)
130
+
131
+ # Estimate HR
132
  hr_bpm, _ = estimate_heart_rate(green_array, fps=FPS, window_secs=WINDOW_SIZE)
133
+
134
+ # Create figure with 2 subplots (1 row, 2 columns)
135
  fig = plt.figure(figsize=(12, 4))
136
+
137
+ # Left subplot: Both red and green signals
138
  ax1 = fig.add_subplot(1, 2, 1)
 
139
  ax1.plot(time_array, red_filt, label='Red (filtered)', color='red', linewidth=1.5)
140
  ax1.plot(time_array, green_filt, label='Green (filtered)', color='green', linewidth=1.5)
141
  ax1.set_xlabel('Time (s)')
142
  ax1.set_ylabel('Normalized Intensity')
143
+ ax1.set_title('Filtered PPG (last 5s)')
144
  ax1.legend(loc='upper right')
145
  ax1.grid(True, alpha=0.3)
146
+
147
+ # Right subplot: Green signal with peaks
148
+ ax2 = fig.add_subplot(1, 2, 2)
149
  ax2.plot(time_array, green_filt, label='Green (filtered)', color='green', linewidth=1.5)
150
  if len(peaks_idx) > 0:
151
  ax2.scatter(time_array[peaks_idx], green_filt[peaks_idx], color='red', s=50, label='Peaks', zorder=5)
152
+
153
+ # Set title with HR estimate
154
+ if hr_bpm is not None:
155
+ ax2.set_title(f'Peaks (HR = {hr_bpm:.1f} bpm)')
156
+ else:
157
+ ax2.set_title('Peaks')
158
+
159
  ax2.set_xlabel('Time (s)')
160
  ax2.set_ylabel('Normalized Intensity')
 
161
  ax2.legend(loc='upper right')
162
  ax2.grid(True, alpha=0.3)
163
+
164
  fig.tight_layout()
165
  return fig
166
 
 
170
  red_quality = calculate_signal_quality(state["red"])
171
  green_quality = calculate_signal_quality(state["green"])
172
  plot_fig = make_plots(time_list, red_list, green_list)
173
+
174
  hr_text = f'{bpm:.1f} bpm' if bpm is not None else 'Measuring...'
175
  red_quality_text = f'Red: {red_quality:.0f}%'
176
  green_quality_text = f'Green: {green_quality:.0f}%'
177
  stats_text = f'Frames: {state["frame_count"]} | Duration: {state["session_duration"]:.1f}s | Samples: {len(state["red"])}/{MAX_LEN}'
178
+
179
  return img, plot_fig, hr_text, red_quality_text, green_quality_text, stats_text, state
180
 
181
+
182
  with gr.Blocks(title="Finger Vital Sign Monitor") as demo:
183
  gr.Markdown("# Real-time Finger Vital Sign Monitoring")
184
  gr.Markdown("Place your finger on the camera and keep it steady. The system will continuously monitor PPG signals and estimate heart rate.")