trixy194t commited on
Commit
b47eaf8
·
verified ·
1 Parent(s): 222ed66

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -57
app.py CHANGED
@@ -154,31 +154,33 @@ try:
154
 
155
  except Exception as e1:
156
  print(f" Method 1 failed: {str(e1)[:150]}")
157
- print(" Trying Method 2: Load weights only...")
158
 
159
  try:
160
- # Build a compatible architecture based on your latest model code
161
  new_counting_model = models.Sequential([
162
- layers.Input(shape=(128, None, 1)), # Variable time dimension
163
- layers.Conv2D(32, (3, 3), activation='relu', padding='same'),
164
- layers.BatchNormalization(),
165
  layers.MaxPooling2D((2, 2)),
166
-
167
- layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
168
  layers.BatchNormalization(),
 
 
 
169
  layers.MaxPooling2D((2, 2)),
170
-
171
- layers.Conv2D(128, (3, 3), activation='relu', padding='same'),
172
  layers.BatchNormalization(),
173
- layers.GlobalAveragePooling2D(),
 
 
 
174
 
 
175
  layers.Dense(128, activation='relu'),
176
- layers.Dropout(0.4),
177
  layers.Dense(1, activation='sigmoid')
178
  ])
179
 
180
  new_counting_model.load_weights(NEW_MODEL_PATH)
181
- print("✅ New Counting Model Loaded Successfully (Method 2 - Weight Reconstruction)!")
182
  print(f" Model input shape: {new_counting_model.input_shape}")
183
  print(f" Model output shape: {new_counting_model.output_shape}")
184
 
@@ -269,36 +271,39 @@ def predict_snoring_segment(y_segment, sr, model):
269
  # ========================================
270
 
271
  def extract_spectrogram_for_new_model(y_segment, sr):
272
- """Extract spectrogram for new model (from latest model code)"""
273
- # Resample to 16kHz if needed
274
  if sr != NEW_MODEL_SAMPLE_RATE:
275
  y_segment = librosa.resample(y_segment, orig_sr=sr, target_sr=NEW_MODEL_SAMPLE_RATE)
276
  sr = NEW_MODEL_SAMPLE_RATE
277
 
278
- # Ensure exact length
279
- target_len = NEW_MODEL_SAMPLE_RATE * NEW_MODEL_DURATION
280
  if len(y_segment) < target_len:
281
  y_segment = np.pad(y_segment, (0, target_len - len(y_segment)))
282
  else:
283
  y_segment = y_segment[:target_len]
284
 
285
- # Generate Mel Spectrogram
286
- spec = librosa.feature.melspectrogram(y=y_segment, sr=sr, n_mels=128, n_fft=2048, hop_length=512)
287
- log_spec = librosa.power_to_db(spec, ref=np.max)
288
- # Normalize
289
- log_spec = (log_spec - log_spec.min()) / (log_spec.max() - log_spec.min() + 1e-6)
 
 
290
  return log_spec[..., np.newaxis]
291
 
292
  def count_snores_in_segment(y_segment, sr, model, threshold=0.7):
293
  """
294
  Count individual snores within a detected snoring segment
295
  Uses sliding 2-second window to detect multiple snore events
 
296
  """
297
  if model is None:
298
  return 0
299
 
300
  try:
301
- # Resample to 16kHz for new model
302
  if sr != NEW_MODEL_SAMPLE_RATE:
303
  y_segment = librosa.resample(y_segment, orig_sr=sr, target_sr=NEW_MODEL_SAMPLE_RATE)
304
  sr = NEW_MODEL_SAMPLE_RATE
@@ -307,16 +312,18 @@ def count_snores_in_segment(y_segment, sr, model, threshold=0.7):
307
  is_snoring = False
308
 
309
  # Slide a 2-second window across the segment
310
- window_size = 2 * sr
311
  step_size = int(0.5 * sr) # 0.5s steps for high resolution
312
 
313
  for start in range(0, len(y_segment) - window_size, step_size):
314
  chunk = y_segment[start : start + window_size]
315
 
316
- # Process chunk to spectrogram
317
- spec = librosa.feature.melspectrogram(y=chunk, sr=sr, n_mels=128, n_fft=2048, hop_length=512)
318
- log_spec = librosa.power_to_db(spec, ref=np.max)
319
- log_spec = (log_spec - log_spec.min()) / (log_spec.max() - log_spec.min() + 1e-6)
 
 
320
 
321
  # Predict
322
  prediction = model.predict(log_spec[np.newaxis, ..., np.newaxis], verbose=0)
@@ -766,7 +773,7 @@ async def analyze_audio(file: UploadFile = File(...)):
766
 
767
  annotations.append(annotation)
768
 
769
- # Apnea detection - only with HIGH and MEDIUM confidence snoring
770
  for i in range(len(segment_results) - 1):
771
  current = segment_results[i]
772
  if current['is_snoring'] and current['confidence_level'] in ['HIGH', 'MEDIUM']:
@@ -774,35 +781,27 @@ async def analyze_audio(file: UploadFile = File(...)):
774
  if segment_results[j]['is_snoring'] and segment_results[j]['confidence_level'] in ['HIGH', 'MEDIUM']:
775
  gap_duration = segment_results[j]['start_time'] - current['end_time']
776
 
777
- # Apnea detection with confidence requirement
778
- if 10.0 <= gap_duration <= 90.0:
779
- # Require higher confidence for longer gaps
780
- if gap_duration > 30.0:
781
- # For long gaps, require at least one HIGH confidence
782
- if current['confidence_level'] == 'HIGH' or segment_results[j]['confidence_level'] == 'HIGH':
783
- apnea_count += 1
784
- risk = "MEDIUM" if gap_duration < 40.0 else "HIGH"
785
- annotations.append({
786
- "label": "APNEA",
787
- "start_sec": round(current['end_time'], 2),
788
- "end_sec": round(segment_results[j]['start_time'], 2),
789
- "duration": round(gap_duration, 2),
790
- "risk_level": risk,
791
- "note": "Detected between high/medium confidence snoring segments"
792
- })
793
- print(f" ⚠️ Apnea: {gap_duration:.1f}s gap (between confident snoring segments)")
794
  else:
795
- # Shorter gaps - any confidence level (but already filtered for HIGH/MEDIUM)
796
- apnea_count += 1
797
- risk = "LOW" if gap_duration < 20.0 else "MEDIUM"
798
- annotations.append({
799
- "label": "APNEA",
800
- "start_sec": round(current['end_time'], 2),
801
- "end_sec": round(segment_results[j]['start_time'], 2),
802
- "duration": round(gap_duration, 2),
803
- "risk_level": risk
804
- })
805
- print(f" ⚠️ Apnea: {gap_duration:.1f}s gap")
806
  break
807
 
808
  # 7. STATISTICS
@@ -837,13 +836,14 @@ async def analyze_audio(file: UploadFile = File(...)):
837
  "valid_recording": True,
838
  "validation_warning": validation_warning,
839
  "snore_count": snore_count,
 
840
  "high_confidence_snores": high_conf_count,
841
  "medium_confidence_snores": medium_conf_count,
842
  "rejected_potential_snores": rejected_count,
843
  "total_segments": total_segments,
844
  "snoring_percentage": round(snoring_percentage, 2),
845
  "apnea_count": apnea_count,
846
- "apnea_risk": risk_summary,
847
  "ahi_score": round(ahi, 1),
848
  "detection_mode": "STRICT: Only HIGH and MEDIUM confidence snoring detection",
849
  "detection_note": detection_note,
 
154
 
155
  except Exception as e1:
156
  print(f" Method 1 failed: {str(e1)[:150]}")
157
+ print(" Trying Method 2: Rebuild EXACT architecture from training code...")
158
 
159
  try:
160
+ # Build EXACT architecture from your training code
161
  new_counting_model = models.Sequential([
162
+ # First Block: Detects basic edges in sound
163
+ layers.Conv2D(32, (3, 3), activation='relu', input_shape=(128, 431, 1)),
 
164
  layers.MaxPooling2D((2, 2)),
 
 
165
  layers.BatchNormalization(),
166
+
167
+ # Second Block: Detects complex patterns (snore textures)
168
+ layers.Conv2D(64, (3, 3), activation='relu'),
169
  layers.MaxPooling2D((2, 2)),
 
 
170
  layers.BatchNormalization(),
171
+
172
+ # Third Block
173
+ layers.Conv2D(128, (3, 3), activation='relu'),
174
+ layers.MaxPooling2D((2, 2)),
175
 
176
+ layers.Flatten(),
177
  layers.Dense(128, activation='relu'),
178
+ layers.Dropout(0.5),
179
  layers.Dense(1, activation='sigmoid')
180
  ])
181
 
182
  new_counting_model.load_weights(NEW_MODEL_PATH)
183
+ print("✅ New Counting Model Loaded Successfully (Method 2 - EXACT Architecture Rebuild)!")
184
  print(f" Model input shape: {new_counting_model.input_shape}")
185
  print(f" Model output shape: {new_counting_model.output_shape}")
186
 
 
271
  # ========================================
272
 
273
  def extract_spectrogram_for_new_model(y_segment, sr):
274
+ """Extract spectrogram for new model - EXACT from training code"""
275
+ # Resample to 16kHz as in training
276
  if sr != NEW_MODEL_SAMPLE_RATE:
277
  y_segment = librosa.resample(y_segment, orig_sr=sr, target_sr=NEW_MODEL_SAMPLE_RATE)
278
  sr = NEW_MODEL_SAMPLE_RATE
279
 
280
+ # Ensure exact length: 160,000 samples for 10s @ 16kHz
281
+ target_len = NEW_MODEL_SAMPLE_RATE * NEW_MODEL_DURATION # 160,000
282
  if len(y_segment) < target_len:
283
  y_segment = np.pad(y_segment, (0, target_len - len(y_segment)))
284
  else:
285
  y_segment = y_segment[:target_len]
286
 
287
+ # Convert to Mel Spectrogram - EXACT same as training
288
+ spectrogram = librosa.feature.melspectrogram(y=y_segment, sr=sr, n_mels=128)
289
+ log_spec = librosa.power_to_db(spectrogram, ref=np.max)
290
+
291
+ # Normalize between 0 and 1 - EXACT same as training
292
+ log_spec = (log_spec - log_spec.min()) / (log_spec.max() - log_spec.min())
293
+
294
  return log_spec[..., np.newaxis]
295
 
296
  def count_snores_in_segment(y_segment, sr, model, threshold=0.7):
297
  """
298
  Count individual snores within a detected snoring segment
299
  Uses sliding 2-second window to detect multiple snore events
300
+ USES EXACT PREPROCESSING FROM TRAINING CODE
301
  """
302
  if model is None:
303
  return 0
304
 
305
  try:
306
+ # Resample to 16kHz for new model (as in training)
307
  if sr != NEW_MODEL_SAMPLE_RATE:
308
  y_segment = librosa.resample(y_segment, orig_sr=sr, target_sr=NEW_MODEL_SAMPLE_RATE)
309
  sr = NEW_MODEL_SAMPLE_RATE
 
312
  is_snoring = False
313
 
314
  # Slide a 2-second window across the segment
315
+ window_size = 2 * sr # 2 seconds
316
  step_size = int(0.5 * sr) # 0.5s steps for high resolution
317
 
318
  for start in range(0, len(y_segment) - window_size, step_size):
319
  chunk = y_segment[start : start + window_size]
320
 
321
+ # Process chunk to spectrogram - EXACT same as training code
322
+ spectrogram = librosa.feature.melspectrogram(y=chunk, sr=sr, n_mels=128)
323
+ log_spec = librosa.power_to_db(spectrogram, ref=np.max)
324
+
325
+ # Normalize between 0 and 1 - EXACT same as training
326
+ log_spec = (log_spec - log_spec.min()) / (log_spec.max() - log_spec.min())
327
 
328
  # Predict
329
  prediction = model.predict(log_spec[np.newaxis, ..., np.newaxis], verbose=0)
 
773
 
774
  annotations.append(annotation)
775
 
776
+ # Apnea detection - FOCUS: Silence gaps of 10-120 seconds between snoring events
777
  for i in range(len(segment_results) - 1):
778
  current = segment_results[i]
779
  if current['is_snoring'] and current['confidence_level'] in ['HIGH', 'MEDIUM']:
 
781
  if segment_results[j]['is_snoring'] and segment_results[j]['confidence_level'] in ['HIGH', 'MEDIUM']:
782
  gap_duration = segment_results[j]['start_time'] - current['end_time']
783
 
784
+ # APNEA: Silence gap between 10-120 seconds
785
+ if 10.0 <= gap_duration <= 120.0:
786
+ apnea_count += 1
787
+
788
+ # Risk classification based on gap duration
789
+ if gap_duration < 20.0:
790
+ risk = "LOW"
791
+ elif gap_duration < 40.0:
792
+ risk = "MEDIUM"
 
 
 
 
 
 
 
 
793
  else:
794
+ risk = "HIGH"
795
+
796
+ annotations.append({
797
+ "label": "APNEA",
798
+ "start_sec": round(current['end_time'], 2),
799
+ "end_sec": round(segment_results[j]['start_time'], 2),
800
+ "duration": round(gap_duration, 2),
801
+ "risk_level": risk,
802
+ "note": f"Silence gap of {gap_duration:.1f}s between snoring events"
803
+ })
804
+ print(f" ⚠️ Apnea: {gap_duration:.1f}s silence gap (Risk: {risk})")
805
  break
806
 
807
  # 7. STATISTICS
 
836
  "valid_recording": True,
837
  "validation_warning": validation_warning,
838
  "snore_count": snore_count,
839
+ "total_snore_counts": total_individual_snores, # NEW: Total individual snores counted by new model
840
  "high_confidence_snores": high_conf_count,
841
  "medium_confidence_snores": medium_conf_count,
842
  "rejected_potential_snores": rejected_count,
843
  "total_segments": total_segments,
844
  "snoring_percentage": round(snoring_percentage, 2),
845
  "apnea_count": apnea_count,
846
+ "overall_risk": risk_summary,
847
  "ahi_score": round(ahi, 1),
848
  "detection_mode": "STRICT: Only HIGH and MEDIUM confidence snoring detection",
849
  "detection_note": detection_note,