kalpit sharma commited on
Commit
5b6acdd
·
1 Parent(s): fca7bd8

adding changes

Browse files
.gitignore ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+ train/
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116
+ .pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121
+ __pypackages__/
122
+
123
+ # Celery stuff
124
+ celerybeat-schedule
125
+ celerybeat.pid
126
+
127
+ # SageMath parsed files
128
+ *.sage.py
129
+
130
+ # Environments
131
+ .env
132
+ .venv
133
+ env/
134
+ venv/
135
+ ENV/
136
+ env.bak/
137
+ venv.bak/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # PyCharm
164
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
167
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168
+ #.idea/
169
+
170
+ # Ruff stuff:
171
+ .ruff_cache/
172
+
173
+ # PyPI configuration file
174
+ .pypirc
README.md ADDED
@@ -0,0 +1 @@
 
 
1
+ # face-emotion-detection
app.py ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify , render_template , Response
2
+ from flask_cors import CORS
3
+ from models.clip_emotion import detect_emotion , detect_age
4
+ import cv2
5
+ from camera import VideoCamera
6
+ import os
7
+ from cnn_emotion import detect_emotion as detect_emotion_cnn
8
+ # from cnn_lstm import detect_cnn_lstm_emotion
9
+ from cnn_resnet import detect_cnn_resnetemotion
10
+
11
+ from cnn import detect_cnn
12
+ from knn import detect_knn
13
+ from svm import detect_svm
14
+ from randomforest import detect_rf
15
+ from logisticregression import detect_lr
16
+
17
+ import logging
18
+
19
+ # Configure logging
20
+ logging.basicConfig(
21
+ level=logging.DEBUG, # Change to INFO in production
22
+ format='[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
23
+ )
24
+
25
+
26
+ app = Flask(__name__)
27
+ CORS(app)
28
+
29
+ UPLOAD_FOLDER = "temp_frames"
30
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
31
+
32
+ @app.route('/')
33
+ def home():
34
+ return render_template('index.html')
35
+
36
+ @app.route('/knn')
37
+ def index_knn():
38
+ return render_template('knn.html')
39
+
40
+ @app.route('/svm')
41
+ def index_svm():
42
+ return render_template('svm.html')
43
+
44
+ @app.route('/logistic_regression')
45
+ def index_lr():
46
+ return render_template('lr.html')
47
+
48
+ @app.route('/randomforest')
49
+ def index_rf():
50
+ return render_template('rf.html')
51
+
52
+ @app.route('/vit')
53
+ def index_vit():
54
+ return render_template('vit.html')
55
+
56
+ @app.route('/cnn')
57
+ def index_cnnonly():
58
+ return render_template('cnn.html')
59
+
60
+ @app.route('/cnnkeras')
61
+ def index_cnn():
62
+ return render_template('cnnkeras.html')
63
+
64
+ @app.route('/cnnlstm')
65
+ def index_cnnlstm():
66
+ return render_template('cnn_lstm.html')
67
+
68
+ @app.route('/cnn_resnet')
69
+ def index_cnn_resnet():
70
+ return render_template('cnn_resnet.html')
71
+
72
+ def gen(camera):
73
+ while True:
74
+ frame = camera.get_frame()
75
+ yield (b'--frame\r\n'
76
+ b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
77
+
78
+ @app.route('/video_feed', methods=["POST"])
79
+ def video_feed():
80
+ # return Response(gen(VideoCamera()),
81
+ # mimetype='multipart/x-mixed-replace; boundary=frame')
82
+
83
+ if "frame" not in request.files:
84
+ return jsonify({"error": "No frame received"}), 400
85
+
86
+ file = request.files["frame"]
87
+ filepath = os.path.join(UPLOAD_FOLDER, file.filename)
88
+ file.save(filepath)
89
+ try:
90
+ emotion, image_base64 = detect_emotion_cnn(filepath)
91
+ return jsonify({"emotion": emotion, "image": image_base64})
92
+ except Exception as e:
93
+ return jsonify({"error": str(e)}), 500
94
+
95
+ @app.route("/analyze", methods=["POST"])
96
+ def analyze():
97
+ if "frame" not in request.files:
98
+ return jsonify({"error": "No frame received"}), 400
99
+
100
+ file = request.files["frame"]
101
+ filepath = os.path.join(UPLOAD_FOLDER, file.filename)
102
+ file.save(filepath)
103
+
104
+ try:
105
+ emotion, scores = detect_emotion(filepath)
106
+ age, age_scores = detect_age(filepath)
107
+ return jsonify({"emotion": emotion, "scores": scores, "age": age})
108
+ except Exception as e:
109
+ return jsonify({"error": str(e)}), 500
110
+
111
+ @app.route('/cnn_lstm_video_feed', methods=["POST"])
112
+ def cnn_lstm_video_feed():
113
+
114
+ if "frame" not in request.files:
115
+ return jsonify({"error": "No frame received"}), 400
116
+
117
+ file = request.files["frame"]
118
+ filepath = os.path.join(UPLOAD_FOLDER, file.filename)
119
+ file.save(filepath)
120
+ try:
121
+ emotion, image_base64 = detect_emotion_cnn(filepath)
122
+ return jsonify({"emotion": emotion, "image": image_base64})
123
+ except Exception as e:
124
+ return jsonify({"error": str(e)}), 500
125
+
126
+ @app.route('/cnn_resnet', methods=['POST'])
127
+ def cnn_resnet():
128
+ if "frame" not in request.files:
129
+ logging.warning("No frame in request")
130
+ return jsonify({"error": "No frame received"}), 400
131
+ file = request.files["frame"]
132
+ filepath = os.path.join(UPLOAD_FOLDER, file.filename)
133
+ logging.info(f"File saved to {filepath}")
134
+ file.save(filepath)
135
+ try:
136
+ emotion, image_base64 = detect_cnn_resnetemotion(filepath)
137
+ return jsonify({"emotion": emotion, "image": image_base64})
138
+ except Exception as e:
139
+ logging.error(f"Failed to save file: {e}")
140
+ return jsonify({"error": str(e)}), 500
141
+
142
+ @app.route('/cnn', methods=['POST'])
143
+ def cnn():
144
+ if "frame" not in request.files:
145
+ logging.warning("No frame in request")
146
+ return jsonify({"error": "No frame received"}), 400
147
+ file = request.files["frame"]
148
+ filepath = os.path.join(UPLOAD_FOLDER, file.filename)
149
+ logging.info(f"File saved to {filepath}")
150
+ file.save(filepath)
151
+ try:
152
+ emotion, image_base64 = detect_cnn(filepath)
153
+ return jsonify({"emotion": emotion, "image": image_base64})
154
+ except Exception as e:
155
+ logging.error(f"Failed to save file: {e}")
156
+ return jsonify({"error": str(e)}), 500
157
+
158
+ @app.route('/knn', methods=['POST'])
159
+ def knnn():
160
+ if "frame" not in request.files:
161
+ logging.warning("No frame in request")
162
+ return jsonify({"error": "No frame received"}), 400
163
+ file = request.files["frame"]
164
+ filepath = os.path.join(UPLOAD_FOLDER, file.filename)
165
+ logging.info(f"File saved to {filepath}")
166
+ file.save(filepath)
167
+ try:
168
+ emotion, image_base64 = detect_knn(filepath)
169
+ return jsonify({"emotion": emotion, "image": image_base64})
170
+ except Exception as e:
171
+ logging.error(f"Failed to save file: {e}")
172
+ return jsonify({"error": str(e)}), 500
173
+
174
+ @app.route('/svm', methods=['POST'])
175
+ def svmm():
176
+ if "frame" not in request.files:
177
+ logging.warning("No frame in request")
178
+ return jsonify({"error": "No frame received"}), 400
179
+ file = request.files["frame"]
180
+ filepath = os.path.join(UPLOAD_FOLDER, file.filename)
181
+ logging.info(f"File saved to {filepath}")
182
+ file.save(filepath)
183
+ try:
184
+ emotion, image_base64 = detect_svm(filepath)
185
+ return jsonify({"emotion": emotion, "image": image_base64})
186
+ except Exception as e:
187
+ logging.error(f"Failed to save file: {e}")
188
+ return jsonify({"error": str(e)}), 500
189
+
190
+ @app.route('/randomforest', methods=['POST'])
191
+ def rff():
192
+ if "frame" not in request.files:
193
+ logging.warning("No frame in request")
194
+ return jsonify({"error": "No frame received"}), 400
195
+ file = request.files["frame"]
196
+ filepath = os.path.join(UPLOAD_FOLDER, file.filename)
197
+ logging.info(f"File saved to {filepath}")
198
+ file.save(filepath)
199
+ try:
200
+ emotion, image_base64 = detect_rf(filepath)
201
+ return jsonify({"emotion": emotion, "image": image_base64})
202
+ except Exception as e:
203
+ logging.error(f"Failed to save file: {e}")
204
+ return jsonify({"error": str(e)}), 500
205
+
206
+ @app.route('/logistic_regression', methods=['POST'])
207
+ def lr():
208
+ if "frame" not in request.files:
209
+ logging.warning("No frame in request")
210
+ return jsonify({"error": "No frame received"}), 400
211
+ file = request.files["frame"]
212
+ filepath = os.path.join(UPLOAD_FOLDER, file.filename)
213
+ logging.info(f"File saved to {filepath}")
214
+ file.save(filepath)
215
+ try:
216
+ emotion, image_base64 = detect_lr(filepath)
217
+ return jsonify({"emotion": emotion, "image": image_base64})
218
+ except Exception as e:
219
+ logging.error(f"Failed to save file: {e}")
220
+ return jsonify({"error": str(e)}), 500
221
+
222
+ @app.route("/reports")
223
+ def show_reports():
224
+ svm_report = """[RESULTS] SVM Classification Report
225
+ precision recall f1-score support
226
+ angry 0.33 0.35 0.34 779
227
+ disgust 0.56 0.16 0.25 92
228
+ fear 0.33 0.25 0.29 838
229
+ happy 0.59 0.68 0.63 1473
230
+ neutral 0.42 0.44 0.43 987
231
+ sad 0.35 0.33 0.34 977
232
+ surprise 0.57 0.54 0.55 596
233
+ accuracy 0.45 5742
234
+ macro avg 0.45 0.39 0.40 5742
235
+ weighted avg 0.44 0.45 0.44 5742"""
236
+
237
+ rf_report = """[RESULTS] Random Forest Classification Report
238
+ precision recall f1-score support
239
+ angry 0.38 0.20 0.26 779
240
+ disgust 1.00 0.27 0.43 92
241
+ fear 0.39 0.21 0.28 838
242
+ happy 0.47 0.82 0.60 1473
243
+ neutral 0.40 0.43 0.41 987
244
+ sad 0.37 0.31 0.34 977
245
+ surprise 0.71 0.50 0.58 596
246
+ accuracy 0.45 5742
247
+ macro avg 0.53 0.39 0.41 5742
248
+ weighted avg 0.45 0.45 0.42 5742"""
249
+
250
+ knn_report = """[RESULTS] k-NN Classification Report
251
+ precision recall f1-score support
252
+ angry 0.34 0.35 0.35 779
253
+ disgust 0.39 0.36 0.38 92
254
+ fear 0.38 0.31 0.34 838
255
+ happy 0.53 0.75 0.62 1473
256
+ neutral 0.39 0.42 0.40 987
257
+ sad 0.40 0.21 0.28 977
258
+ surprise 0.56 0.47 0.51 596
259
+ accuracy 0.45 5742
260
+ macro avg 0.43 0.41 0.41 5742
261
+ weighted avg 0.44 0.45 0.43 5742"""
262
+
263
+ lr_report = """[RESULTS] Logistic Regression Classification Report
264
+ precision recall f1-score support
265
+ angry 0.33 0.31 0.32 779
266
+ disgust 0.56 0.15 0.24 92
267
+ fear 0.32 0.22 0.26 838
268
+ happy 0.57 0.70 0.63 1473
269
+ neutral 0.41 0.43 0.42 987
270
+ sad 0.34 0.31 0.32 977
271
+ surprise 0.51 0.55 0.53 596
272
+ accuracy 0.44 5742
273
+ macro avg 0.43 0.38 0.39 5742
274
+ weighted avg 0.43 0.44 0.43 5742"""
275
+
276
+ return render_template(
277
+ "classification_reports.html",
278
+ svm_report=svm_report,
279
+ rf_report=rf_report,
280
+ knn_report=knn_report,
281
+ lr_report=lr_report
282
+ )
283
+
284
+
285
+ if __name__ == "__main__":
286
+ app.run(host="0.0.0.0", port=7860)
camera.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ from model import ERModel
3
+ import numpy as np
4
+
5
+ facec = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
6
+ model = ERModel("model.json", "model.weights.h5")
7
+ font = cv2.FONT_HERSHEY_SIMPLEX
8
+
9
+ class VideoCamera(object):
10
+ def __init__(self):
11
+ self.video = cv2.VideoCapture(0)
12
+ # Set camera resolution (smaller size like 320x240)
13
+ # self.video.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
14
+ # self.video.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
15
+
16
+ def __del__(self):
17
+ self.video.release()
18
+
19
+ def get_frame(self):
20
+ _, fr = self.video.read()
21
+ gray_fr = cv2.cvtColor(fr, cv2.COLOR_BGR2GRAY)
22
+ faces = facec.detectMultiScale(gray_fr, 1.3, 5)
23
+
24
+ for (x, y, w, h) in faces:
25
+ fc = gray_fr[y:y+h, x:x+w]
26
+
27
+ roi = cv2.resize(fc, (48, 48))
28
+ pred = model.predict_emotion(roi[np.newaxis, :, :, np.newaxis])
29
+
30
+ cv2.putText(fr, pred, (x, y), font, 1, (255, 255, 0), 2)
31
+ cv2.rectangle(fr,(x,y),(x+w,y+h),(255,0,0),2)
32
+
33
+ _, jpeg = cv2.imencode('.jpg', fr)
34
+ return jpeg.tobytes()
clean_requirements.txt ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ absl-py==2.2.2
2
+ astunparse==1.6.3
3
+ blinker==1.9.0
4
+ cachetools==5.5.2
5
+ certifi==2025.1.31
6
+ charset-normalizer==3.4.1
7
+ click==8.1.8
8
+ clip @ git+https://github.com/openai/CLIP.git@dcba3cb2e2827b402d2701e7e1c7d9fed8a20ef1
9
+ colorama==0.4.6
10
+ contourpy==1.3.1
11
+ cycler==0.12.1
12
+ filelock==3.18.0
13
+ Flask==3.1.0
14
+ flask-cors==5.0.1
15
+ flatbuffers==25.2.10
16
+ fonttools==4.57.0
17
+ fsspec==2025.3.2
18
+ ftfy==6.3.1
19
+ gast==0.4.0
20
+ google-auth==2.38.0
21
+ google-auth-oauthlib==1.0.0
22
+ google-pasta==0.2.0
23
+ grpcio==1.71.0
24
+ h5py==3.13.0
25
+ huggingface-hub==0.30.2
26
+ idna==3.10
27
+ imageio==2.37.0
28
+ itsdangerous==2.2.0
29
+ Jinja2==3.1.6
30
+ joblib==1.4.2
31
+ keras==2.15.0
32
+ kiwisolver==1.4.8
33
+ lazy_loader==0.4
34
+ libclang==18.1.1
35
+ Markdown==3.8
36
+ markdown-it-py==3.0.0
37
+ MarkupSafe==3.0.2
38
+ matplotlib==3.7.2
39
+ mdurl==0.1.2
40
+ ml-dtypes==0.2.0
41
+ mpmath==1.3.0
42
+ namex==0.0.8
43
+ networkx==3.4.2
44
+ numpy==1.26.4
45
+ oauthlib==3.2.2
46
+ opencv-python==4.11.0.86
47
+ opencv-python-headless==4.11.0.86
48
+ opt_einsum==3.4.0
49
+ optree==0.15.0
50
+ packaging==24.2
51
+ pandas==2.2.3
52
+ pillow==11.1.0
53
+ protobuf==4.25.6
54
+ pyasn1==0.6.1
55
+ pyasn1_modules==0.4.2
56
+ Pygments==2.19.1
57
+ pyparsing==3.0.9
58
+ python-dateutil==2.9.0.post0
59
+ pytz==2025.2
60
+ PyYAML==6.0.2
61
+ regex==2024.11.6
62
+ requests==2.31.0
63
+ requests-oauthlib==2.0.0
64
+ rich==14.0.0
65
+ rsa==4.9
66
+ safetensors==0.5.3
67
+ scikit-image==0.25.2
68
+ scikit-learn==1.6.1
69
+ scipy==1.15.2
70
+ seaborn==0.12.2
71
+ six==1.17.0
72
+ sympy==1.13.1
73
+ tensorboard==2.15.2
74
+ tensorboard-data-server==0.7.2
75
+ tensorboard-plugin-wit==1.8.1
76
+ tensorflow==2.15.0
77
+ tensorflow-estimator==2.15.0
78
+ tensorflow-intel==2.15.0
79
+ tensorflow-io-gcs-filesystem==0.31.0
80
+ termcolor==3.0.1
81
+ threadpoolctl==3.6.0
82
+ tifffile==2025.3.30
83
+ tokenizers==0.21.1
84
+ torch==2.1.2
85
+ torchvision==0.16.2
86
+ tqdm==4.67.1
87
+ transformers==4.51.2
88
+ typing_extensions==4.5.0
89
+ tzdata==2025.2
90
+ urllib3==2.4.0
91
+ wcwidth==0.2.13
92
+ Werkzeug==3.1.3
93
+ wrapt==1.14.1
cnn.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import cv2
3
+ import base64
4
+ from tensorflow.keras.models import load_model
5
+ from tensorflow.keras.preprocessing import image
6
+ import logging
7
+
8
+
9
+ # Load model once globally
10
+ model = load_model("emotion_detector_model.h5")
11
+
12
+ # Constants
13
+ IMG_HEIGHT = 48
14
+ IMG_WIDTH = 48
15
+ TIME_STEPS = 6
16
+ CHUNK_SIZE = 8 # So width 48 -> 6 chunks of 8
17
+ CLASS_NAMES = ['Angry', 'Disgust', 'Fear', 'Happy', 'Sad', 'Surprise', 'Neutral'] # Change based on your dataset
18
+
19
+ def preprocess_frame(frame):
20
+ # Convert to grayscale and resize
21
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
22
+ resized = cv2.resize(gray, (IMG_WIDTH, IMG_HEIGHT)) # 48x48
23
+ norm_img = resized / 255.0
24
+
25
+ # Split the width into 6 chunks (each 8 pixels wide)
26
+ chunks = [norm_img[:, i*CHUNK_SIZE:(i+1)*CHUNK_SIZE] for i in range(TIME_STEPS)]
27
+ sequence = np.stack([chunk[..., np.newaxis] for chunk in chunks], axis=0) # (6, 48, 8, 1)
28
+
29
+ return sequence[np.newaxis, ...] # shape: (1, 6, 48, 8, 1)
30
+
31
+
32
+ def detect_cnn(image_path):
33
+ # Read image
34
+ logging.debug(f"Reading image from {image_path}")
35
+ frame = cv2.imread(image_path)
36
+ img = image.load_img(image_path, target_size=(48, 48), color_mode='grayscale')
37
+ img_array = image.img_to_array(img)
38
+ img_array = np.expand_dims(img_array, axis=0) / 255.0 # Normalize
39
+
40
+ # ✅ Predict
41
+ prediction = model.predict(img_array)
42
+ predicted_class = np.argmax(prediction)
43
+
44
+ # ✅ Label map (check your training classes to confirm order)
45
+ class_labels = ['angry', 'disgust', 'fear', 'happy', 'neutral', 'sad', 'surprise']
46
+
47
+ # ✅ Print result
48
+ # Encode image to base64 to send back
49
+ _, buffer = cv2.imencode('.jpg', frame)
50
+ frame_base64 = base64.b64encode(buffer).decode('utf-8')
51
+ emotion=class_labels[predicted_class]
52
+ return emotion,frame_base64
cnn_emotion.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # cnn_emotion.py
2
+ import cv2
3
+ import numpy as np
4
+ import base64
5
+ from model import ERModel
6
+
7
+ facec = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
8
+ model = ERModel("model.json", "model.weights.h5")
9
+ font = cv2.FONT_HERSHEY_SIMPLEX
10
+
11
+ def detect_emotion(image_path):
12
+ fr = cv2.imread(image_path)
13
+ gray_fr = cv2.cvtColor(fr, cv2.COLOR_BGR2GRAY)
14
+ faces = facec.detectMultiScale(gray_fr, 1.3, 5)
15
+
16
+ final_pred = ""
17
+ for (x, y, w, h) in faces:
18
+ fc = gray_fr[y:y+h, x:x+w]
19
+
20
+ roi = cv2.resize(fc, (48, 48))
21
+ pred = model.predict_emotion(roi[np.newaxis, :, :, np.newaxis])
22
+ final_pred = pred
23
+
24
+ cv2.putText(fr, pred, (x, y - 10), font, 0.9, (0, 255, 0), 2)
25
+ cv2.rectangle(fr, (x, y), (x+w, y+h), (0, 255, 0), 2)
26
+
27
+ # Encode image to base64
28
+ _, buffer = cv2.imencode('.jpg', fr)
29
+ frame_base64 = base64.b64encode(buffer).decode('utf-8')
30
+
31
+ return final_pred, frame_base64
cnn_lstm.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import cv2
2
+ # import numpy as np
3
+ # from tensorflow.keras.models import load_model
4
+ # from tensorflow.keras.preprocessing.image import img_to_array
5
+ # import base64
6
+
7
+ # # Load the model once
8
+ # model = load_model("emotion_cnn_lstm.h5")
9
+
10
+ # # Emotion labels
11
+ # emotion_labels = ['Angry', 'Disgust', 'Fear', 'Happy', 'Neutral', 'Sad', 'Surprise']
12
+
13
+ # # Load Haar cascade
14
+ # face_classifier = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
15
+
16
+ # def detect_cnn_lstm_emotion(image_path):
17
+ # frame = cv2.imread(image_path)
18
+ # gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
19
+
20
+ # faces = face_classifier.detectMultiScale(gray, scaleFactor=1.3, minNeighbors=5)
21
+ # print(f"Detected faces: {len(faces)}")
22
+
23
+ # final_pred = "No face detected"
24
+
25
+ # for (x, y, w, h) in faces:
26
+ # roi_gray = gray[y:y + h, x:x + w]
27
+ # roi_gray = cv2.resize(roi_gray, (48, 48))
28
+ # roi = roi_gray.astype("float") / 255.0
29
+ # roi = img_to_array(roi)
30
+ # roi = np.expand_dims(roi, axis=0)
31
+
32
+ # prediction = model.predict(roi)[0]
33
+ # label = emotion_labels[np.argmax(prediction)]
34
+ # final_pred = label
35
+
36
+ # # Draw results on original image
37
+ # cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 255, 255), 2)
38
+ # cv2.putText(frame, label, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
39
+
40
+ # # Encode frame to base64
41
+ # _, buffer = cv2.imencode('.jpg', frame)
42
+ # frame_base64 = base64.b64encode(buffer).decode('utf-8')
43
+
44
+ # return final_pred, frame_base64
cnn_resnet.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import cv2
3
+ import base64
4
+ from tensorflow.keras.models import load_model
5
+ import logging
6
+
7
+
8
+ # Load model once globally
9
+ model = load_model("cnn_rnn_model_from_dir.h5")
10
+
11
+ # Constants
12
+ IMG_HEIGHT = 48
13
+ IMG_WIDTH = 48
14
+ TIME_STEPS = 6
15
+ CHUNK_SIZE = 8 # So width 48 -> 6 chunks of 8
16
+ CLASS_NAMES = ['Angry', 'Disgust', 'Fear', 'Happy', 'Sad', 'Surprise', 'Neutral'] # Change based on your dataset
17
+
18
+ def preprocess_frame(frame):
19
+ # Convert to grayscale and resize
20
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
21
+ resized = cv2.resize(gray, (IMG_WIDTH, IMG_HEIGHT)) # 48x48
22
+ norm_img = resized / 255.0
23
+
24
+ # Split the width into 6 chunks (each 8 pixels wide)
25
+ chunks = [norm_img[:, i*CHUNK_SIZE:(i+1)*CHUNK_SIZE] for i in range(TIME_STEPS)]
26
+ sequence = np.stack([chunk[..., np.newaxis] for chunk in chunks], axis=0) # (6, 48, 8, 1)
27
+
28
+ return sequence[np.newaxis, ...] # shape: (1, 6, 48, 8, 1)
29
+
30
+
31
+ def detect_cnn_resnetemotion(image_path):
32
+ # Read image
33
+ logging.debug(f"Reading image from {image_path}")
34
+ frame = cv2.imread(image_path)
35
+ if frame is None:
36
+ logging.error("Failed to load image.")
37
+ raise ValueError("Could not read image")
38
+
39
+ # Preprocess
40
+ input_data = preprocess_frame(frame)
41
+
42
+ # Predict
43
+ prediction = model.predict(input_data)
44
+ predicted_class = int(np.argmax(prediction))
45
+ emotion = CLASS_NAMES[predicted_class] if predicted_class < len(CLASS_NAMES) else str(predicted_class)
46
+
47
+ # Encode image to base64 to send back
48
+ _, buffer = cv2.imencode('.jpg', frame)
49
+ frame_base64 = base64.b64encode(buffer).decode('utf-8')
50
+
51
+ return emotion, frame_base64
cnn_rnn_model_from_dir.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:34427f14fcc055ce73aef33799c94ffd003ea3aa0baa784ca14fd540a4806a49
3
+ size 1904456
comparison.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import numpy as np
3
+ import torch
4
+ import cv2
5
+ import matplotlib.pyplot as plt
6
+ from sklearn.svm import SVC
7
+ from sklearn.metrics import classification_report, confusion_matrix
8
+ from sklearn.model_selection import train_test_split
9
+ from skimage.feature import hog
10
+ from tensorflow.keras.models import Sequential
11
+ from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
12
+ from tensorflow.keras.utils import to_categorical
13
+ from tensorflow.keras.preprocessing.image import load_img, img_to_array
14
+ from sklearn.preprocessing import LabelEncoder
15
+ from transformers import CLIPProcessor, CLIPModel
16
+ from transformers import ViTForImageClassification, ViTFeatureExtractor
17
+ from skimage.color import rgb2gray
18
+ from skimage.feature import hog
19
+ import numpy as np
20
+ from PIL import Image
21
+ # ----------------------
22
+ # 1. Load and Preprocess Custom Dataset
23
+ # ----------------------
24
+
25
+ def load_custom_dataset(dataset_path, image_size=(48, 48)):
26
+ images = []
27
+ labels = []
28
+ label_map = {}
29
+ label_idx = 0
30
+
31
+ # Loop through the dataset folders (each folder is an emotion class)
32
+ for folder_name in os.listdir(dataset_path):
33
+ folder_path = os.path.join(dataset_path, folder_name)
34
+
35
+ # Ignore non-directories (i.e., files)
36
+ if not os.path.isdir(folder_path):
37
+ continue
38
+
39
+ # Assign an integer label to each emotion
40
+ if folder_name not in label_map:
41
+ label_map[folder_name] = label_idx
42
+ label_idx += 1
43
+
44
+ # Load images and labels
45
+ for img_name in os.listdir(folder_path):
46
+ img_path = os.path.join(folder_path, img_name)
47
+ if img_path.endswith('.jpg') or img_path.endswith('.png'):
48
+ img = load_img(img_path, target_size=image_size, color_mode='grayscale')
49
+ img_array = img_to_array(img)
50
+ images.append(img_array)
51
+ labels.append(label_map[folder_name])
52
+
53
+ # Convert images and labels to numpy arrays
54
+ images = np.array(images, dtype="float32") / 255.0 # Normalize
55
+ labels = np.array(labels)
56
+
57
+ return images, labels, label_map
58
+
59
+ dataset_path = 'train' # Replace with the path to your dataset
60
+ x, y, label_map = load_custom_dataset(dataset_path)
61
+
62
+ # Print class mapping for reference
63
+ print("Class labels:", label_map)
64
+
65
+ # ----------------------
66
+ # 2. HOG + SVM (Classical ML)
67
+ # ----------------------
68
+
69
+
70
+
71
+ def extract_hog_features(images):
72
+ # Check if the image is grayscale (2D) or RGB (3D)
73
+ # If grayscale (shape (H, W, 1)), squeeze it to (H, W) for processing
74
+ grayscale_images = [img.squeeze() if len(img.shape) == 3 else img for img in images]
75
+
76
+ # Extract HOG features from grayscale images
77
+ return np.array([hog(img, pixels_per_cell=(8, 8), cells_per_block=(2, 2)) for img in grayscale_images])
78
+
79
+ # Assuming your images are already in grayscale, so you can skip rgb2gray for grayscale images.
80
+
81
+
82
+
83
+ # def extract_hog_features(images):
84
+ # return np.array([hog(img, pixels_per_cell=(8, 8), cells_per_block=(2, 2)) for img in images])
85
+
86
+ print("[INFO] Extracting HOG features...")
87
+ x_hog = extract_hog_features(x)
88
+ x_train_hog, x_test_hog, y_train_hog, y_test_hog = train_test_split(x_hog, y, test_size=0.2, random_state=42)
89
+
90
+ print("[INFO] Training SVM classifier...")
91
+ svm = SVC(kernel='linear')
92
+ svm.fit(x_train_hog, y_train_hog)
93
+ y_pred_svm = svm.predict(x_test_hog)
94
+
95
+ print("\n[RESULTS] SVM Classification Report")
96
+ print(classification_report(y_test_hog, y_pred_svm))
97
+
98
+ # ----------------------
99
+ # 3. CNN (Deep Learning)
100
+ # ----------------------
101
+
102
+ y_cnn = to_categorical(y, num_classes=len(label_map))
103
+ x_train_cnn, x_test_cnn, y_train_cnn, y_test_cnn = train_test_split(x, y_cnn, test_size=0.2, random_state=42)
104
+
105
+ print("[INFO] Building CNN model...")
106
+ cnn = Sequential([
107
+ Conv2D(32, (3,3), activation='relu', input_shape=(48, 48, 1)),
108
+ MaxPooling2D(2,2),
109
+ Conv2D(64, (3,3), activation='relu'),
110
+ MaxPooling2D(2,2),
111
+ Flatten(),
112
+ Dense(128, activation='relu'),
113
+ Dense(len(label_map), activation='softmax')
114
+ ])
115
+
116
+ cnn.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
117
+ cnn.fit(x_train_cnn, y_train_cnn, epochs=3, batch_size=64, validation_split=0.1)
118
+
119
+ print("[INFO] Evaluating CNN...")
120
+ loss, acc = cnn.evaluate(x_test_cnn, y_test_cnn)
121
+ print(f"CNN Accuracy: {acc * 100:.2f}%")
122
+
123
+ # ----------------------
124
+ # 4. CLIP (Vision-Language) Comparison
125
+ # ----------------------
126
+
127
+ # Load the CLIP model and processor from Huggingface
128
+ clip_model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
129
+ clip_processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
130
+
131
+ # Example usage of CLIP for image processing
132
+ # image_input = torch.tensor(x[0]).unsqueeze(0) # Convert to tensor for CLIP input, add batch dimension
133
+ # text_input = ["Emotion description of the image"] # Provide a text description for comparison
134
+
135
+ # # Process the inputs with the CLIP processor
136
+ # inputs = clip_processor(text=text_input, images=image_input, return_tensors="pt", padding=True)
137
+ from PIL import Image
138
+
139
+ # Convert your NumPy grayscale image to a 3-channel RGB PIL image and resize it
140
+ gray_img = (x[0] * 255).astype(np.uint8).squeeze() # shape: (48, 48)
141
+ rgb_img = np.stack([gray_img]*3, axis=-1) # Convert to RGB shape: (48, 48, 3)
142
+ pil_img = Image.fromarray(rgb_img).resize((224, 224)) # Resize for CLIP input
143
+
144
+ # Prepare text input
145
+ text_input = ["Emotion description of the image"]
146
+
147
+ # Process using CLIPProcessor
148
+ inputs = clip_processor(text=text_input, images=pil_img, return_tensors="pt", padding=True)
149
+ # Get model predictions (outputs)
150
+ outputs = clip_model(**inputs)
151
+
152
+ # CLIP similarity comparison (text vs image)
153
+ logits_per_image = outputs.logits_per_image # similarity score
154
+ logits_per_text = outputs.logits_per_text # similarity score
155
+
156
+ print("\n[RESULTS] CLIP Similarity Scores")
157
+ print(f"Logits per image: {logits_per_image}")
158
+ print(f"Logits per text: {logits_per_text}")
159
+
160
+ # ----------------------
161
+ # 5. Vision Transformer (ViT) Comparison
162
+ # ----------------------
163
+
164
+ # Load ViT model and feature extractor for image classification
165
+ vit_model = ViTForImageClassification.from_pretrained("google/vit-base-patch16-224-in21k")
166
+ vit_feature_extractor = ViTFeatureExtractor.from_pretrained("google/vit-base-patch16-224-in21k")
167
+
168
+ # Process image and get predictions
169
+ inputs_vit = vit_feature_extractor(x[0], return_tensors="pt")
170
+ outputs_vit = vit_model(**inputs_vit)
171
+
172
+ print("\n[RESULTS] ViT Classification Scores")
173
+ print(f"ViT logits: {outputs_vit.logits}")
174
+
175
+ # ----------------------
176
+ # 6. Comparison Results Visualization
177
+ # ----------------------
178
+
179
+ # Prepare to visualize the comparisons
180
+ plt.figure(figsize=(12, 6))
181
+
182
+ # Plot Sample Image
183
+ plt.subplot(131)
184
+ plt.title("Sample Image")
185
+ plt.imshow(x[0], cmap='gray')
186
+
187
+ # Plot HOG Feature Visualization
188
+ plt.subplot(132)
189
+ plt.title("HOG Feature Visualization")
190
+ hog_img = hog(x[0], visualize=True)[1]
191
+ plt.imshow(hog_img, cmap='gray')
192
+
193
+ # Placeholder for CNN's evaluation
194
+ plt.subplot(133)
195
+ plt.title("CNN Evaluation")
196
+ cnn_img = cnn.predict(np.expand_dims(x[0], axis=0))
197
+ plt.imshow(cnn_img[0], cmap='gray')
198
+
199
+ plt.show()
200
+
201
+ # ----------------------
202
+ # 7. Final Model Comparison Summary
203
+ # ----------------------
204
+
205
+ # You can compare the results side by side in a table or any custom visualization
206
+ print("\n[COMPARISON SUMMARY]")
207
+ print(f"SVM (HOG) Accuracy: {svm.score(x_test_hog, y_test_hog) * 100:.2f}%")
208
+ print(f"CNN Accuracy: {acc * 100:.2f}%")
209
+
210
+ # CLIP and ViT don't have a direct "accuracy" metric, but you can report similarity scores for CLIP or logits for ViT.
211
+ print(f"CLIP Similarity Score: {logits_per_image}")
212
+ print(f"ViT Logits: {outputs_vit.logits}")
comparison2.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import os
2
+ # import joblib
3
+ # import json
4
+ # import numpy as np
5
+ # import tensorflow as tf
6
+ # from tqdm import tqdm
7
+ # from skimage.io import imread
8
+ # from skimage.transform import resize
9
+ # from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix
10
+ # from tensorflow.keras.models import model_from_json
11
+ # from transformers import CLIPProcessor, CLIPModel
12
+ # from torchvision import transforms
13
+ # from PIL import Image
14
+ # from sklearn.model_selection import train_test_split
15
+
16
+ # # ========== Constants ==========
17
+ # IMG_SIZE = 48
18
+ # DATASET_PATH = "train"
19
+ # EMOTIONS = ['angry', 'disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral']
20
+ # MODEL_PATH = '' # Update with the correct path to your model files
21
+
22
+ # # ========== Feature Extraction ==========
23
+ # def extract_hog(img):
24
+ # from skimage.feature import hog
25
+ # return hog(img, pixels_per_cell=(8, 8), cells_per_block=(2, 2), feature_vector=True)
26
+
27
+ # def extract_lbp(img):
28
+ # from skimage.feature import local_binary_pattern
29
+ # lbp = local_binary_pattern(img, P=8, R=1, method="uniform")
30
+ # (hist, _) = np.histogram(lbp.ravel(), bins=np.arange(0, 10), range=(0, 9))
31
+ # hist = hist.astype("float")
32
+ # hist /= (hist.sum() + 1e-7)
33
+ # return hist
34
+
35
+ # def extract_gabor(img):
36
+ # import cv2
37
+ # filters = []
38
+ # ksize = 31
39
+ # for theta in np.arange(0, np.pi, np.pi / 4):
40
+ # kernel = cv2.getGaborKernel((ksize, ksize), 4.0, theta, 10.0, 0.5, 0, ktype=cv2.CV_32F)
41
+ # filters.append(kernel)
42
+ # feats = [np.mean(cv2.filter2D(img, cv2.CV_8UC3, k)) for k in filters]
43
+ # return feats
44
+
45
+ # def extract_features(img):
46
+ # features = []
47
+ # features.extend(extract_hog(img))
48
+ # features.extend(extract_lbp(img))
49
+ # features.extend(extract_gabor(img))
50
+ # return features
51
+
52
+ # # ========== Dataset Loader ==========
53
+ # def load_dataset_features():
54
+ # X, y = [], []
55
+ # for label in EMOTIONS:
56
+ # folder = os.path.join(DATASET_PATH, label)
57
+ # if not os.path.exists(folder): continue
58
+ # for file in tqdm(os.listdir(folder), desc=f"Extracting {label}"):
59
+ # path = os.path.join(folder, file)
60
+ # try:
61
+ # img = imread(path, as_gray=True)
62
+ # img = resize(img, (IMG_SIZE, IMG_SIZE), anti_aliasing=True)
63
+ # feat = extract_features(img)
64
+ # X.append(feat)
65
+ # y.append(EMOTIONS.index(label))
66
+ # except Exception as e:
67
+ # print(f"[WARN] Skipped {file}: {e}")
68
+ # return np.array(X), np.array(y)
69
+
70
+ # def load_images():
71
+ # images, labels = [], []
72
+ # for label in EMOTIONS:
73
+ # folder = os.path.join(DATASET_PATH, label)
74
+ # if not os.path.exists(folder): continue
75
+ # for file in os.listdir(folder):
76
+ # path = os.path.join(folder, file)
77
+ # try:
78
+ # img = imread(path, as_gray=False)
79
+ # img = resize(img, (IMG_SIZE, IMG_SIZE), anti_aliasing=True)
80
+ # images.append(img)
81
+ # labels.append(EMOTIONS.index(label))
82
+ # except:
83
+ # continue
84
+ # return np.array(images), np.array(labels)
85
+
86
+ # # ========== Evaluation Metrics ==========
87
+ # def evaluate_model(y_true, y_pred, model_name):
88
+ # print(f"\n[RESULTS] {model_name}")
89
+ # print("Accuracy:", accuracy_score(y_true, y_pred))
90
+ # print("Precision:", precision_score(y_true, y_pred, average='weighted'))
91
+ # print("Recall:", recall_score(y_true, y_pred, average='weighted'))
92
+ # print("F1 Score:", f1_score(y_true, y_pred, average='weighted'))
93
+ # print("Confusion Matrix:\n", confusion_matrix(y_true, y_pred))
94
+
95
+ # # ========== Classical Models ==========
96
+ # # def evaluate_classical_models():
97
+ # # X_test, y_test = load_dataset_features()
98
+ # # for model_file in ["k-nn_model.joblib", "logistic_regression_model.joblib", "random_forest_model.joblib", "svm_model.joblib"]:
99
+ # # model = joblib.load(model_file)
100
+ # # y_pred = model.predict(X_test)
101
+ # # evaluate_model(y_test, y_pred, model_file)
102
+
103
+
104
+ # def evaluate_classical_models():
105
+ # print("\n[INFO] Evaluating classical ML models...\n")
106
+ # X, y = load_dataset_features()
107
+ # X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
108
+
109
+ # model_files = {
110
+ # 'KNN': 'k-nn_model.joblib',
111
+ # 'Logistic Regression': 'logistic_regression_model.joblib',
112
+ # 'Random Forest': 'random_forest_model.joblib',
113
+ # 'SVM': 'svm_model.joblib',
114
+ # }
115
+
116
+ # for name, file in model_files.items():
117
+ # print(f"\n--- {name} ---")
118
+ # model_path = os.path.join(MODEL_PATH, file)
119
+ # model = joblib.load(model_path)
120
+
121
+ # expected_input_size = model.n_features_in_
122
+ # if X_test.shape[1] != expected_input_size:
123
+ # print(f"[WARNING] Feature size mismatch for {name}: "
124
+ # f"Expected {expected_input_size}, Got {X_test.shape[1]}. Skipping...")
125
+ # continue
126
+
127
+ # y_pred = model.predict(X_test)
128
+ # acc = accuracy_score(y_test, y_pred)
129
+ # prec = precision_score(y_test, y_pred, average='weighted', zero_division=0)
130
+ # rec = recall_score(y_test, y_pred, average='weighted', zero_division=0)
131
+ # f1 = f1_score(y_test, y_pred, average='weighted', zero_division=0)
132
+
133
+ # print(f"Accuracy: {acc:.4f}")
134
+ # print(f"Precision: {prec:.4f}")
135
+ # print(f"Recall: {rec:.4f}")
136
+ # print(f"F1 Score: {f1:.4f}")
137
+ # print("Confusion Matrix:")
138
+ # print(confusion_matrix(y_test, y_pred))
139
+
140
+
141
+ # # ========== CNN/RNN Models ==========
142
+ # def evaluate_keras_model(model_path, X_test, y_test, model_name):
143
+ # model = tf.keras.models.load_model(model_path)
144
+ # y_pred = np.argmax(model.predict(X_test), axis=1)
145
+ # evaluate_model(y_test, y_pred, model_name)
146
+
147
+ # def evaluate_json_model(json_path, weights_path, X_test, y_test, model_name):
148
+ # with open(json_path, 'r') as f:
149
+ # model = model_from_json(f.read())
150
+ # model.load_weights(weights_path)
151
+ # y_pred = np.argmax(model.predict(X_test), axis=1)
152
+ # evaluate_model(y_test, y_pred, model_name)
153
+
154
+ # # ========== ViT/CLIP Model ==========
155
+ # def evaluate_clip_model():
156
+ # processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
157
+ # model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
158
+ # X_test, y_test = load_images()
159
+ # y_pred = []
160
+
161
+ # for i in tqdm(range(len(X_test))):
162
+ # img = Image.fromarray((X_test[i] * 255).astype(np.uint8))
163
+ # text = [f"a face showing {emotion} emotion" for emotion in EMOTIONS]
164
+ # inputs = processor(text=text, images=img, return_tensors="pt", padding=True)
165
+ # outputs = model(**inputs)
166
+ # logits_per_image = outputs.logits_per_image
167
+ # pred = logits_per_image.argmax().item()
168
+ # y_pred.append(pred)
169
+
170
+ # evaluate_model(y_test, y_pred, "ViT-B/32 (CLIP)")
171
+
172
+
173
+
174
+ # # ========== Run All ==========
175
+ # if __name__ == '__main__':
176
+ # # evaluate_classical_models()
177
+
178
+ # X_raw, y_raw = load_images()
179
+ # X_raw = X_raw.reshape(-1, IMG_SIZE, IMG_SIZE)
180
+
181
+ # # X_raw = X_raw.reshape(-1, IMG_SIZE, IMG_SIZE, 3)
182
+
183
+ # evaluate_keras_model("emotion_detector_model.h5", X_raw, y_raw, "CNN Emotion Model")
184
+ # evaluate_keras_model("cnn_rnn_model_from_dir.h5", X_raw, y_raw, "CNN + RNN Emotion Model")
185
+ # evaluate_json_model("model_cleaned.json", "model.weights.h5", X_raw, y_raw, "Custom JSON + Weights Model")
186
+
187
+ # # evaluate_clip_model()
emotion_detector_model.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e86e437571b9a100392f8911e913ce395a8f1dad3346dd3ed0e2ab556b47317d
3
+ size 10114720
haarcascade_frontalface_default.xml ADDED
The diff for this file is too large to render. See raw diff
 
help.txt ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ python3 -m venv venv
2
+ source venv/bin/activate
3
+
4
+ # Install dependencies
5
+ pip install --upgrade pip
6
+ pip install -r requirements.txt
7
+
8
+
9
+ python app.py
10
+
11
+
12
+ flask run --host=0.0.0.0 --port=5000
13
+
14
+
15
+
16
+
17
+
18
+ ngrok config add-authtoken 2Pz1X9nABC1234567890abcdefg
19
+
20
+
21
+ Authtoken saved to configuration file: /home/ubuntu/.config/ngrok/ngrok.yml
22
+
23
+
24
+ ngrok http 7860
25
+
26
+
27
+ python app.py --host=0.0.0.0 --port=7860
28
+
29
+ sudo apt install tmux
30
+ tmux new -s emotionapp
31
+ python app.py --host=0.0.0.0 --port=7860
32
+ ngrok http 7860
33
+ tmux attach -t emotionappNGORK
34
+
index.html ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Emotion Detection via ViT</title>
5
+ </head>
6
+ <body>
7
+ <h2>Webcam Emotion Detector</h2>
8
+ <video id="video" width="320" height="240" autoplay></video>
9
+ <canvas id="canvas" width="320" height="240" style="display:none;"></canvas>
10
+ <p id="emotion"></p>
11
+ <script>
12
+ const video = document.getElementById("video");
13
+ const canvas = document.getElementById("canvas");
14
+ const context = canvas.getContext("2d");
15
+ const emotionText = document.getElementById("emotion");
16
+
17
+ navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
18
+ video.srcObject = stream;
19
+ });
20
+
21
+ setInterval(() => {
22
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
23
+ canvas.toBlob(blob => {
24
+ const formData = new FormData();
25
+ formData.append("frame", blob, "frame.jpg");
26
+
27
+ fetch("http://127.0.0.1:7860/analyze", {
28
+ method: "POST",
29
+ body: formData,
30
+ })
31
+ .then(response => response.json())
32
+ .then(data => {
33
+ emotionText.textContent = "Detected Emotion: " + data.emotion;
34
+ });
35
+ }, "image/jpeg");
36
+ }, 3000); // every 3 seconds
37
+ </script>
38
+ </body>
39
+ </html>
k-nn_model.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:385097ff0871d5669fcdea19e79296c722b800f8dc0f79cf531b75d959c2c8e3
3
+ size 182810196
knn.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import cv2
3
+ import base64
4
+ from tensorflow.keras.preprocessing import image
5
+ import logging
6
+ import joblib
7
+
8
+ # Load KNN model globally
9
+ knn_model = joblib.load('k-nn_model.joblib')
10
+
11
+ # Emotion classes
12
+ class_labels = ['angry', 'disgust', 'fear', 'happy', 'neutral', 'sad', 'surprise']
13
+
14
+ def detect_knn(image_path):
15
+ frame = cv2.imread(image_path)
16
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
17
+ resized = cv2.resize(gray, (48, 48)) # 48x48
18
+ norm_img = resized / 255.0
19
+
20
+ # Feature extraction similar to training: horizontal chunks
21
+ chunks = [norm_img[:, i*8:(i+1)*8] for i in range(6)] # 6 chunks of 8px
22
+ sequence = np.stack([chunk.flatten() for chunk in chunks]) # (6, 384)
23
+ features = sequence.flatten() # (2304,)
24
+ features = features[:994] # match training shape
25
+
26
+ features = features.reshape(1, -1)
27
+
28
+ prediction = knn_model.predict(features)[0]
29
+ class_labels = ['angry', 'disgust', 'fear', 'happy', 'neutral', 'sad', 'surprise']
30
+ emotion = class_labels[prediction]
31
+
32
+ _, buffer = cv2.imencode('.jpg', frame)
33
+ frame_base64 = base64.b64encode(buffer).decode('utf-8')
34
+
35
+ return emotion, frame_base64
logistic_regression_model.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1ec9febd0fbd6a44614a85172d55d18ee0a45a7f0e2962475afb4e1dc6fdbf8d
3
+ size 56623
logisticregression.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import cv2
3
+ import base64
4
+ from tensorflow.keras.preprocessing import image
5
+ import logging
6
+ import joblib
7
+
8
+ # Load KNN model globally
9
+ knn_model = joblib.load('logistic_regression_model.joblib')
10
+
11
+ # Emotion classes
12
+ class_labels = ['angry', 'disgust', 'fear', 'happy', 'neutral', 'sad', 'surprise']
13
+
14
+ def detect_lr(image_path):
15
+ frame = cv2.imread(image_path)
16
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
17
+ resized = cv2.resize(gray, (48, 48)) # 48x48
18
+ norm_img = resized / 255.0
19
+
20
+ # Feature extraction similar to training: horizontal chunks
21
+ chunks = [norm_img[:, i*8:(i+1)*8] for i in range(6)] # 6 chunks of 8px
22
+ sequence = np.stack([chunk.flatten() for chunk in chunks]) # (6, 384)
23
+ features = sequence.flatten() # (2304,)
24
+ features = features[:994] # match training shape
25
+
26
+ features = features.reshape(1, -1)
27
+
28
+ prediction = knn_model.predict(features)[0]
29
+ class_labels = ['angry', 'disgust', 'fear', 'happy', 'neutral', 'sad', 'surprise']
30
+ emotion = class_labels[prediction]
31
+
32
+ _, buffer = cv2.imencode('.jpg', frame)
33
+ frame_base64 = base64.b64encode(buffer).decode('utf-8')
34
+
35
+ return emotion, frame_base64
model.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"module": "keras", "class_name": "Sequential", "config": {"name": "sequential_12", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "layers": [{"module": "keras.layers", "class_name": "InputLayer", "config": {"batch_shape": [null, 48, 48, 1], "dtype": "float32", "sparse": false, "name": "input_layer_12"}, "registered_name": null}, {"module": "keras.layers", "class_name": "Conv2D", "config": {"name": "conv2d_48", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "filters": 64, "kernel_size": [3, 3], "strides": [1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"module": "keras.initializers", "class_name": "GlorotUniform", "config": {"seed": null}, "registered_name": null}, "bias_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "registered_name": null, "build_config": {"input_shape": [null, 48, 48, 1]}}, {"module": "keras.layers", "class_name": "BatchNormalization", "config": {"name": "batch_normalization_72", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "axis": -1, "momentum": 0.99, "epsilon": 0.001, "center": true, "scale": true, "beta_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "gamma_initializer": {"module": "keras.initializers", "class_name": "Ones", "config": {}, "registered_name": null}, "moving_mean_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "moving_variance_initializer": {"module": "keras.initializers", "class_name": "Ones", "config": {}, "registered_name": null}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null, "synchronized": false}, "registered_name": null, "build_config": {"input_shape": [null, 48, 48, 64]}}, {"module": "keras.layers", "class_name": "Activation", "config": {"name": "activation_72", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "activation": "relu"}, "registered_name": null, "build_config": {"input_shape": [null, 48, 48, 64]}}, {"module": "keras.layers", "class_name": "MaxPooling2D", "config": {"name": "max_pooling2d_48", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "pool_size": [2, 2], "padding": "valid", "strides": [2, 2], "data_format": "channels_last"}, "registered_name": null, "build_config": {"input_shape": [null, 48, 48, 64]}}, {"module": "keras.layers", "class_name": "Dropout", "config": {"name": "dropout_72", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "rate": 0.25, "seed": null, "noise_shape": null}, "registered_name": null, "build_config": {"input_shape": [null, 24, 24, 64]}}, {"module": "keras.layers", "class_name": "Conv2D", "config": {"name": "conv2d_49", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "filters": 128, "kernel_size": [5, 5], "strides": [1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"module": "keras.initializers", "class_name": "GlorotUniform", "config": {"seed": null}, "registered_name": null}, "bias_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "registered_name": null, "build_config": {"input_shape": [null, 24, 24, 64]}}, {"module": "keras.layers", "class_name": "BatchNormalization", "config": {"name": "batch_normalization_73", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "axis": -1, "momentum": 0.99, "epsilon": 0.001, "center": true, "scale": true, "beta_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "gamma_initializer": {"module": "keras.initializers", "class_name": "Ones", "config": {}, "registered_name": null}, "moving_mean_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "moving_variance_initializer": {"module": "keras.initializers", "class_name": "Ones", "config": {}, "registered_name": null}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null, "synchronized": false}, "registered_name": null, "build_config": {"input_shape": [null, 24, 24, 128]}}, {"module": "keras.layers", "class_name": "Activation", "config": {"name": "activation_73", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "activation": "relu"}, "registered_name": null, "build_config": {"input_shape": [null, 24, 24, 128]}}, {"module": "keras.layers", "class_name": "MaxPooling2D", "config": {"name": "max_pooling2d_49", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "pool_size": [2, 2], "padding": "valid", "strides": [2, 2], "data_format": "channels_last"}, "registered_name": null, "build_config": {"input_shape": [null, 24, 24, 128]}}, {"module": "keras.layers", "class_name": "Dropout", "config": {"name": "dropout_73", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "rate": 0.25, "seed": null, "noise_shape": null}, "registered_name": null, "build_config": {"input_shape": [null, 12, 12, 128]}}, {"module": "keras.layers", "class_name": "Conv2D", "config": {"name": "conv2d_50", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "filters": 256, "kernel_size": [3, 3], "strides": [1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"module": "keras.initializers", "class_name": "GlorotUniform", "config": {"seed": null}, "registered_name": null}, "bias_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "registered_name": null, "build_config": {"input_shape": [null, 12, 12, 128]}}, {"module": "keras.layers", "class_name": "BatchNormalization", "config": {"name": "batch_normalization_74", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "axis": -1, "momentum": 0.99, "epsilon": 0.001, "center": true, "scale": true, "beta_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "gamma_initializer": {"module": "keras.initializers", "class_name": "Ones", "config": {}, "registered_name": null}, "moving_mean_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "moving_variance_initializer": {"module": "keras.initializers", "class_name": "Ones", "config": {}, "registered_name": null}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null, "synchronized": false}, "registered_name": null, "build_config": {"input_shape": [null, 12, 12, 256]}}, {"module": "keras.layers", "class_name": "Activation", "config": {"name": "activation_74", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "activation": "relu"}, "registered_name": null, "build_config": {"input_shape": [null, 12, 12, 256]}}, {"module": "keras.layers", "class_name": "MaxPooling2D", "config": {"name": "max_pooling2d_50", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "pool_size": [2, 2], "padding": "valid", "strides": [2, 2], "data_format": "channels_last"}, "registered_name": null, "build_config": {"input_shape": [null, 12, 12, 256]}}, {"module": "keras.layers", "class_name": "Dropout", "config": {"name": "dropout_74", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "rate": 0.25, "seed": null, "noise_shape": null}, "registered_name": null, "build_config": {"input_shape": [null, 6, 6, 256]}}, {"module": "keras.layers", "class_name": "Conv2D", "config": {"name": "conv2d_51", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "filters": 512, "kernel_size": [3, 3], "strides": [1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1], "groups": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"module": "keras.initializers", "class_name": "GlorotUniform", "config": {"seed": null}, "registered_name": null}, "bias_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "registered_name": null, "build_config": {"input_shape": [null, 6, 6, 256]}}, {"module": "keras.layers", "class_name": "BatchNormalization", "config": {"name": "batch_normalization_75", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "axis": -1, "momentum": 0.99, "epsilon": 0.001, "center": true, "scale": true, "beta_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "gamma_initializer": {"module": "keras.initializers", "class_name": "Ones", "config": {}, "registered_name": null}, "moving_mean_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "moving_variance_initializer": {"module": "keras.initializers", "class_name": "Ones", "config": {}, "registered_name": null}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null, "synchronized": false}, "registered_name": null, "build_config": {"input_shape": [null, 6, 6, 512]}}, {"module": "keras.layers", "class_name": "Activation", "config": {"name": "activation_75", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "activation": "relu"}, "registered_name": null, "build_config": {"input_shape": [null, 6, 6, 512]}}, {"module": "keras.layers", "class_name": "MaxPooling2D", "config": {"name": "max_pooling2d_51", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "pool_size": [2, 2], "padding": "valid", "strides": [2, 2], "data_format": "channels_last"}, "registered_name": null, "build_config": {"input_shape": [null, 6, 6, 512]}}, {"module": "keras.layers", "class_name": "Dropout", "config": {"name": "dropout_75", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "rate": 0.25, "seed": null, "noise_shape": null}, "registered_name": null, "build_config": {"input_shape": [null, 3, 3, 512]}}, {"module": "keras.layers", "class_name": "Flatten", "config": {"name": "flatten_12", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "data_format": "channels_last"}, "registered_name": null, "build_config": {"input_shape": [null, 3, 3, 512]}}, {"module": "keras.layers", "class_name": "Dense", "config": {"name": "dense_36", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "units": 256, "activation": "linear", "use_bias": true, "kernel_initializer": {"module": "keras.initializers", "class_name": "GlorotUniform", "config": {"seed": null}, "registered_name": null}, "bias_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "kernel_regularizer": null, "bias_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "registered_name": null, "build_config": {"input_shape": [null, 4608]}}, {"module": "keras.layers", "class_name": "BatchNormalization", "config": {"name": "batch_normalization_76", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "axis": -1, "momentum": 0.99, "epsilon": 0.001, "center": true, "scale": true, "beta_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "gamma_initializer": {"module": "keras.initializers", "class_name": "Ones", "config": {}, "registered_name": null}, "moving_mean_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "moving_variance_initializer": {"module": "keras.initializers", "class_name": "Ones", "config": {}, "registered_name": null}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null, "synchronized": false}, "registered_name": null, "build_config": {"input_shape": [null, 256]}}, {"module": "keras.layers", "class_name": "Activation", "config": {"name": "activation_76", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "activation": "relu"}, "registered_name": null, "build_config": {"input_shape": [null, 256]}}, {"module": "keras.layers", "class_name": "Dropout", "config": {"name": "dropout_76", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "rate": 0.25, "seed": null, "noise_shape": null}, "registered_name": null, "build_config": {"input_shape": [null, 256]}}, {"module": "keras.layers", "class_name": "Dense", "config": {"name": "dense_37", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "units": 512, "activation": "linear", "use_bias": true, "kernel_initializer": {"module": "keras.initializers", "class_name": "GlorotUniform", "config": {"seed": null}, "registered_name": null}, "bias_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "kernel_regularizer": null, "bias_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "registered_name": null, "build_config": {"input_shape": [null, 256]}}, {"module": "keras.layers", "class_name": "BatchNormalization", "config": {"name": "batch_normalization_77", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "axis": -1, "momentum": 0.99, "epsilon": 0.001, "center": true, "scale": true, "beta_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "gamma_initializer": {"module": "keras.initializers", "class_name": "Ones", "config": {}, "registered_name": null}, "moving_mean_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "moving_variance_initializer": {"module": "keras.initializers", "class_name": "Ones", "config": {}, "registered_name": null}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null, "synchronized": false}, "registered_name": null, "build_config": {"input_shape": [null, 512]}}, {"module": "keras.layers", "class_name": "Activation", "config": {"name": "activation_77", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "activation": "relu"}, "registered_name": null, "build_config": {"input_shape": [null, 512]}}, {"module": "keras.layers", "class_name": "Dropout", "config": {"name": "dropout_77", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "rate": 0.25, "seed": null, "noise_shape": null}, "registered_name": null, "build_config": {"input_shape": [null, 512]}}, {"module": "keras.layers", "class_name": "Dense", "config": {"name": "dense_38", "trainable": true, "dtype": {"module": "keras", "class_name": "DTypePolicy", "config": {"name": "float32"}, "registered_name": null}, "units": 7, "activation": "softmax", "use_bias": true, "kernel_initializer": {"module": "keras.initializers", "class_name": "GlorotUniform", "config": {"seed": null}, "registered_name": null}, "bias_initializer": {"module": "keras.initializers", "class_name": "Zeros", "config": {}, "registered_name": null}, "kernel_regularizer": null, "bias_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "registered_name": null, "build_config": {"input_shape": [null, 512]}}], "build_input_shape": [null, 48, 48, 1]}, "registered_name": null, "build_config": {"input_shape": [null, 48, 48, 1]}, "compile_config": {"optimizer": {"module": "keras.optimizers", "class_name": "Adam", "config": {"name": "adam", "learning_rate": 9.999999747378752e-06, "weight_decay": null, "clipnorm": null, "global_clipnorm": null, "clipvalue": null, "use_ema": false, "ema_momentum": 0.99, "ema_overwrite_frequency": null, "loss_scale_factor": null, "gradient_accumulation_steps": null, "beta_1": 0.9, "beta_2": 0.999, "epsilon": 1e-07, "amsgrad": false}, "registered_name": null}, "loss": "sparse_categorical_crossentropy", "loss_weights": null, "metrics": ["accuracy"], "weighted_metrics": null, "run_eagerly": false, "steps_per_execution": 1, "jit_compile": false}}
model.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tensorflow.keras.models import model_from_json
2
+ from tensorflow.python.keras.backend import set_session
3
+ import numpy as np
4
+
5
+ import tensorflow as tf
6
+
7
+ config = tf.compat.v1.ConfigProto()
8
+ config.gpu_options.per_process_gpu_memory_fraction = 0.15
9
+ session = tf.compat.v1.Session(config=config)
10
+ set_session(session)
11
+
12
+
13
+ class ERModel(object):
14
+
15
+ EMOTIONS_LIST = ["Angry", "Disgust",
16
+ "Fear", "Happy",
17
+ "Neutral", "Sad",
18
+ "Surprise"]
19
+
20
+ def __init__(self, model_json_file, model_weights_file):
21
+
22
+ with open(model_json_file, "r") as json_file:
23
+ loaded_model_json = json_file.read()
24
+ self.loaded_model = model_from_json(loaded_model_json)
25
+
26
+ self.loaded_model.load_weights(model_weights_file)
27
+
28
+ def predict_emotion(self, img):
29
+ global session
30
+ set_session(session)
31
+ self.preds = self.loaded_model.predict(img)
32
+ return ERModel.EMOTIONS_LIST[np.argmax(self.preds)]
model.weights.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:67e7e598c46e362929f4d6ccd090e4364459d09ca10f7a5518061ef5c0e35340
3
+ size 36100040
model/clip_emotion.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import clip
3
+ from PIL import Image
4
+ import os
5
+
6
+ device = "cuda" if torch.cuda.is_available() else "cpu"
7
+ model, preprocess = clip.load("ViT-B/32", device=device)
8
+
9
+ EMOTION_LABELS = ["happy", "sad", "angry", "surprised", "neutral", "disgusted", "fearful"]
10
+
11
+ def detect_emotion(image_path):
12
+ image = preprocess(Image.open(image_path)).unsqueeze(0).to(device)
13
+ text_inputs = torch.cat([clip.tokenize(f"A face showing {emotion}") for emotion in EMOTION_LABELS]).to(device)
14
+
15
+ with torch.no_grad():
16
+ image_features = model.encode_image(image)
17
+ text_features = model.encode_text(text_inputs)
18
+ logits_per_image, _ = model(image, text_inputs)
19
+ probs = logits_per_image.softmax(dim=-1).cpu().numpy().flatten()
20
+
21
+ result = dict(zip(EMOTION_LABELS, probs.tolist()))
22
+ top_emotion = max(result, key=result.get)
23
+ return top_emotion, result
model_weights.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f0511939f49907e32eeb49815ce85c963110258263a3b836a195aadc0202de0a
3
+ size 12075696
models/clip_emotion.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import clip
3
+ from PIL import Image
4
+ import os
5
+
6
+ device = "cuda" if torch.cuda.is_available() else "cpu"
7
+ model, preprocess = clip.load("ViT-B/32", device=device)
8
+
9
+ EMOTION_LABELS = ["happy", "sad", "angry", "surprised", "neutral", "disgusted", "fearful"]
10
+ AGE_LABELS = [
11
+ "a child", "a teenager", "a young adult", "a middle-aged person", "an elderly person"
12
+ ]
13
+
14
+ def detect_emotion(image_path):
15
+ image = preprocess(Image.open(image_path)).unsqueeze(0).to(device)
16
+ text_inputs = torch.cat([clip.tokenize(f"A face showing {emotion}") for emotion in EMOTION_LABELS]).to(device)
17
+
18
+ with torch.no_grad():
19
+ image_features = model.encode_image(image)
20
+ text_features = model.encode_text(text_inputs)
21
+ logits_per_image, _ = model(image, text_inputs)
22
+ probs = logits_per_image.softmax(dim=-1).cpu().numpy().flatten()
23
+
24
+ result = dict(zip(EMOTION_LABELS, probs.tolist()))
25
+ top_emotion = max(result, key=result.get)
26
+ return top_emotion, result
27
+
28
+ def detect_age(image_path):
29
+ image = preprocess(Image.open(image_path)).unsqueeze(0).to(device)
30
+ text_inputs = torch.cat([clip.tokenize(f"This is {label}") for label in AGE_LABELS]).to(device)
31
+
32
+ with torch.no_grad():
33
+ logits_per_image, _ = model(image, text_inputs)
34
+ probs = logits_per_image.softmax(dim=-1).cpu().numpy().flatten()
35
+
36
+ result = dict(zip(AGE_LABELS, probs.tolist()))
37
+ top_age = max(result, key=result.get)
38
+ return top_age, result
random_forest_model.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6c90e3878501a6d0604642695a545ba089c6fa564e17a5af8266ab1cb46453b1
3
+ size 122485297
randomforest.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import cv2
3
+ import base64
4
+ from tensorflow.keras.preprocessing import image
5
+ import logging
6
+ import joblib
7
+
8
+ # Load KNN model globally
9
+ knn_model = joblib.load('random_forest_model.joblib')
10
+
11
+ # Emotion classes
12
+ class_labels = ['angry', 'disgust', 'fear', 'happy', 'neutral', 'sad', 'surprise']
13
+
14
+ def detect_rf(image_path):
15
+ frame = cv2.imread(image_path)
16
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
17
+ resized = cv2.resize(gray, (48, 48)) # 48x48
18
+ norm_img = resized / 255.0
19
+
20
+ # Feature extraction similar to training: horizontal chunks
21
+ chunks = [norm_img[:, i*8:(i+1)*8] for i in range(6)] # 6 chunks of 8px
22
+ sequence = np.stack([chunk.flatten() for chunk in chunks]) # (6, 384)
23
+ features = sequence.flatten() # (2304,)
24
+ features = features[:994] # match training shape
25
+
26
+ features = features.reshape(1, -1)
27
+
28
+ prediction = knn_model.predict(features)[0]
29
+ class_labels = ['angry', 'disgust', 'fear', 'happy', 'neutral', 'sad', 'surprise']
30
+ emotion = class_labels[prediction]
31
+
32
+ _, buffer = cv2.imencode('.jpg', frame)
33
+ frame_base64 = base64.b64encode(buffer).decode('utf-8')
34
+
35
+ return emotion, frame_base64
requirements.txt ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ tensorflow
2
+ torch
3
+ transformers
4
+ opencv-python
5
+ flask
6
+ flask-cors
7
+ Pillow
8
+ ftfy
9
+ regex
10
+ tqdm
11
+ git+https://github.com/openai/CLIP.git
12
+ scikit-image
13
+ joblib
14
+ scikit-learn
static/index.html ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Emotion Detection</title>
5
+ </head>
6
+ <body>
7
+ <h2>Webcam Emotion Detector</h2>
8
+ <video id="video" width="320" height="240" autoplay></video>
9
+ <canvas id="canvas" width="320" height="240" style="display:none;"></canvas>
10
+ <p id="emotion"></p>
11
+ <script>
12
+ const video = document.getElementById("video");
13
+ const canvas = document.getElementById("canvas");
14
+ const context = canvas.getContext("2d");
15
+ const emotionText = document.getElementById("emotion");
16
+
17
+ navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
18
+ video.srcObject = stream;
19
+ });
20
+
21
+ setInterval(() => {
22
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
23
+ canvas.toBlob(blob => {
24
+ const formData = new FormData();
25
+ formData.append("frame", blob, "frame.jpg");
26
+
27
+ fetch("http://127.0.0.1:7860/analyze", {
28
+ method: "POST",
29
+ body: formData,
30
+ })
31
+ .then(response => response.json())
32
+ .then(data => {
33
+ emotionText.textContent = "Detected Emotion: " + data.emotion;
34
+ });
35
+ }, "image/jpeg");
36
+ }, 3000); // every 3 seconds
37
+ </script>
38
+ </body>
39
+ </html>
svm.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import cv2
3
+ import base64
4
+ from tensorflow.keras.preprocessing import image
5
+ import logging
6
+ import joblib
7
+
8
+ # Load KNN model globally
9
+ knn_model = joblib.load('svm_model.joblib')
10
+
11
+ # Emotion classes
12
+ class_labels = ['angry', 'disgust', 'fear', 'happy', 'neutral', 'sad', 'surprise']
13
+
14
+ def detect_svm(image_path):
15
+ frame = cv2.imread(image_path)
16
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
17
+ resized = cv2.resize(gray, (48, 48)) # 48x48
18
+ norm_img = resized / 255.0
19
+
20
+ # Feature extraction similar to training: horizontal chunks
21
+ chunks = [norm_img[:, i*8:(i+1)*8] for i in range(6)] # 6 chunks of 8px
22
+ sequence = np.stack([chunk.flatten() for chunk in chunks]) # (6, 384)
23
+ features = sequence.flatten() # (2304,)
24
+ features = features[:994] # match training shape
25
+
26
+ features = features.reshape(1, -1)
27
+
28
+ prediction = knn_model.predict(features)[0]
29
+ class_labels = ['angry', 'disgust', 'fear', 'happy', 'neutral', 'sad', 'surprise']
30
+ emotion = class_labels[prediction]
31
+
32
+ _, buffer = cv2.imencode('.jpg', frame)
33
+ frame_base64 = base64.b64encode(buffer).decode('utf-8')
34
+
35
+ return emotion, frame_base64
svm_model.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e7132d2c3f1d01003c85fe2411a2322d7a0d3d62056ae5f1b3b5343869ea988f
3
+ size 156484827
templates/classification_reports.html ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Emotion Recognition Model Reports</title>
6
+ <style>
7
+ body {
8
+ font-family: Arial, sans-serif;
9
+ background-color: #f4f4f4;
10
+ margin: 0;
11
+ padding: 2rem;
12
+ }
13
+ h1 {
14
+ text-align: center;
15
+ color: #333;
16
+ }
17
+ h2 {
18
+ margin-top: 2rem;
19
+ color: #2c3e50;
20
+ }
21
+ pre {
22
+ background-color: #fff;
23
+ border-left: 5px solid #007BFF;
24
+ padding: 1rem;
25
+ overflow-x: auto;
26
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
27
+ white-space: pre-wrap;
28
+ }
29
+ </style>
30
+ </head>
31
+ <body>
32
+ <h1>Classification Reports for Emotion Recognition Models</h1>
33
+
34
+ <h2>Support Vector Machine (SVM)</h2>
35
+ <pre>{{ svm_report }}</pre>
36
+
37
+ <h2>Random Forest</h2>
38
+ <pre>{{ rf_report }}</pre>
39
+
40
+ <h2>k-Nearest Neighbors (k-NN)</h2>
41
+ <pre>{{ knn_report }}</pre>
42
+
43
+ <h2>Logistic Regression</h2>
44
+ <pre>{{ lr_report }}</pre>
45
+ </body>
46
+ </html>
templates/cnn.html ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Emotion Detection via CNN</title>
5
+ <style>
6
+ body {
7
+ font-family: Arial, sans-serif;
8
+ text-align: center;
9
+ }
10
+ #video, #canvas {
11
+ border: 2px solid #444;
12
+ border-radius: 8px;
13
+ margin: 10px;
14
+ }
15
+ #result {
16
+ font-size: 18px;
17
+ font-weight: bold;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <h2>Webcam Emotion Detector</h2>
23
+
24
+ <video id="video" width="320" height="240" autoplay></video>
25
+ <canvas id="canvas" width="320" height="240" style="display:none;"></canvas>
26
+
27
+ <p id="result">Waiting for detection...</p>
28
+
29
+ <button onclick="window.location.href='http://127.0.0.1:7860/'">
30
+ ⬅ Back to Model Selection
31
+ </button>
32
+
33
+ <script>
34
+ const video = document.getElementById("video");
35
+ const canvas = document.getElementById("canvas");
36
+ const context = canvas.getContext("2d");
37
+ const resultText = document.getElementById("result");
38
+
39
+ // Start video stream
40
+ navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
41
+ video.srcObject = stream;
42
+ });
43
+
44
+ setInterval(() => {
45
+ // Draw current video frame to canvas
46
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
47
+
48
+ // Convert to blob and send to backend
49
+ canvas.toBlob(blob => {
50
+ const formData = new FormData();
51
+ formData.append("frame", blob, "frame.jpg");
52
+
53
+ fetch("http://127.0.0.1:7860/cnn", {
54
+ method: "POST",
55
+ body: formData,
56
+ })
57
+ .then(response => response.json())
58
+ .then(data => {
59
+ resultText.innerHTML = `
60
+ Detected Emotion: <strong>${data.emotion}</strong><br>
61
+ `;
62
+
63
+ })
64
+ .catch(err => {
65
+ resultText.textContent = "Error: " + err.message;
66
+ });
67
+ }, "image/jpeg");
68
+ }, 300); // Every 3 seconds
69
+ </script>
70
+ </body>
71
+ </html>
templates/cnn_lstm.html ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Emotion Detection via ViT</title>
5
+ <style>
6
+ body {
7
+ font-family: Arial, sans-serif;
8
+ text-align: center;
9
+ }
10
+ #video, #canvas {
11
+ border: 2px solid #444;
12
+ border-radius: 8px;
13
+ margin: 10px;
14
+ }
15
+ #result {
16
+ font-size: 18px;
17
+ font-weight: bold;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <h2>Webcam CNN LSTM Emotion Detector</h2>
23
+ <video id="video" width="320" height="240" autoplay></video>
24
+ <canvas id="canvas" width="320" height="240" style="display:none;"></canvas>
25
+ <p id="emotion">Waiting for detection...</p>
26
+ <script>
27
+ const video = document.getElementById("video");
28
+ const canvas = document.getElementById("canvas");
29
+ const context = canvas.getContext("2d");
30
+ const emotionText = document.getElementById("emotion");
31
+
32
+ navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
33
+ video.srcObject = stream;
34
+ });
35
+
36
+ setInterval(() => {
37
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
38
+ canvas.toBlob(blob => {
39
+ const formData = new FormData();
40
+ formData.append("frame", blob, "frame.jpg");
41
+
42
+ fetch("http://127.0.0.1:7860/cnn_lstm_video_feed", {
43
+ method: "POST",
44
+ body: formData,
45
+ })
46
+ .then(response => response.json())
47
+ .then(data => {
48
+ emotionText.textContent = "Detected Emotion: " + data.emotion;
49
+ });
50
+ }, "image/jpeg");
51
+ }, 300); // every 3 seconds
52
+ </script>
53
+ <button onclick="window.location.href='http://127.0.0.1:7860/'">⬅ Back to Model Selection</button>
54
+ </body>
55
+ </html>
templates/cnn_resnet.html ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Emotion Detection via CNN + RNN</title>
5
+ <style>
6
+ body {
7
+ font-family: Arial, sans-serif;
8
+ text-align: center;
9
+ }
10
+ #video, #canvas {
11
+ border: 2px solid #444;
12
+ border-radius: 8px;
13
+ margin: 10px;
14
+ }
15
+ #result {
16
+ font-size: 18px;
17
+ font-weight: bold;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <h2>Webcam Emotion Detector</h2>
23
+
24
+ <video id="video" width="320" height="240" autoplay></video>
25
+ <canvas id="canvas" width="320" height="240" style="display:none;"></canvas>
26
+
27
+ <p id="result">Waiting for detection...</p>
28
+
29
+ <button onclick="window.location.href='http://127.0.0.1:7860/'">
30
+ ⬅ Back to Model Selection
31
+ </button>
32
+
33
+ <script>
34
+ const video = document.getElementById("video");
35
+ const canvas = document.getElementById("canvas");
36
+ const context = canvas.getContext("2d");
37
+ const resultText = document.getElementById("result");
38
+
39
+ // Start video stream
40
+ navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
41
+ video.srcObject = stream;
42
+ });
43
+
44
+ setInterval(() => {
45
+ // Draw current video frame to canvas
46
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
47
+
48
+ // Convert to blob and send to backend
49
+ canvas.toBlob(blob => {
50
+ const formData = new FormData();
51
+ formData.append("frame", blob, "frame.jpg");
52
+
53
+ fetch("http://127.0.0.1:7860/cnn_resnet", {
54
+ method: "POST",
55
+ body: formData,
56
+ })
57
+ .then(response => response.json())
58
+ .then(data => {
59
+ resultText.innerHTML = `
60
+ Detected Emotion: <strong>${data.emotion}</strong><br>
61
+ `;
62
+
63
+ })
64
+ .catch(err => {
65
+ resultText.textContent = "Error: " + err.message;
66
+ });
67
+ }, "image/jpeg");
68
+ }, 300); // Every 3 seconds
69
+ </script>
70
+ </body>
71
+ </html>
templates/cnnkeras.html ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Emotion Detection via ViT</title>
5
+ <style>
6
+ body {
7
+ font-family: Arial, sans-serif;
8
+ text-align: center;
9
+ }
10
+ #video, #canvas {
11
+ border: 2px solid #444;
12
+ border-radius: 8px;
13
+ margin: 10px;
14
+ }
15
+ #result {
16
+ font-size: 18px;
17
+ font-weight: bold;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <h2>Webcam CNN Keras Emotion Detector</h2>
23
+ <video id="video" width="320" height="240" autoplay></video>
24
+ <canvas id="canvas" width="320" height="240" style="display:none;"></canvas>
25
+ <p id="emotion">Waiting for detection...</p>
26
+ <script>
27
+ const video = document.getElementById("video");
28
+ const canvas = document.getElementById("canvas");
29
+ const context = canvas.getContext("2d");
30
+ const emotionText = document.getElementById("emotion");
31
+
32
+ navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
33
+ video.srcObject = stream;
34
+ });
35
+
36
+ setInterval(() => {
37
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
38
+ canvas.toBlob(blob => {
39
+ const formData = new FormData();
40
+ formData.append("frame", blob, "frame.jpg");
41
+
42
+ fetch("http://127.0.0.1:7860/video_feed", {
43
+ method: "POST",
44
+ body: formData,
45
+ })
46
+ .then(response => response.json())
47
+ .then(data => {
48
+ emotionText.textContent = "Detected Emotion: " + data.emotion;
49
+ });
50
+ }, "image/jpeg");
51
+ }, 300); // every 3 seconds
52
+ </script>
53
+ <button onclick="window.location.href='http://127.0.0.1:7860/'">⬅ Back to Model Selection</button>
54
+ </body>
55
+ </html>
templates/index.html ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Select Emotion Detection Model</title>
5
+ <style>
6
+ body {
7
+ font-family: Arial, sans-serif;
8
+ text-align: center;
9
+ }
10
+ #video, #canvas {
11
+ border: 2px solid #444;
12
+ border-radius: 8px;
13
+ margin: 10px;
14
+ }
15
+ #result {
16
+ font-size: 18px;
17
+ font-weight: bold;
18
+ }
19
+ #reportButton {
20
+ margin-top: 20px;
21
+ padding: 10px 20px;
22
+ font-size: 16px;
23
+ cursor: pointer;
24
+ }
25
+ </style>
26
+ </head>
27
+ <body>
28
+ <h2>Select Emotion Detection Model</h2>
29
+
30
+ <label for="model">Choose Model:</label>
31
+ <select id="model">
32
+ <option value="" selected disabled>-- Select Model --</option>
33
+ <option value="knn">KNN </option>
34
+ <option value="svm">SVM </option>
35
+ <option value="rf">Random Forest </option>
36
+ <option value="lr">Logistic Regression </option>
37
+ <option value="vit">Vision Transformer (ViT)</option>
38
+ <option value="cnnkeras">CNN + Keras Tensorflow</option>
39
+ <option value="cnnonly">CNN</option>
40
+ <option value="cnnrnn">CNN + RNN </option>
41
+ <option value="cnnlstm">CNN + LSTM </option>
42
+ </select>
43
+
44
+ <br>
45
+
46
+ <!-- Button to call reports -->
47
+ <button id="reportButton">Show Evaluation Report</button>
48
+
49
+ <script>
50
+ // Model selection redirect
51
+ document.getElementById("model").addEventListener("change", function () {
52
+ const model = this.value;
53
+ if (model === "cnnkeras") {
54
+ window.location.href = "/cnnkeras";
55
+ } else if (model === "vit") {
56
+ window.location.href = "/vit";
57
+ } else if (model === "cnnlstm") {
58
+ window.location.href = "/cnnlstm";
59
+ } else if (model === "cnnrnn") {
60
+ window.location.href = "/cnn_resnet";
61
+ } else if (model === "cnnonly") {
62
+ window.location.href = "/cnn";
63
+ }else if (model === "knn") {
64
+ window.location.href = "/knn";
65
+ }else if (model === "svm") {
66
+ window.location.href = "/svm";
67
+ }else if (model === "rf") {
68
+ window.location.href = "/randomforest";
69
+ }else if (model === "lr") {
70
+ window.location.href = "/logistic_regression";
71
+ }
72
+ });
73
+
74
+ // Button to call /reports endpoint
75
+ document.getElementById("reportButton").addEventListener("click", function () {
76
+ window.location.href = "/reports";
77
+ });
78
+ </script>
79
+ </body>
80
+ </html>
templates/knn.html ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Emotion Detection via KNN</title>
5
+ <style>
6
+ body {
7
+ font-family: Arial, sans-serif;
8
+ text-align: center;
9
+ }
10
+ #video, #canvas {
11
+ border: 2px solid #444;
12
+ border-radius: 8px;
13
+ margin: 10px;
14
+ }
15
+ #result {
16
+ font-size: 18px;
17
+ font-weight: bold;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <h2>Webcam Emotion Detector</h2>
23
+
24
+ <video id="video" width="320" height="240" autoplay></video>
25
+ <canvas id="canvas" width="320" height="240" style="display:none;"></canvas>
26
+
27
+ <p id="result">Waiting for detection...</p>
28
+
29
+ <button onclick="window.location.href='http://127.0.0.1:7860/'">
30
+ ⬅ Back to Model Selection
31
+ </button>
32
+
33
+ <script>
34
+ const video = document.getElementById("video");
35
+ const canvas = document.getElementById("canvas");
36
+ const context = canvas.getContext("2d");
37
+ const resultText = document.getElementById("result");
38
+
39
+ // Start video stream
40
+ navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
41
+ video.srcObject = stream;
42
+ });
43
+
44
+ setInterval(() => {
45
+ // Draw current video frame to canvas
46
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
47
+
48
+ // Convert to blob and send to backend
49
+ canvas.toBlob(blob => {
50
+ const formData = new FormData();
51
+ formData.append("frame", blob, "frame.jpg");
52
+
53
+ fetch("http://127.0.0.1:7860/knn", {
54
+ method: "POST",
55
+ body: formData,
56
+ })
57
+ .then(response => response.json())
58
+ .then(data => {
59
+ resultText.innerHTML = `
60
+ Detected Emotion: <strong>${data.emotion}</strong><br>
61
+ `;
62
+
63
+ })
64
+ .catch(err => {
65
+ resultText.textContent = "Error: " + err.message;
66
+ });
67
+ }, "image/jpeg");
68
+ }, 300); // Every 3 seconds
69
+ </script>
70
+ </body>
71
+ </html>
templates/lr.html ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Emotion Detection via Logistic Regression</title>
5
+ <style>
6
+ body {
7
+ font-family: Arial, sans-serif;
8
+ text-align: center;
9
+ }
10
+ #video, #canvas {
11
+ border: 2px solid #444;
12
+ border-radius: 8px;
13
+ margin: 10px;
14
+ }
15
+ #result {
16
+ font-size: 18px;
17
+ font-weight: bold;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <h2>Webcam Emotion Detector</h2>
23
+
24
+ <video id="video" width="320" height="240" autoplay></video>
25
+ <canvas id="canvas" width="320" height="240" style="display:none;"></canvas>
26
+
27
+ <p id="result">Waiting for detection...</p>
28
+
29
+ <button onclick="window.location.href='http://127.0.0.1:7860/'">
30
+ ⬅ Back to Model Selection
31
+ </button>
32
+
33
+ <script>
34
+ const video = document.getElementById("video");
35
+ const canvas = document.getElementById("canvas");
36
+ const context = canvas.getContext("2d");
37
+ const resultText = document.getElementById("result");
38
+
39
+ // Start video stream
40
+ navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
41
+ video.srcObject = stream;
42
+ });
43
+
44
+ setInterval(() => {
45
+ // Draw current video frame to canvas
46
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
47
+
48
+ // Convert to blob and send to backend
49
+ canvas.toBlob(blob => {
50
+ const formData = new FormData();
51
+ formData.append("frame", blob, "frame.jpg");
52
+
53
+ fetch("http://127.0.0.1:7860/logistic_regression", {
54
+ method: "POST",
55
+ body: formData,
56
+ })
57
+ .then(response => response.json())
58
+ .then(data => {
59
+ resultText.innerHTML = `
60
+ Detected Emotion: <strong>${data.emotion}</strong><br>
61
+ `;
62
+
63
+ })
64
+ .catch(err => {
65
+ resultText.textContent = "Error: " + err.message;
66
+ });
67
+ }, "image/jpeg");
68
+ }, 300); // Every 3 seconds
69
+ </script>
70
+ </body>
71
+ </html>
templates/rf.html ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Emotion Detection via Random Forest</title>
5
+ <style>
6
+ body {
7
+ font-family: Arial, sans-serif;
8
+ text-align: center;
9
+ }
10
+ #video, #canvas {
11
+ border: 2px solid #444;
12
+ border-radius: 8px;
13
+ margin: 10px;
14
+ }
15
+ #result {
16
+ font-size: 18px;
17
+ font-weight: bold;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <h2>Webcam Emotion Detector</h2>
23
+
24
+ <video id="video" width="320" height="240" autoplay></video>
25
+ <canvas id="canvas" width="320" height="240" style="display:none;"></canvas>
26
+
27
+ <p id="result">Waiting for detection...</p>
28
+
29
+ <button onclick="window.location.href='http://127.0.0.1:7860/'">
30
+ ⬅ Back to Model Selection
31
+ </button>
32
+
33
+ <script>
34
+ const video = document.getElementById("video");
35
+ const canvas = document.getElementById("canvas");
36
+ const context = canvas.getContext("2d");
37
+ const resultText = document.getElementById("result");
38
+
39
+ // Start video stream
40
+ navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
41
+ video.srcObject = stream;
42
+ });
43
+
44
+ setInterval(() => {
45
+ // Draw current video frame to canvas
46
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
47
+
48
+ // Convert to blob and send to backend
49
+ canvas.toBlob(blob => {
50
+ const formData = new FormData();
51
+ formData.append("frame", blob, "frame.jpg");
52
+
53
+ fetch("http://127.0.0.1:7860/randomforest", {
54
+ method: "POST",
55
+ body: formData,
56
+ })
57
+ .then(response => response.json())
58
+ .then(data => {
59
+ resultText.innerHTML = `
60
+ Detected Emotion: <strong>${data.emotion}</strong><br>
61
+ `;
62
+
63
+ })
64
+ .catch(err => {
65
+ resultText.textContent = "Error: " + err.message;
66
+ });
67
+ }, "image/jpeg");
68
+ }, 300); // Every 3 seconds
69
+ </script>
70
+ </body>
71
+ </html>
templates/svm.html ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Emotion Detection via SVM</title>
5
+ <style>
6
+ body {
7
+ font-family: Arial, sans-serif;
8
+ text-align: center;
9
+ }
10
+ #video, #canvas {
11
+ border: 2px solid #444;
12
+ border-radius: 8px;
13
+ margin: 10px;
14
+ }
15
+ #result {
16
+ font-size: 18px;
17
+ font-weight: bold;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <h2>Webcam Emotion Detector</h2>
23
+
24
+ <video id="video" width="320" height="240" autoplay></video>
25
+ <canvas id="canvas" width="320" height="240" style="display:none;"></canvas>
26
+
27
+ <p id="result">Waiting for detection...</p>
28
+
29
+ <button onclick="window.location.href='http://127.0.0.1:7860/'">
30
+ ⬅ Back to Model Selection
31
+ </button>
32
+
33
+ <script>
34
+ const video = document.getElementById("video");
35
+ const canvas = document.getElementById("canvas");
36
+ const context = canvas.getContext("2d");
37
+ const resultText = document.getElementById("result");
38
+
39
+ // Start video stream
40
+ navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
41
+ video.srcObject = stream;
42
+ });
43
+
44
+ setInterval(() => {
45
+ // Draw current video frame to canvas
46
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
47
+
48
+ // Convert to blob and send to backend
49
+ canvas.toBlob(blob => {
50
+ const formData = new FormData();
51
+ formData.append("frame", blob, "frame.jpg");
52
+
53
+ fetch("http://127.0.0.1:7860/svm", {
54
+ method: "POST",
55
+ body: formData,
56
+ })
57
+ .then(response => response.json())
58
+ .then(data => {
59
+ resultText.innerHTML = `
60
+ Detected Emotion: <strong>${data.emotion}</strong><br>
61
+ `;
62
+
63
+ })
64
+ .catch(err => {
65
+ resultText.textContent = "Error: " + err.message;
66
+ });
67
+ }, "image/jpeg");
68
+ }, 300); // Every 3 seconds
69
+ </script>
70
+ </body>
71
+ </html>
templates/vit.html ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Emotion & Age Detection via ViT</title>
5
+ <style>
6
+ body {
7
+ font-family: Arial, sans-serif;
8
+ text-align: center;
9
+ }
10
+ #video, #canvas {
11
+ border: 2px solid #444;
12
+ border-radius: 8px;
13
+ margin: 10px;
14
+ }
15
+ #result {
16
+ font-size: 18px;
17
+ font-weight: bold;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <h2>Webcam Emotion & Age Detector</h2>
23
+
24
+ <video id="video" width="320" height="240" autoplay></video>
25
+ <canvas id="canvas" width="320" height="240" style="display:none;"></canvas>
26
+
27
+ <p id="result">Waiting for detection...</p>
28
+
29
+ <button onclick="window.location.href='http://127.0.0.1:7860/'">
30
+ ⬅ Back to Model Selection
31
+ </button>
32
+
33
+ <script>
34
+ const video = document.getElementById("video");
35
+ const canvas = document.getElementById("canvas");
36
+ const context = canvas.getContext("2d");
37
+ const resultText = document.getElementById("result");
38
+
39
+ // Start video stream
40
+ navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
41
+ video.srcObject = stream;
42
+ });
43
+
44
+ setInterval(() => {
45
+ // Draw current video frame to canvas
46
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
47
+
48
+ // Convert to blob and send to backend
49
+ canvas.toBlob(blob => {
50
+ const formData = new FormData();
51
+ formData.append("frame", blob, "frame.jpg");
52
+
53
+ fetch("http://127.0.0.1:7860/analyze", {
54
+ method: "POST",
55
+ body: formData,
56
+ })
57
+ .then(response => response.json())
58
+ .then(data => {
59
+ resultText.innerHTML = `
60
+ Detected Emotion: <strong>${data.emotion}</strong><br>
61
+ Estimated Age Group: <strong>${data.age}</strong>
62
+ `;
63
+
64
+ })
65
+ .catch(err => {
66
+ resultText.textContent = "Error: " + err.message;
67
+ });
68
+ }, "image/jpeg");
69
+ }, 300); // Every 3 seconds
70
+ </script>
71
+ </body>
72
+ </html>
testing.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+
2
+ import tensorflow as tf
3
+ import keras
4
+ print("TensorFlow:", tf.__version__)
5
+ print("Keras:", keras.__version__)
train_basic_ml.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Classical ML Emotion Detection with HOG, LBP, Gabor (NumPy 2.x Compatible - OpenCV Removed)
2
+
3
+ import numpy as np
4
+ from skimage.io import imread
5
+ from skimage.transform import resize
6
+ from skimage.feature import hog, local_binary_pattern
7
+ from skimage.filters import gabor
8
+ from sklearn.model_selection import train_test_split
9
+ from sklearn.svm import SVC
10
+ from sklearn.ensemble import RandomForestClassifier
11
+ from sklearn.neighbors import KNeighborsClassifier
12
+ from sklearn.linear_model import LogisticRegression
13
+ from sklearn.metrics import classification_report
14
+ import os
15
+ from tqdm import tqdm
16
+ import joblib # Import joblib for saving models
17
+
18
+ # ----------------------
19
+ # Configuration
20
+ # ----------------------
21
+ IMG_SIZE = 48
22
+ DATASET_PATH = "./train" # Folder structure: ./dataset/<label>/<image>.jpg
23
+ EMOTIONS = ['angry', 'disgust', 'fear', 'happy', 'neutral', 'sad', 'surprise']
24
+
25
+ # ----------------------
26
+ # Feature Extraction Functions
27
+ # ----------------------
28
+ def extract_hog(img):
29
+ return hog(img, pixels_per_cell=(8, 8), cells_per_block=(2, 2))
30
+
31
+ def extract_lbp(img):
32
+ lbp = local_binary_pattern(img, P=8, R=1, method="uniform")
33
+ (hist, _) = np.histogram(lbp.ravel(), bins=np.arange(0, 59))
34
+ hist = hist.astype("float")
35
+ hist /= (hist.sum() + 1e-6)
36
+ return hist
37
+
38
+ def extract_gabor(img):
39
+ filt_real, _ = gabor(img, frequency=0.6)
40
+ return filt_real.ravel()[::64] # downsample to reduce dimensionality
41
+
42
+ # ----------------------
43
+ # Load Dataset and Extract Features
44
+ # ----------------------
45
+ def load_dataset():
46
+ data = []
47
+ labels = []
48
+ print("[INFO] Loading dataset and extracting features...")
49
+
50
+ for label in EMOTIONS:
51
+ folder = os.path.join(DATASET_PATH, label)
52
+ if not os.path.exists(folder):
53
+ continue
54
+ for file in tqdm(os.listdir(folder), desc=label):
55
+ path = os.path.join(folder, file)
56
+ try:
57
+ img = imread(path, as_gray=True)
58
+ img = resize(img, (IMG_SIZE, IMG_SIZE), anti_aliasing=True)
59
+ except Exception as e:
60
+ print(f"[WARNING] Skipped {file}: {e}")
61
+ continue
62
+
63
+ feat = []
64
+ feat.extend(extract_hog(img))
65
+ feat.extend(extract_lbp(img))
66
+ feat.extend(extract_gabor(img))
67
+
68
+ data.append(feat)
69
+ labels.append(EMOTIONS.index(label))
70
+
71
+ return np.array(data), np.array(labels)
72
+
73
+ # ----------------------
74
+ # Train & Evaluate Models
75
+ # ----------------------
76
+ def train_and_evaluate(X, y):
77
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
78
+
79
+ classifiers = {
80
+ "SVM": SVC(kernel='linear'),
81
+ "Random Forest": RandomForestClassifier(n_estimators=100),
82
+ "k-NN": KNeighborsClassifier(n_neighbors=5),
83
+ "Logistic Regression": LogisticRegression(max_iter=1000)
84
+ }
85
+
86
+ for name, clf in classifiers.items():
87
+ print(f"\n[INFO] Training {name}...")
88
+ clf.fit(X_train, y_train)
89
+ preds = clf.predict(X_test)
90
+ print(f"\n[RESULTS] {name} Classification Report")
91
+ print(classification_report(y_test, preds, target_names=EMOTIONS))
92
+ # Save the trained model using joblib
93
+ model_filename = f'{name.lower().replace(" ", "_")}_model.joblib'
94
+ print(f"[INFO] Saving {name} model to {model_filename}...")
95
+ joblib.dump(clf, model_filename)
96
+
97
+ # ----------------------
98
+ # Run Full Pipeline
99
+ # ----------------------
100
+ if __name__ == "__main__":
101
+ X, y = load_dataset()
102
+ train_and_evaluate(X, y)
train_cnn_rnn_fer2013.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import tensorflow as tf
3
+ from tensorflow.keras.models import Sequential
4
+ from tensorflow.keras.layers import Conv2D, MaxPooling2D, TimeDistributed, Flatten, Dense, Dropout, SimpleRNN
5
+ from tensorflow.keras.preprocessing.image import ImageDataGenerator
6
+ import os
7
+
8
+ # Constants
9
+ img_height, img_width = 48, 48
10
+ channels = 1
11
+ batch_size = 64
12
+ time_steps = 6
13
+ chunk_size = 8 # 6 chunks of width 8 to match 48 width
14
+
15
+ # Paths
16
+ train_dir = 'train'
17
+ test_dir = 'test'
18
+
19
+ # Image Generators (no augmentation for now)
20
+ train_datagen = ImageDataGenerator(rescale=1./255)
21
+ test_datagen = ImageDataGenerator(rescale=1./255)
22
+
23
+ # Load images from directories
24
+ train_generator = train_datagen.flow_from_directory(
25
+ train_dir,
26
+ target_size=(img_height, img_width),
27
+ color_mode='grayscale',
28
+ class_mode='categorical',
29
+ batch_size=batch_size,
30
+ shuffle=True
31
+ )
32
+
33
+ test_generator = test_datagen.flow_from_directory(
34
+ test_dir,
35
+ target_size=(img_height, img_width),
36
+ color_mode='grayscale',
37
+ class_mode='categorical',
38
+ batch_size=batch_size,
39
+ shuffle=False
40
+ )
41
+
42
+ # Helper: Reshape batches to (samples, time_steps, height, chunk_size, 1)
43
+ def reshape_batch(batch_x):
44
+ return batch_x.reshape(-1, time_steps, img_height, chunk_size, 1)
45
+
46
+ # Build CNN + RNN Model
47
+ model = Sequential()
48
+ model.add(TimeDistributed(Conv2D(32, (3, 3), activation='relu'), input_shape=(time_steps, img_height, chunk_size, 1)))
49
+ model.add(TimeDistributed(MaxPooling2D(pool_size=(2, 2))))
50
+ model.add(TimeDistributed(Flatten()))
51
+ model.add(SimpleRNN(64))
52
+ model.add(Dense(128, activation='relu'))
53
+ model.add(Dropout(0.5))
54
+ model.add(Dense(train_generator.num_classes, activation='softmax'))
55
+
56
+ model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
57
+ model.summary()
58
+
59
+ # Custom training loop to reshape batches for CNN + RNN
60
+ epochs = 10
61
+ steps_per_epoch = train_generator.samples // batch_size
62
+ validation_steps = test_generator.samples // batch_size
63
+
64
+ for epoch in range(epochs):
65
+ print(f"\nEpoch {epoch+1}/{epochs}")
66
+ for step in range(steps_per_epoch):
67
+ batch_x, batch_y = next(train_generator)
68
+ batch_x_rnn = reshape_batch(batch_x)
69
+ model.train_on_batch(batch_x_rnn, batch_y)
70
+
71
+ # Validation
72
+ val_accuracy = []
73
+ val_loss = []
74
+ for _ in range(validation_steps):
75
+ val_x, val_y = next(test_generator)
76
+ val_x_rnn = reshape_batch(val_x)
77
+ loss, acc = model.evaluate(val_x_rnn, val_y, verbose=0)
78
+ val_accuracy.append(acc)
79
+ val_loss.append(loss)
80
+ print(f"Validation Loss: {np.mean(val_loss):.4f}, Accuracy: {np.mean(val_accuracy):.4f}")
81
+
82
+ # Save model
83
+ model.save("cnn_rnn_model_from_dir.h5")