parth036 commited on
Commit
8882192
·
verified ·
1 Parent(s): 3452443

Upload folder using huggingface_hub

Browse files
.claude/settings.local.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "PowerShell(cd \"d:\\\\ALL PROGRAM PROJECTS\\\\ML\\\\Detection-of-Anxiety-and-Depression-main\\\\Detection-of-Anxiety-and-Depression-main\"; $env:FLASK_APP=\"app.py\"; python -c \"import app\" 2>&1 | Select-Object -First 30)"
5
+ ]
6
+ }
7
+ }
.dockerignore ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ venv/
2
+ .venv/
3
+ C:/venvs/
4
+ __pycache__/
5
+ *.pyc
6
+ .git/
7
+ .github/
8
+ tests/
9
+ training/
10
+ docs/
11
+ Speech-Emotion-Analyzer/
12
+ fer2013.csv
13
+ output10.wav
14
+ output.txt
15
+ install_log.txt
16
+ *.whl
17
+ .pytest_cache/
.gitattributes CHANGED
@@ -1,35 +1,16 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ # Normalize line endings: let Git handle text automatically
2
+ * text=auto
3
+
4
+ # Explicitly mark binary assets so Git never tries to diff/convert them
5
+ *.h5 binary
6
+ *.whl binary
7
+ *.png binary
8
+ *.jpg binary
9
+ *.jpeg binary
10
+ *.wav binary
11
+ *.xml binary
12
+ *.ipynb binary
13
+ *.svg binary
14
+ anxiety_app/static/Presentation2.jpg filter=lfs diff=lfs merge=lfs -text
15
+ models/fer.h5 filter=lfs diff=lfs merge=lfs -text
16
+ models/voice_model.h5 filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.gitignore ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Virtual environments
2
+ venv/
3
+ .venv/
4
+ env/
5
+ ENV/
6
+
7
+ # Python cache / build
8
+ __pycache__/
9
+ *.py[cod]
10
+ *$py.class
11
+ *.egg-info/
12
+ .eggs/
13
+ build/
14
+ dist/
15
+
16
+ # Runtime artifacts produced by the app
17
+ output.txt
18
+ /output10.wav
19
+ install_log.txt
20
+
21
+ # Training artifacts / datasets (not committed)
22
+ results/
23
+ data/
24
+ *.npy
25
+ fer2013.csv
26
+ balanced-all.csv
27
+
28
+ # IDE / OS
29
+ .vscode/
30
+ .idea/
31
+ .DS_Store
32
+ Thumbs.db
Dockerfile ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Container image for the multimodal affect-screening app.
2
+ # Works locally (docker run) and on Hugging Face Spaces (Docker SDK, port 7860).
3
+ FROM python:3.11-slim
4
+
5
+ # System libs: ffmpeg (decode browser webm/ogg audio), libGL + glib (OpenCV).
6
+ RUN apt-get update && apt-get install -y --no-install-recommends \
7
+ ffmpeg libgl1 libglib2.0-0 \
8
+ && rm -rf /var/lib/apt/lists/*
9
+
10
+ ENV PYTHONUNBUFFERED=1 \
11
+ TF_CPP_MIN_LOG_LEVEL=3 \
12
+ TF_USE_LEGACY_KERAS=1 \
13
+ TF_ENABLE_ONEDNN_OPTS=0 \
14
+ HF_HOME=/tmp/hf \
15
+ PORT=7860
16
+
17
+ WORKDIR /app
18
+
19
+ # Install Python deps first for better layer caching.
20
+ COPY requirements-modern.txt .
21
+ RUN pip install --no-cache-dir -r requirements-modern.txt
22
+
23
+ # App code + models.
24
+ COPY . .
25
+
26
+ EXPOSE 7860
27
+
28
+ # Single worker (TF model is loaded lazily per process); threads handle concurrency.
29
+ CMD ["sh", "-c", "gunicorn --bind 0.0.0.0:${PORT:-7860} --workers 1 --threads 4 --timeout 180 app:app"]
Emotion_Anaysis.ipynb ADDED
Binary file (40.7 kB). View file
 
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Parth Ashtikar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
anxiety_app/__init__.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Application factory for the multimodal affect-screening app."""
2
+ from flask import Flask
3
+ from flask_bootstrap import Bootstrap
4
+
5
+ from .logging_config import configure_logging
6
+
7
+
8
+ def create_app():
9
+ configure_logging()
10
+ app = Flask(__name__) # templates/ and static/ live inside this package
11
+ Bootstrap(app)
12
+
13
+ from .routes import main
14
+ app.register_blueprint(main)
15
+ return app
anxiety_app/config.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Central configuration: filesystem paths and runtime settings.
2
+
3
+ Paths are resolved relative to the project root so the app works regardless of
4
+ the current working directory.
5
+ """
6
+ from pathlib import Path
7
+
8
+ # <repo root>/anxiety_app/config.py -> <repo root>
9
+ BASE_DIR = Path(__file__).resolve().parent.parent
10
+ MODELS_DIR = BASE_DIR / "models"
11
+
12
+ # Model artifacts
13
+ FACE_MODEL_JSON = MODELS_DIR / "fer.json"
14
+ FACE_MODEL_WEIGHTS = MODELS_DIR / "fer.h5"
15
+ HAAR_CASCADE = MODELS_DIR / "haarcascade_frontalface_default.xml"
16
+ VOICE_MODEL_JSON = MODELS_DIR / "voice_model.json"
17
+ VOICE_MODEL_WEIGHTS = MODELS_DIR / "voice_model.h5"
18
+
19
+ # Where microphone recordings are written
20
+ RECORDING_PATH = BASE_DIR / "output10.wav"
21
+
22
+ # Audio recording parameters
23
+ SAMPLE_RATE = 44100
24
+ CHANNELS = 2
25
+ CHUNK = 1024
26
+ RECORD_SECONDS = 4
27
+
28
+ # FER2013 emotion classes in the model's native (Kaggle) order. The app maps
29
+ # "fear" -> Anxiety and "sad" -> Depressed as supplementary affective signals
30
+ # (NOT a clinical diagnosis).
31
+ FACE_EMOTIONS = ("angry", "disgust", "fear", "happy", "sad", "surprise", "neutral")
32
+
33
+ # Voice model output classes (gender + emotion).
34
+ VOICE_CLASSES = (
35
+ "Male Angry", "Male Calm", "Male Anxious", "Male Happy", "Male Depressed",
36
+ "Female Angry", "Female Calm", "Female Anxious", "Female Happy", "Female Depressed",
37
+ )
anxiety_app/logging_config.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Logging setup shared across the app."""
2
+ import logging
3
+
4
+ _CONFIGURED = False
5
+
6
+
7
+ def configure_logging(level=logging.INFO):
8
+ """Configure root logging once with a concise, timestamped format."""
9
+ global _CONFIGURED
10
+ if _CONFIGURED:
11
+ return
12
+ logging.basicConfig(
13
+ level=level,
14
+ format="%(asctime)s %(levelname)-7s %(name)s | %(message)s",
15
+ datefmt="%H:%M:%S",
16
+ )
17
+ _CONFIGURED = True
anxiety_app/routes.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """HTTP routes for the app, grouped in a single blueprint.
2
+
3
+ Capture happens in the browser (getUserMedia / MediaRecorder); frames and audio
4
+ are POSTed to JSON API endpoints. Heavy ML services are imported lazily inside
5
+ handlers so importing this module (e.g. for fast route tests) does not pull in
6
+ TensorFlow/OpenCV.
7
+ """
8
+ import logging
9
+
10
+ from flask import Blueprint, jsonify, render_template, request
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ main = Blueprint("main", __name__)
15
+
16
+ TITLE = "Anxiety and Depression Detection"
17
+
18
+
19
+ @main.route("/")
20
+ @main.route("/home")
21
+ def index():
22
+ return render_template("index.html")
23
+
24
+
25
+ @main.route("/qstn")
26
+ def phq():
27
+ return render_template("phq9.html", data=TITLE)
28
+
29
+
30
+ @main.route("/face")
31
+ def face():
32
+ return render_template("face.html", data=TITLE)
33
+
34
+
35
+ @main.route("/voice")
36
+ def voice():
37
+ return render_template("voice.html", data="Allow the mic, then record a few seconds.")
38
+
39
+
40
+ # ---------------------------------------------------------------- JSON API ---
41
+
42
+ @main.post("/api/face")
43
+ def api_face():
44
+ """Classify faces in one browser-captured frame (multipart 'frame')."""
45
+ from .services import face_emotion
46
+
47
+ file = request.files.get("frame")
48
+ if file is None:
49
+ return jsonify(error="no frame uploaded"), 400
50
+ try:
51
+ return jsonify(faces=face_emotion.classify_frame(file.read()))
52
+ except Exception as exc: # noqa: BLE001
53
+ logger.exception("Face frame classification failed")
54
+ return jsonify(error=str(exc)), 500
55
+
56
+
57
+ @main.post("/api/voice")
58
+ def api_voice():
59
+ """Classify a browser-recorded audio blob (multipart 'audio')."""
60
+ from .services import voice_emotion
61
+
62
+ file = request.files.get("audio")
63
+ if file is None:
64
+ return jsonify(error="no audio uploaded"), 400
65
+ try:
66
+ return jsonify(label=voice_emotion.analyze_upload(file))
67
+ except Exception as exc: # noqa: BLE001
68
+ logger.exception("Voice analysis failed")
69
+ return jsonify(error=str(exc)), 500
anxiety_app/services/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ """Inference and capture services for the app."""
anxiety_app/services/face_emotion.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Facial emotion recognition from a live webcam feed.
2
+
3
+ Loads the FER2013 CNN once (lazily) and classifies each detected face. Emotions
4
+ are tallied across the session; the dominant affective signal is returned as a
5
+ supplementary cue (Anxious / Depressed / Normal) -- NOT a clinical diagnosis.
6
+
7
+ Preprocessing: per-image standardization (subtract the face's mean, divide by
8
+ its std) to match how the model was trained. This fixes a train/serve skew in
9
+ the original code (which divided by 255) and roughly doubles accuracy.
10
+ """
11
+ import logging
12
+
13
+ import numpy as np
14
+
15
+ from .. import config
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # Heavy deps (tf_keras, cv2) are imported lazily so the package can be imported
20
+ # (e.g. for fast route tests) without TensorFlow/OpenCV present.
21
+ _model = None
22
+ _cascade = None
23
+
24
+
25
+ def _load():
26
+ global _model, _cascade
27
+ if _model is None:
28
+ import cv2
29
+ from tf_keras.models import model_from_json
30
+
31
+ logger.info("Loading face model from %s", config.FACE_MODEL_JSON)
32
+ _model = model_from_json(config.FACE_MODEL_JSON.read_text())
33
+ _model.load_weights(str(config.FACE_MODEL_WEIGHTS))
34
+ _cascade = cv2.CascadeClassifier(str(config.HAAR_CASCADE))
35
+ return _model, _cascade
36
+
37
+
38
+ def preprocess(face_gray):
39
+ """48x48 grayscale -> standardized (1, 48, 48, 1) batch."""
40
+ import cv2
41
+
42
+ face = cv2.resize(face_gray, (48, 48)).astype("float32")
43
+ face = (face - face.mean()) / (face.std() + 1e-7) # per-image standardization
44
+ return face.reshape(1, 48, 48, 1)
45
+
46
+
47
+ def classify_face(face_gray):
48
+ """Return the predicted FER2013 emotion label for one grayscale face crop."""
49
+ model, _ = _load()
50
+ preds = model.predict(preprocess(face_gray), verbose=0)
51
+ return config.FACE_EMOTIONS[int(np.argmax(preds[0]))]
52
+
53
+
54
+ # Map raw emotions to the app's affective signal (NOT a diagnosis).
55
+ _SIGNAL = {"fear": "Anxious", "sad": "Depressed"}
56
+
57
+
58
+ def emotion_to_signal(emotion):
59
+ return _SIGNAL.get(emotion, "Normal")
60
+
61
+
62
+ def classify_frame(image_bytes):
63
+ """Detect faces in an encoded image (e.g. a browser JPEG) and classify each.
64
+
65
+ Returns a list of {"box": [x, y, w, h], "emotion": str, "signal": str}.
66
+ Used by the browser-capture API so no server-side camera is required.
67
+ """
68
+ import cv2
69
+
70
+ _, cascade = _load()
71
+ arr = np.frombuffer(image_bytes, np.uint8)
72
+ img = cv2.imdecode(arr, cv2.IMREAD_COLOR)
73
+ if img is None:
74
+ return []
75
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
76
+ results = []
77
+ for (x, y, w, h) in cascade.detectMultiScale(gray, 1.32, 5):
78
+ emotion = classify_face(gray[y:y + h, x:x + w])
79
+ results.append({
80
+ "box": [int(x), int(y), int(w), int(h)],
81
+ "emotion": emotion,
82
+ "signal": emotion_to_signal(emotion),
83
+ })
84
+ return results
85
+
86
+
87
+ def _verdict(counts):
88
+ """Map tallied emotions to a dominant affective signal."""
89
+ signal = {
90
+ "Anxious": counts.get("fear", 0),
91
+ "Depressed": counts.get("sad", 0),
92
+ "Normal": sum(v for k, v in counts.items() if k not in ("fear", "sad")),
93
+ }
94
+ return max(signal, key=signal.get)
95
+
96
+
97
+ def detect_emotion(camera_index=0):
98
+ """Open the webcam, classify faces until 'q' is pressed, return the verdict.
99
+
100
+ NOTE: opens a native OpenCV window; requires a local display + webcam.
101
+ """
102
+ import cv2
103
+
104
+ model, cascade = _load()
105
+ cap = cv2.VideoCapture(camera_index)
106
+ if not cap.isOpened():
107
+ raise RuntimeError("Could not open the webcam (camera index %s)." % camera_index)
108
+
109
+ counts = {}
110
+ try:
111
+ while True:
112
+ ok, frame = cap.read()
113
+ if not ok:
114
+ continue
115
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
116
+ for (x, y, w, h) in cascade.detectMultiScale(gray, 1.32, 5):
117
+ cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 3)
118
+ emotion = classify_face(gray[y:y + h, x:x + w])
119
+ counts[emotion] = counts.get(emotion, 0) + 1
120
+ cv2.putText(frame, emotion, (x, y), cv2.FONT_HERSHEY_SIMPLEX,
121
+ 1, (0, 0, 255), 2)
122
+ cv2.imshow("Facial emotion analysis", cv2.resize(frame, (1000, 700)))
123
+ cv2.setWindowProperty("Facial emotion analysis", cv2.WND_PROP_TOPMOST, 1)
124
+ if cv2.waitKey(10) == ord("q"):
125
+ break
126
+ finally:
127
+ cap.release()
128
+ cv2.destroyAllWindows()
129
+
130
+ verdict = _verdict(counts)
131
+ logger.info("Face session counts=%s -> %s", counts, verdict)
132
+ return verdict
anxiety_app/services/recorder.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Microphone recording helper (server-side capture via PyAudio)."""
2
+ import logging
3
+ import wave
4
+
5
+ from .. import config
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ def record_to_wav(path=None, seconds=None):
11
+ """Record a fixed-length clip from the default microphone to a WAV file.
12
+
13
+ NOTE: captures on the machine running the server; requires a local mic.
14
+ """
15
+ import pyaudio
16
+
17
+ path = str(path or config.RECORDING_PATH)
18
+ seconds = seconds or config.RECORD_SECONDS
19
+ fmt = pyaudio.paInt16
20
+
21
+ audio = pyaudio.PyAudio()
22
+ stream = audio.open(format=fmt, channels=config.CHANNELS, rate=config.SAMPLE_RATE,
23
+ input=True, frames_per_buffer=config.CHUNK)
24
+ logger.info("Recording %ss to %s", seconds, path)
25
+ frames = [stream.read(config.CHUNK)
26
+ for _ in range(int(config.SAMPLE_RATE / config.CHUNK * seconds))]
27
+ stream.stop_stream()
28
+ stream.close()
29
+ audio.terminate()
30
+
31
+ with wave.open(path, "wb") as wf:
32
+ wf.setnchannels(config.CHANNELS)
33
+ wf.setsampwidth(audio.get_sample_size(fmt))
34
+ wf.setframerate(config.SAMPLE_RATE)
35
+ wf.writeframes(b"".join(frames))
36
+ return path
anxiety_app/services/voice_emotion.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Speech emotion recognition from a recorded WAV file.
2
+
3
+ Loads the RAVDESS-trained 1-D CNN once (lazily) and predicts one of 10
4
+ gender x emotion classes from MFCC features. Gender is part of this single
5
+ model -- there is no separate gender network.
6
+ """
7
+ import logging
8
+
9
+ import numpy as np
10
+
11
+ from .. import config
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ _model = None
16
+
17
+
18
+ def _load():
19
+ global _model
20
+ if _model is None:
21
+ from tf_keras.models import model_from_json
22
+
23
+ logger.info("Loading voice model from %s", config.VOICE_MODEL_JSON)
24
+ _model = model_from_json(config.VOICE_MODEL_JSON.read_text())
25
+ _model.load_weights(str(config.VOICE_MODEL_WEIGHTS))
26
+ return _model
27
+
28
+
29
+ def extract_features(wav_path):
30
+ """Return the mean MFCC feature vector the model expects."""
31
+ import librosa
32
+
33
+ X, sample_rate = librosa.load(str(wav_path), res_type="kaiser_fast",
34
+ duration=2.5, sr=22050 * 2, offset=0.5)
35
+ mfccs = np.mean(librosa.feature.mfcc(y=X, sr=sample_rate, n_mfcc=13), axis=0)
36
+ return np.expand_dims(np.expand_dims(mfccs, axis=0), axis=2)
37
+
38
+
39
+ def analyze(wav_path=None):
40
+ """Classify a recording and return its gender+emotion label."""
41
+ wav_path = wav_path or config.RECORDING_PATH
42
+ model = _load()
43
+ preds = model.predict(extract_features(wav_path), batch_size=32, verbose=0)
44
+ label = config.VOICE_CLASSES[int(preds.argmax(axis=1)[0])]
45
+ logger.info("Voice prediction for %s -> %s", wav_path, label)
46
+ return label
47
+
48
+
49
+ def analyze_upload(file_storage):
50
+ """Classify an uploaded audio blob (e.g. browser MediaRecorder webm/wav).
51
+
52
+ librosa decodes via soundfile (wav) or audioread/ffmpeg (webm/ogg), so the
53
+ container ships ffmpeg. Returns the gender+emotion label.
54
+ """
55
+ import os
56
+ import tempfile
57
+
58
+ suffix = os.path.splitext(file_storage.filename or "")[1] or ".webm"
59
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=suffix)
60
+ try:
61
+ file_storage.save(tmp.name)
62
+ tmp.close()
63
+ return analyze(tmp.name)
64
+ finally:
65
+ os.unlink(tmp.name)
anxiety_app/static/Presentation2.jpg ADDED
anxiety_app/static/bgm.svg ADDED
anxiety_app/static/face-detection.svg ADDED
anxiety_app/static/face.js ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Browser webcam capture -> POST frames to /api/face -> draw boxes + tally signals.
2
+ (function () {
3
+ const video = document.getElementById("video");
4
+ const overlay = document.getElementById("overlay");
5
+ const ctx = overlay.getContext("2d");
6
+ const startBtn = document.getElementById("startBtn");
7
+ const stopBtn = document.getElementById("stopBtn");
8
+ const statusEl = document.getElementById("status");
9
+ const resultEl = document.getElementById("result");
10
+
11
+ const capture = document.createElement("canvas");
12
+ const counts = { Anxious: 0, Depressed: 0, Normal: 0 };
13
+ let stream = null, timer = null, busy = false;
14
+
15
+ startBtn.onclick = async () => {
16
+ try {
17
+ stream = await navigator.mediaDevices.getUserMedia({ video: true });
18
+ video.srcObject = stream;
19
+ Object.keys(counts).forEach((k) => (counts[k] = 0));
20
+ resultEl.textContent = "";
21
+ startBtn.disabled = true;
22
+ stopBtn.disabled = false;
23
+ statusEl.textContent = "Detecting… keep your face in view.";
24
+ timer = setInterval(tick, 500);
25
+ } catch (e) {
26
+ statusEl.textContent = "Could not access webcam: " + e.message;
27
+ }
28
+ };
29
+
30
+ stopBtn.onclick = () => {
31
+ if (timer) clearInterval(timer);
32
+ timer = null;
33
+ if (stream) stream.getTracks().forEach((t) => t.stop());
34
+ startBtn.disabled = false;
35
+ stopBtn.disabled = true;
36
+ ctx.clearRect(0, 0, overlay.width, overlay.height);
37
+ const total = counts.Anxious + counts.Depressed + counts.Normal;
38
+ const verdict = Object.keys(counts).reduce((a, b) => (counts[a] >= counts[b] ? a : b));
39
+ resultEl.textContent = total ? "Result: " + verdict : "No face detected — try again.";
40
+ statusEl.textContent = "Stopped.";
41
+ };
42
+
43
+ async function tick() {
44
+ if (busy || video.readyState < 2) return;
45
+ busy = true;
46
+ capture.width = video.videoWidth;
47
+ capture.height = video.videoHeight;
48
+ capture.getContext("2d").drawImage(video, 0, 0);
49
+ try {
50
+ const blob = await new Promise((r) => capture.toBlob(r, "image/jpeg", 0.7));
51
+ const fd = new FormData();
52
+ fd.append("frame", blob, "frame.jpg");
53
+ const res = await fetch("/api/face", { method: "POST", body: fd });
54
+ const data = await res.json();
55
+ draw(data.faces || []);
56
+ } catch (e) {
57
+ /* drop the occasional failed frame */
58
+ }
59
+ busy = false;
60
+ }
61
+
62
+ function draw(faces) {
63
+ const sx = overlay.width / (video.videoWidth || overlay.width);
64
+ const sy = overlay.height / (video.videoHeight || overlay.height);
65
+ ctx.clearRect(0, 0, overlay.width, overlay.height);
66
+ ctx.lineWidth = 2;
67
+ ctx.strokeStyle = "#00b3ff";
68
+ ctx.fillStyle = "#00b3ff";
69
+ ctx.font = "18px sans-serif";
70
+ faces.forEach((f) => {
71
+ const [x, y, w, h] = f.box;
72
+ ctx.strokeRect(x * sx, y * sy, w * sx, h * sy);
73
+ const label = f.emotion + " (" + f.signal + ")";
74
+ ctx.fillText(label, x * sx, y * sy > 20 ? y * sy - 6 : y * sy + 16);
75
+ if (counts[f.signal] !== undefined) counts[f.signal]++;
76
+ });
77
+ }
78
+ })();
anxiety_app/static/index.css ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body{
2
+ background: #172326;
3
+
4
+ }
5
+ ul {
6
+ list-style-type: none;
7
+ margin: 0;
8
+ padding: 0;
9
+ overflow: hidden;
10
+ background-color: #333;
11
+ }
12
+
13
+ li {
14
+ float: left;
15
+ }
16
+
17
+ li a {
18
+ display: block;
19
+ color: white;
20
+ text-align: center;
21
+ padding: 14px 16px;
22
+ text-decoration: none;
23
+ }
24
+
25
+ li a:hover {
26
+ background-color: #111;
27
+ }
28
+
29
+ h2{
30
+ position: absolute;
31
+ left: 360px;
32
+ top: 300px;
33
+ font-family: Roboto;
34
+ font-style: normal;
35
+ font-weight: 900;
36
+ font-size: 55px;
37
+ line-height: 75px;
38
+ text-align: center;
39
+ color: #fffdfd;
40
+ }
41
+
anxiety_app/static/microphone.svg ADDED
anxiety_app/static/styles.css ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body{
2
+ background: #172326;
3
+
4
+ }
5
+
6
+ img{
7
+ position: absolute;
8
+ width: 486px;
9
+ height: 426px;
10
+ left: 94px;
11
+ top: 130px;
12
+ }
13
+
14
+ h2{
15
+ position: absolute;
16
+ left: 730px;
17
+ top: 130px;
18
+ font-family: Roboto;
19
+ font-style: normal;
20
+ font-weight: 900;
21
+ font-size: 55px;
22
+ line-height: 75px;
23
+ text-align: center;
24
+ color: #fffdfd;
25
+ }
26
+
27
+ .detect_btn {
28
+ position: absolute;
29
+ width: 410px;
30
+ height: 90px;
31
+ left: 790px;
32
+ top: 400px;
33
+ background: #474AF0;
34
+ border-radius: 66px;
35
+ font-family: Roboto;
36
+ font-style: normal;
37
+ font-weight: 900;
38
+ font-size: 48px;
39
+ line-height: 56px;
40
+ text-align: center;
41
+ color: #E5E5E5;
42
+ }
43
+
44
+ .icon{
45
+
46
+ position: absolute;
47
+ width: 54px;
48
+ height: 54px;
49
+ left: 834px;
50
+ z-index: 1;
51
+ top: 415px;
52
+
53
+
54
+ }
55
+
56
+ p {
57
+ position: absolute;
58
+ width: 154px;
59
+ height: 28px;
60
+ left: 910px;
61
+ top: 520px;
62
+
63
+ font-family: Roboto;
64
+ font-style: normal;
65
+ font-weight: normal;
66
+ font-size: 20px;
67
+ line-height: 28px;
68
+
69
+ color: #FFFFFF;
70
+
71
+ }
72
+
73
+ .voice_detect_btn {
74
+ position: absolute;
75
+ width: 410px;
76
+ height: 90px;
77
+ left: 790px;
78
+ top: 400px;
79
+ background: #F04747;
80
+ border-radius: 66px;
81
+ font-family: Roboto;
82
+ font-style: normal;
83
+ font-weight: 900;
84
+ font-size: 48px;
85
+ line-height: 56px;
86
+ text-align: center;
87
+ color: #E5E5E5;
88
+ }
89
+
90
+ a:hover {
91
+ background-color:#F04747;
92
+ }
anxiety_app/static/voice.js ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Browser mic capture (MediaRecorder) -> POST blob to /api/voice -> show label.
2
+ (function () {
3
+ const recBtn = document.getElementById("recBtn");
4
+ const statusEl = document.getElementById("vstatus");
5
+ const resultEl = document.getElementById("vresult");
6
+ const player = document.getElementById("player");
7
+ const SECONDS = 4;
8
+
9
+ recBtn.onclick = async () => {
10
+ let stream;
11
+ try {
12
+ stream = await navigator.mediaDevices.getUserMedia({ audio: true });
13
+ } catch (e) {
14
+ statusEl.textContent = "Could not access microphone: " + e.message;
15
+ return;
16
+ }
17
+ const chunks = [];
18
+ const recorder = new MediaRecorder(stream);
19
+ recorder.ondataavailable = (e) => e.data.size && chunks.push(e.data);
20
+ recorder.onstop = async () => {
21
+ stream.getTracks().forEach((t) => t.stop());
22
+ const type = recorder.mimeType || "audio/webm";
23
+ const blob = new Blob(chunks, { type });
24
+ player.src = URL.createObjectURL(blob);
25
+ player.style.display = "block";
26
+ statusEl.textContent = "Analyzing…";
27
+ const ext = type.includes("ogg") ? "ogg" : "webm";
28
+ const fd = new FormData();
29
+ fd.append("audio", blob, "recording." + ext);
30
+ try {
31
+ const res = await fetch("/api/voice", { method: "POST", body: fd });
32
+ const data = await res.json();
33
+ resultEl.textContent = data.label ? "Result: " + data.label
34
+ : "Error: " + (data.error || "unknown");
35
+ statusEl.textContent = "Done.";
36
+ } catch (e) {
37
+ statusEl.textContent = "Request failed: " + e.message;
38
+ }
39
+ recBtn.disabled = false;
40
+ };
41
+
42
+ recBtn.disabled = true;
43
+ resultEl.textContent = "";
44
+ statusEl.textContent = "Recording " + SECONDS + "s…";
45
+ recorder.start();
46
+ setTimeout(() => recorder.state !== "inactive" && recorder.stop(), SECONDS * 1000);
47
+ };
48
+ })();
anxiety_app/templates/face.html ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "bootstrap/base.html" %}
2
+ {% block styles %}
3
+ {{super()}}
4
+ <link rel="stylesheet" href="{{url_for('static', filename='styles.css')}}">
5
+ <link rel="preconnect" href="https://fonts.gstatic.com">
6
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@900&display=swap" rel="stylesheet">
7
+ {% endblock %}
8
+
9
+ {% block content %}
10
+ <ul style="list-style-type: none; margin: 0; padding: 0; overflow: hidden; background-color: #333;">
11
+ <li style="float: left;"><a class="active" href="home" style="display: block; color: white;padding: 14px 16px;">Home</a></li>
12
+ <li style="float: left;"><a href="face" style="display: block; color: white;padding: 14px 16px;">Face</a></li>
13
+ <li style="float: left;"><a href="voice" style="display: block; color: white;padding: 14px 16px;">Voice</a></li>
14
+ <li style="float: left;"><a href="qstn" style="display: block; color: white;padding: 14px 16px;">PHQ-9</a></li>
15
+ </ul>
16
+
17
+ <h2 class="heading">Facial Emotion Detection</h2>
18
+
19
+ <div style="text-align:center;">
20
+ <div style="position:relative; display:inline-block;">
21
+ <video id="video" autoplay playsinline muted width="640" height="480" style="background:#000;border-radius:8px;"></video>
22
+ <canvas id="overlay" width="640" height="480" style="position:absolute; left:0; top:0;"></canvas>
23
+ </div>
24
+ <div style="margin:14px;">
25
+ <button id="startBtn" class="detect_btn">Start camera</button>
26
+ <button id="stopBtn" class="detect_btn" disabled>Stop &amp; get result</button>
27
+ </div>
28
+ <p id="status">Click &ldquo;Start camera&rdquo; and allow webcam access.</p>
29
+ <h3 id="result"></h3>
30
+ <p style="color:#888; max-width:640px; margin:10px auto; font-size:0.9em;">
31
+ Emotion signals are a demo cue, <strong>not a diagnosis</strong>. For screening use the PHQ-9.
32
+ </p>
33
+ </div>
34
+
35
+ <script src="{{url_for('static', filename='face.js')}}"></script>
36
+ {% endblock %}
anxiety_app/templates/index.html ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "bootstrap/base.html" %}
2
+
3
+ {% block styles %}
4
+ {{super()}}
5
+ <link rel="stylesheet" href="{{url_for('static', filename='index.css')}}">
6
+ <link rel="preconnect" href="https://fonts.gstatic.com">
7
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@900&display=swap" rel="stylesheet">
8
+ {% endblock %}
9
+
10
+ {% block content%}
11
+ <ul>
12
+ <li><a class="active" href="#home">Home</a></li>
13
+ <li><a href="face">Face</a></li>
14
+ <li><a href="voice">Voice</a></li>
15
+ </ul>
16
+ <h2>Anxiety and Depression Detection</h2>
17
+
18
+
19
+
20
+ {% endblock %}
anxiety_app/templates/phq9.html ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "bootstrap/base.html" %}
2
+ {% block styles %}
3
+ {{super()}}
4
+ <link rel="stylesheet" href="{{url_for('static', filename='styles.css')}}">
5
+ <link rel="preconnect" href="https://fonts.gstatic.com">
6
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@900&display=swap" rel="stylesheet">
7
+ {% endblock %}
8
+
9
+ {% block content%}
10
+ <ul style="list-style-type: none; margin: 0; padding: 0; overflow: hidden; background-color: #333;">
11
+ <li style="float: left;"><a class="active" href="home" style="display: block; color: white;padding: 14px 16px;">Home</a></li>
12
+ <li style="float: left;"><a href="face" style="display: block; color: white;padding: 14px 16px;">Face</a></li>
13
+ <li style="float: left;"><a href="voice" style="display: block; color: white;padding: 14px 16px;">Voice</a></li>
14
+ </ul>
15
+ <img src="{{url_for('static',filename = 'Presentation2.jpg')}}">
16
+ <h2 class="heading">{{data}}</br></h2>
17
+ <img class="icon" src="{{url_for('static',filename = 'face-detection.svg')}}">
18
+ <a href="expression"><button class="detect_btn">Detect</button></a>
19
+ <p>Press q to quit</p>
20
+
21
+
22
+
23
+ {% endblock %}
anxiety_app/templates/voice.html ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "bootstrap/base.html" %}
2
+ {% block styles %}
3
+ {{super()}}
4
+ <link rel="stylesheet" href="{{url_for('static', filename='styles.css')}}">
5
+ <link rel="preconnect" href="https://fonts.gstatic.com">
6
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@900&display=swap" rel="stylesheet">
7
+ {% endblock %}
8
+
9
+ {% block content %}
10
+ <ul style="list-style-type: none; margin: 0; padding: 0; overflow: hidden; background-color: #333;">
11
+ <li style="float: left;"><a class="active" href="home" style="display: block; color: white;padding: 14px 16px;">Home</a></li>
12
+ <li style="float: left;"><a href="face" style="display: block; color: white;padding: 14px 16px;">Face</a></li>
13
+ <li style="float: left;"><a href="voice" style="display: block; color: white;padding: 14px 16px;">Voice</a></li>
14
+ <li style="float: left;"><a href="qstn" style="display: block; color: white;padding: 14px 16px;">PHQ-9</a></li>
15
+ </ul>
16
+
17
+ <h2 class="heading">Voice Emotion Detection</h2>
18
+
19
+ <div style="text-align:center;">
20
+ <img class="icon" src="{{url_for('static', filename='microphone.svg')}}" alt="microphone" style="width:90px;">
21
+ <div style="margin:14px;">
22
+ <button id="recBtn" class="voice_detect_btn">Record 4s</button>
23
+ </div>
24
+ <p id="vstatus">{{data}}</p>
25
+ <h3 id="vresult"></h3>
26
+ <audio id="player" controls style="display:none; margin:10px auto;"></audio>
27
+ <p style="color:#888; max-width:640px; margin:10px auto; font-size:0.9em;">
28
+ Predicts gender + emotion from your voice. A demo cue, <strong>not a diagnosis</strong>.
29
+ </p>
30
+ </div>
31
+
32
+ <script src="{{url_for('static', filename='voice.js')}}"></script>
33
+ {% endblock %}
app.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ """Entry point: create the Flask app and run the development server.
2
+
3
+ python app.py # or: flask --app app run
4
+ """
5
+ from anxiety_app import create_app
6
+
7
+ app = create_app()
8
+
9
+ if __name__ == "__main__":
10
+ app.run(host="127.0.0.1", port=5000, debug=False)
models/fer.h5 ADDED
Binary file (132 Bytes). View file
 
models/fer.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"class_name": "Sequential", "config": {"name": "sequential", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": [null, 48, 48, 1], "dtype": "float32", "sparse": false, "ragged": false, "name": "conv2d_input"}}, {"class_name": "Conv2D", "config": {"name": "conv2d", "trainable": true, "batch_input_shape": [null, 48, 48, 1], "dtype": "float32", "filters": 64, "kernel_size": [3, 3], "strides": [1, 1], "padding": "valid", "data_format": "channels_last", "dilation_rate": [1, 1], "groups": 1, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Conv2D", "config": {"name": "conv2d_1", "trainable": true, "dtype": "float32", "filters": 64, "kernel_size": [3, 3], "strides": [1, 1], "padding": "valid", "data_format": "channels_last", "dilation_rate": [1, 1], "groups": 1, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "MaxPooling2D", "config": {"name": "max_pooling2d", "trainable": true, "dtype": "float32", "pool_size": [2, 2], "padding": "valid", "strides": [2, 2], "data_format": "channels_last"}}, {"class_name": "Dropout", "config": {"name": "dropout", "trainable": true, "dtype": "float32", "rate": 0.5, "noise_shape": null, "seed": null}}, {"class_name": "Conv2D", "config": {"name": "conv2d_2", "trainable": true, "dtype": "float32", "filters": 64, "kernel_size": [3, 3], "strides": [1, 1], "padding": "valid", "data_format": "channels_last", "dilation_rate": [1, 1], "groups": 1, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Conv2D", "config": {"name": "conv2d_3", "trainable": true, "dtype": "float32", "filters": 64, "kernel_size": [3, 3], "strides": [1, 1], "padding": "valid", "data_format": "channels_last", "dilation_rate": [1, 1], "groups": 1, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "MaxPooling2D", "config": {"name": "max_pooling2d_1", "trainable": true, "dtype": "float32", "pool_size": [2, 2], "padding": "valid", "strides": [2, 2], "data_format": "channels_last"}}, {"class_name": "Dropout", "config": {"name": "dropout_1", "trainable": true, "dtype": "float32", "rate": 0.5, "noise_shape": null, "seed": null}}, {"class_name": "Conv2D", "config": {"name": "conv2d_4", "trainable": true, "dtype": "float32", "filters": 128, "kernel_size": [3, 3], "strides": [1, 1], "padding": "valid", "data_format": "channels_last", "dilation_rate": [1, 1], "groups": 1, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Conv2D", "config": {"name": "conv2d_5", "trainable": true, "dtype": "float32", "filters": 128, "kernel_size": [3, 3], "strides": [1, 1], "padding": "valid", "data_format": "channels_last", "dilation_rate": [1, 1], "groups": 1, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "MaxPooling2D", "config": {"name": "max_pooling2d_2", "trainable": true, "dtype": "float32", "pool_size": [2, 2], "padding": "valid", "strides": [2, 2], "data_format": "channels_last"}}, {"class_name": "Flatten", "config": {"name": "flatten", "trainable": true, "dtype": "float32", "data_format": "channels_last"}}, {"class_name": "Dense", "config": {"name": "dense", "trainable": true, "dtype": "float32", "units": 1024, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Dropout", "config": {"name": "dropout_2", "trainable": true, "dtype": "float32", "rate": 0.2, "noise_shape": null, "seed": null}}, {"class_name": "Dense", "config": {"name": "dense_1", "trainable": true, "dtype": "float32", "units": 1024, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Dropout", "config": {"name": "dropout_3", "trainable": true, "dtype": "float32", "rate": 0.2, "noise_shape": null, "seed": null}}, {"class_name": "Dense", "config": {"name": "dense_2", "trainable": true, "dtype": "float32", "units": 7, "activation": "softmax", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}]}, "keras_version": "2.5.0", "backend": "tensorflow"}
models/haarcascade_frontalface_default.xml ADDED
Binary file (963 kB). View file
 
models/voice_model.h5 ADDED
Binary file (132 Bytes). View file
 
models/voice_model.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"class_name": "Sequential", "config": [{"class_name": "Conv1D", "config": {"name": "conv1d_7", "trainable": true, "batch_input_shape": [null, 216, 1], "dtype": "float32", "filters": 128, "kernel_size": [5], "strides": [1], "padding": "same", "dilation_rate": [1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Activation", "config": {"name": "activation_8", "trainable": true, "activation": "relu"}}, {"class_name": "Conv1D", "config": {"name": "conv1d_8", "trainable": true, "filters": 128, "kernel_size": [5], "strides": [1], "padding": "same", "dilation_rate": [1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Activation", "config": {"name": "activation_9", "trainable": true, "activation": "relu"}}, {"class_name": "Dropout", "config": {"name": "dropout_3", "trainable": true, "rate": 0.1}}, {"class_name": "MaxPooling1D", "config": {"name": "max_pooling1d_2", "trainable": true, "strides": [8], "pool_size": [8], "padding": "valid"}}, {"class_name": "Conv1D", "config": {"name": "conv1d_9", "trainable": true, "filters": 128, "kernel_size": [5], "strides": [1], "padding": "same", "dilation_rate": [1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Activation", "config": {"name": "activation_10", "trainable": true, "activation": "relu"}}, {"class_name": "Conv1D", "config": {"name": "conv1d_10", "trainable": true, "filters": 128, "kernel_size": [5], "strides": [1], "padding": "same", "dilation_rate": [1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Activation", "config": {"name": "activation_11", "trainable": true, "activation": "relu"}}, {"class_name": "Conv1D", "config": {"name": "conv1d_11", "trainable": true, "filters": 128, "kernel_size": [5], "strides": [1], "padding": "same", "dilation_rate": [1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Activation", "config": {"name": "activation_12", "trainable": true, "activation": "relu"}}, {"class_name": "Dropout", "config": {"name": "dropout_4", "trainable": true, "rate": 0.2}}, {"class_name": "Conv1D", "config": {"name": "conv1d_12", "trainable": true, "filters": 128, "kernel_size": [5], "strides": [1], "padding": "same", "dilation_rate": [1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Activation", "config": {"name": "activation_13", "trainable": true, "activation": "relu"}}, {"class_name": "Flatten", "config": {"name": "flatten_2", "trainable": true}}, {"class_name": "Dense", "config": {"name": "dense_2", "trainable": true, "units": 10, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Activation", "config": {"name": "activation_14", "trainable": true, "activation": "softmax"}}], "keras_version": "2.0.6", "backend": "tensorflow"}
requirements-modern.txt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Modernized dependency set (Option C) — Python 3.13
2
+ # Uses current TensorFlow + the legacy `tf-keras` package so the original
3
+ # Keras 2.0.6 model files (fer.json/h5, model.json + .h5) load unchanged.
4
+ #
5
+ # Setup:
6
+ # py -3.13 -m venv venv
7
+ # venv\Scripts\activate
8
+ # pip install -r requirements-modern.txt
9
+ # python app.py # or: flask run
10
+ #
11
+ # PyAudio installs from PyPI wheels on 3.13 (the bundled cp39 .whl files are not used).
12
+
13
+ tensorflow==2.21.0
14
+ tf-keras==2.21.0
15
+ opencv-python
16
+ librosa
17
+ pandas
18
+ scikit-learn
19
+ soundfile
20
+ flask
21
+ flask-bootstrap
22
+ pyaudio
23
+ gunicorn