Showing
22 changed files
with
780 additions
and
0 deletions
.gitignore
0 → 100644
model/user.js
0 → 100644
1 | +const { json } = require('express/lib/response'); | ||
2 | +const mongoose = require('mongoose'); | ||
3 | + | ||
4 | +const userSchema = new mongoose.Schema({ | ||
5 | + name : {type : String, required : true, unique : true, }, | ||
6 | + password : {type : String, required : true, trim : true}, | ||
7 | + total_squart : {type : Number, default : 0}, | ||
8 | + today_squart : {type : Number, default : 0}, | ||
9 | + | ||
10 | +}); | ||
11 | + | ||
12 | +userSchema.methods.passwordCheck = function(password, cb) { | ||
13 | + if (password === this.password) | ||
14 | + cb(null, isMatch); | ||
15 | +} | ||
16 | + | ||
17 | + | ||
18 | + | ||
19 | +const User = mongoose.model('squartuser', userSchema ) | ||
20 | +module.exports = {User}; | ||
21 | + | ||
22 | + |
package.json
0 → 100644
1 | +{ | ||
2 | + "name": "healthcare-with-webcam", | ||
3 | + "version": "1.0.0", | ||
4 | + "description": "", | ||
5 | + "main": "server.js", | ||
6 | + "scripts": { | ||
7 | + "test": "echo \"Error: no test specified\" && exit 1", | ||
8 | + "start": "node server.js" | ||
9 | + }, | ||
10 | + "repository": { | ||
11 | + "type": "git", | ||
12 | + "url": "http://khuhub.khu.ac.kr/2019102144/healthcare-with-webcam.git" | ||
13 | + }, | ||
14 | + "author": "", | ||
15 | + "license": "ISC", | ||
16 | + "dependencies": { | ||
17 | + "express": "^4.18.1", | ||
18 | + "express-session": "^1.17.3", | ||
19 | + "mongoose": "^6.3.4", | ||
20 | + "mongoose-session": "0.0.4", | ||
21 | + "ejs": "^3.1.8" | ||
22 | + } | ||
23 | +} |
server.js
0 → 100644
1 | +const express = require('express'); | ||
2 | + | ||
3 | +const ejs = require('ejs') | ||
4 | + | ||
5 | +const app = express(); | ||
6 | + | ||
7 | + | ||
8 | +const port = 3000 | ||
9 | + | ||
10 | + | ||
11 | +app.set('port', port) | ||
12 | +app.set('view engine', 'ejs') | ||
13 | +app.engine('html',ejs.renderFile ) | ||
14 | + | ||
15 | +// model/user.js | ||
16 | +const { User } = require('./model/User'); | ||
17 | +const mongoose = require('mongoose'); | ||
18 | +// db 연결을 위한 키 값 , 보안을 위해 최종 마스터 브런치에는 포함하지 않을 예정. | ||
19 | +mongoose.connect('mongodb+srv://kongtae:ksas9825!%40@squartusers.e2ddc.mongodb.net/?retryWrites=true&w=majority') | ||
20 | +.then(() => console.log('MongoDB connect!')) | ||
21 | +.catch(err => console.log(err)) | ||
22 | + | ||
23 | +// 로그인 세션 : 로그인 정보 유지. | ||
24 | +const express_session = require('express-session') | ||
25 | +app.use(express_session({ | ||
26 | + secret : "@secret@number", // 암호화 키 | ||
27 | + resave : false, | ||
28 | + saveUninitialized : false, | ||
29 | + store:require('mongoose-session')(mongoose), | ||
30 | + cookie : {maxAge : 60*60*24} | ||
31 | +})) | ||
32 | + | ||
33 | + | ||
34 | +app.get('/', (req,res) => { | ||
35 | + console.log(req.session) | ||
36 | + if (req.session.user) | ||
37 | + { | ||
38 | + app.set('views', __dirname + '/views/squatPage') | ||
39 | + res.render('squat.html') | ||
40 | + } | ||
41 | + else | ||
42 | + { | ||
43 | + app.set('views', __dirname + '/views/mainPage') | ||
44 | + res.render('main.html') | ||
45 | + } | ||
46 | + | ||
47 | +}) | ||
48 | + | ||
49 | +// 음성 소리 파일 전송 | ||
50 | +app.get('/sound/0.wav', (req,res) => { | ||
51 | + res.sendFile(__dirname + '/views/squatPage/sound/0.wav') | ||
52 | +}) | ||
53 | +app.get('/sound/1.wav', (req,res) => { | ||
54 | + res.sendFile(__dirname + '/views/squatPage/sound/1.wav') | ||
55 | +}) | ||
56 | +app.get('/sound/2.wav', (req,res) => { | ||
57 | + res.sendFile(__dirname + '/views/squatPage/sound/2.wav') | ||
58 | +}) | ||
59 | +app.get('/sound/3.wav', (req,res) => { | ||
60 | + res.sendFile(__dirname + '/views/squatPage/sound/3.wav') | ||
61 | +}) | ||
62 | +app.get('/sound/4.wav', (req,res) => { | ||
63 | + res.sendFile(__dirname + '/views/squatPage/sound/4.wav') | ||
64 | +}) | ||
65 | +app.get('/sound/5.wav', (req,res) => { | ||
66 | + res.sendFile(__dirname + '/views/squatPage/sound/5.wav') | ||
67 | +}) | ||
68 | +app.get('/sound/6.wav', (req,res) => { | ||
69 | + res.sendFile(__dirname + '/views/squatPage/sound/6.wav') | ||
70 | +}) | ||
71 | +app.get('/sound/7.wav', (req,res) => { | ||
72 | + res.sendFile(__dirname + '/views/squatPage/sound/7.wav') | ||
73 | +}) | ||
74 | +app.get('/sound/8.wav', (req,res) => { | ||
75 | + res.sendFile(__dirname + '/views/squatPage/sound/8.wav') | ||
76 | +}) | ||
77 | +app.get('/sound/9.wav', (req,res) => { | ||
78 | + res.sendFile(__dirname + '/views/squatPage/sound/9.wav') | ||
79 | +}) | ||
80 | +app.get('/sound/bad.mp3', (req,res) => { | ||
81 | + res.sendFile(__dirname + '/views/squatPage/sound/bad.mp3') | ||
82 | +}) | ||
83 | + | ||
84 | + | ||
85 | + | ||
86 | +// | ||
87 | +// js 파일 전송. | ||
88 | +app.get('/main.js', (req,res) => { | ||
89 | + res.sendFile( __dirname + '/views/mainPage/main.js') | ||
90 | +}) | ||
91 | +// css 파일 전송 | ||
92 | +app.get('/main.css', (req,res) => { | ||
93 | + res.sendFile(__dirname + '/views/mainPage/main.css') | ||
94 | +}) | ||
95 | +// js 파일 전송. | ||
96 | +app.get('/squat.js', (req,res) => { | ||
97 | + res.sendFile( __dirname + '/views/squatPage/squat.js') | ||
98 | +}) | ||
99 | +// css 파일 전송 | ||
100 | +app.get('/squat.css', (req,res) => { | ||
101 | + res.sendFile(__dirname + '/views/squatPage/squat.css') | ||
102 | +}) | ||
103 | + | ||
104 | +app.get('/squat', (req,res) => { | ||
105 | + if (req.session.user) | ||
106 | + { | ||
107 | + app.set('views', __dirname + '/views/squatPage') | ||
108 | + res.render('squat.html') | ||
109 | + } | ||
110 | + else | ||
111 | + { // 로그인 안되어 있으면, 스쿼트 페이지 진입 불가. | ||
112 | + app.set('views', __dirname + '/views/mainPage') | ||
113 | + res.render('main.html') | ||
114 | + } | ||
115 | +}) | ||
116 | + | ||
117 | +app.listen(port, () => { | ||
118 | + console.log(`Listening on ${port} port`); | ||
119 | +}) | ||
120 | + | ||
121 | + | ||
122 | + | ||
123 | +// 유저 등록 및 로그인 API | ||
124 | + | ||
125 | + | ||
126 | + | ||
127 | + | ||
128 | +// 등록 . | ||
129 | +app.use(express.json()) | ||
130 | +app.post('/api/users/register', (req,res) => { | ||
131 | + console.log(req.body) | ||
132 | + const new_user = new User(req.body); | ||
133 | + new_user.save((err, userInfo) => { | ||
134 | + if (err) | ||
135 | + { | ||
136 | + var result = res.json({success : false, err}) | ||
137 | + return result | ||
138 | + } | ||
139 | + else | ||
140 | + { | ||
141 | + var result = res.status(200).json({success : true}) | ||
142 | + return result | ||
143 | + } | ||
144 | + }) | ||
145 | +}) | ||
146 | + | ||
147 | +// 로그인 . | ||
148 | +app.post('/api/users/login', (req ,res) => { | ||
149 | + console.log(req.body) | ||
150 | + User.findOne({name : req.body.name}, (err, user) => { | ||
151 | + if (!user) { | ||
152 | + return res.json({ | ||
153 | + loginSuccess: false, | ||
154 | + message : "이름이 일치하는 사용자가 없습니다 !", | ||
155 | + }) | ||
156 | + } | ||
157 | + else if (req.body.password === user.password) { | ||
158 | + req.session.user = { | ||
159 | + user_name : req.body.name, | ||
160 | + user_password : req.body.password, | ||
161 | + } | ||
162 | + req.session.save() | ||
163 | + | ||
164 | + console.log(req.session.user) | ||
165 | + return res.json({ | ||
166 | + loginSuccess : true, | ||
167 | + }) | ||
168 | + } | ||
169 | + else { | ||
170 | + return res.json({ | ||
171 | + loginSuccess : false, | ||
172 | + message : "비밀번호가 일치하지 않습니다 !" | ||
173 | + }) | ||
174 | + } | ||
175 | + }) | ||
176 | +}) | ||
177 | + | ||
178 | +// 로그아웃 | ||
179 | +app.get('/api/users/logout', (req,res) => { | ||
180 | + var session = req.session | ||
181 | + if (session.user) | ||
182 | + { | ||
183 | + req.session.destroy(err => { | ||
184 | + if (err) { | ||
185 | + console.log(err) | ||
186 | + return res.json({ | ||
187 | + logoutSuccess : false | ||
188 | + }) | ||
189 | + } | ||
190 | + else | ||
191 | + { | ||
192 | + console.log('로그아웃 완료') | ||
193 | + return res.json({ | ||
194 | + logoutSuccess : true | ||
195 | + }) | ||
196 | + } | ||
197 | + }) | ||
198 | + // res.redirect('/'); | ||
199 | + } | ||
200 | + else | ||
201 | + { | ||
202 | + console.log('로그인이 되어있지 않습니다.') | ||
203 | + return res.json({ | ||
204 | + logoutSuccess : true, | ||
205 | + }) | ||
206 | + } | ||
207 | + | ||
208 | +}) | ||
209 | + | ||
210 | +app.get('/api/users/name', (req,res) => { | ||
211 | + return res.json({ | ||
212 | + user : req.session.user | ||
213 | + }) | ||
214 | +}) | ||
215 | + | ||
216 | + | ||
217 | +// 스쿼트 갯수 업데이트 API | ||
218 | +app.post('/api/users/countupdate', (req,res) => { | ||
219 | + var userName = req.body.name | ||
220 | + var userCount = req.body.count | ||
221 | + User.findOne({name : userName}, (err,userInfo) => { | ||
222 | + | ||
223 | + if (err) res.json({success : false, err}) | ||
224 | + | ||
225 | + userInfo.today_squart = Number(userInfo.today_squart) + Number(userCount) | ||
226 | + userInfo.total_squart = Number(userInfo.total_squart) + Number(userCount) | ||
227 | + userInfo.save() | ||
228 | + | ||
229 | + | ||
230 | + return res.json({ | ||
231 | + success : true, | ||
232 | + today_squart : userInfo.today_squart, | ||
233 | + total_squart : userInfo.total_squart | ||
234 | + }) | ||
235 | + | ||
236 | + }) | ||
237 | +}) | ||
238 | + | ||
239 | + | ||
240 | +// 세션 저장 확인 | ||
241 | +app.get('/api/session', (req,res) => { | ||
242 | + console.log(req.session.user) | ||
243 | + return res.json({session :req.session}) | ||
244 | +}) |
views/mainPage/main.css
0 → 100644
1 | +@import url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&display=swap'); | ||
2 | +@import url("https://fonts.googleapis.com/css?family=Poppins:200,300,400,500,600,700,800,900&display=swap"); | ||
3 | +@font-face { | ||
4 | + font-family: 'BMJUA'; | ||
5 | + src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_one@1.0/BMJUA.woff') format('woff'); | ||
6 | + font-weight: normal; | ||
7 | + font-style: normal; | ||
8 | +} | ||
9 | + | ||
10 | +@font-face { | ||
11 | + font-family: 'BMEULJIRO'; | ||
12 | + src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_twelve@1.0/BMEULJIRO.woff') format('woff'); | ||
13 | + font-weight: normal; | ||
14 | + font-style: normal; | ||
15 | +} | ||
16 | + | ||
17 | +.title { | ||
18 | + font-size : 40px; | ||
19 | + font-family: 'BMEULJIRO'; | ||
20 | + color : aliceblue; | ||
21 | + margin-top: 10%; | ||
22 | + margin-bottom: 20px; | ||
23 | + text-align: center; | ||
24 | +} | ||
25 | + | ||
26 | +button { | ||
27 | + margin: 10px; | ||
28 | + margin-bottom: 30px; | ||
29 | +} | ||
30 | + | ||
31 | +input { | ||
32 | + margin-top: 0px; | ||
33 | + width: 300px; | ||
34 | + height: 50px; | ||
35 | + border: 1px solid aliceblue; | ||
36 | + border-radius: 10px; | ||
37 | + color: white; | ||
38 | + background-color: #353535; | ||
39 | + font-family: 'BMJUA'; | ||
40 | + font-size: 20px; | ||
41 | + margin-block-end: 10px; | ||
42 | +} | ||
43 | + | ||
44 | +.w-btn { | ||
45 | + position: relative; | ||
46 | + border: none; | ||
47 | + display: inline-block; | ||
48 | + padding: 15px 30px; | ||
49 | + border-radius: 15px; | ||
50 | + font-family: "BMEULJIRO", sans-serif; | ||
51 | + box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2); | ||
52 | + text-decoration: none; | ||
53 | + font-weight: 600; | ||
54 | + transition: 0.25s; | ||
55 | + margin-bottom: 20px; | ||
56 | +} | ||
57 | + | ||
58 | +.w-btn-outline { | ||
59 | + position: relative; | ||
60 | + padding: 15px 30px; | ||
61 | + border-radius: 15px; | ||
62 | + font-family: "BMEULJIRO", sans-serif; | ||
63 | + box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2); | ||
64 | + text-decoration: none; | ||
65 | + font-weight: 600; | ||
66 | + transition: 0.25s; | ||
67 | +} | ||
68 | + | ||
69 | +.w-btn-indigo { | ||
70 | + background-color: aliceblue; | ||
71 | + color: #2e2f30; | ||
72 | +} | ||
73 | + | ||
74 | +.w-btn-indigo-outline { | ||
75 | + border: 3px solid aliceblue; | ||
76 | + color: #2e2f30; | ||
77 | +} | ||
78 | + | ||
79 | +.w-btn-indigo-outline:hover { | ||
80 | + color: #2e2f30; | ||
81 | + background: aliceblue; | ||
82 | +} | ||
83 | + | ||
84 | +.font { | ||
85 | + margin-right: 3px; | ||
86 | + font-size: 20px; | ||
87 | + color:aliceblue; | ||
88 | + font-family: "BMEULJIRO"; | ||
89 | +} | ||
90 | + | ||
91 | +.login_sign { | ||
92 | + margin-top: 10px; | ||
93 | + font-family: "BMJUA"; | ||
94 | + font-size: 20px; | ||
95 | + box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2); | ||
96 | + width: 310px; | ||
97 | + height: 50px; | ||
98 | + color:black; | ||
99 | + border-radius: 10px; | ||
100 | + background-color: wheat; | ||
101 | + border: none; | ||
102 | +} | ||
103 | + | ||
104 | +.flex-container{ | ||
105 | + display:inline-flex; | ||
106 | +} | ||
107 | + | ||
108 | +.flex-item{ | ||
109 | + width: 350px; | ||
110 | + height:400px; | ||
111 | + margin: 10px; | ||
112 | + | ||
113 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
views/mainPage/main.html
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html lang="ko"> | ||
3 | +<head> | ||
4 | + <meta charset="UTF-8"> | ||
5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
7 | + <title>Main Page</title> | ||
8 | + <link rel="stylesheet" href="main.css"> | ||
9 | + <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> | ||
10 | +</head> | ||
11 | +<body bgcolor= "#353535"> | ||
12 | + <div class="title"> | ||
13 | + 🏋️♂️방구석 헬스 트레이너🏋️♀️ | ||
14 | + </div> | ||
15 | + <center> | ||
16 | + <div> | ||
17 | + <input class="w-btn w-btn-indigo" type="button" value = "Squat Start" id = "moveSquart" onclick="moveSquartPage()"/> | ||
18 | + </div> | ||
19 | + | ||
20 | + <script type="text/javascript" src="./main.js"></script> | ||
21 | + <div class="flex-container"> | ||
22 | + <div class="flex-item"> | ||
23 | + <div><input class="input" id = "name" value="👨🦲" type = "text"></div> | ||
24 | + <div><input class="input" id = "password" value="🔑" type = "text"></div> | ||
25 | + <div><button class="login_sign" type="submit" id = "button">로그인</button></div> | ||
26 | + </div> | ||
27 | + <div class="flex-item"> | ||
28 | + <div><input class="input" id = "register_name" value="👨🦲" type = "text"></div> | ||
29 | + <div><input class="input" id = "register_password" value="🔑" type = "text"></div> | ||
30 | + <div><button class="login_sign" type="submit" id = "register_button">회원가입</button></div> | ||
31 | + </div> | ||
32 | + </div> | ||
33 | + </center> | ||
34 | +</body> | ||
35 | +</html> | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
views/mainPage/main.js
0 → 100644
1 | +function moveSquatPage() | ||
2 | +{ | ||
3 | + location.href = "/squat"; | ||
4 | +} | ||
5 | + | ||
6 | +$(document).ready(function(){ | ||
7 | + $('#button').click(function(){ | ||
8 | + let sendName = $('#name').val(); | ||
9 | + let sendPassword = $('#password').val(); | ||
10 | + | ||
11 | + let sendData = { | ||
12 | + "name" : sendName, | ||
13 | + "password" : sendPassword | ||
14 | + } | ||
15 | + | ||
16 | + $.ajax({ | ||
17 | + contentType : "application/json; charset=utf-8", | ||
18 | + type : 'post', | ||
19 | + url : 'api/users/login', | ||
20 | + data : JSON.stringify(sendData), | ||
21 | + dataType : 'JSON', | ||
22 | + | ||
23 | + success : function(datas) { | ||
24 | + if (datas.loginSuccess) | ||
25 | + { | ||
26 | + moveSquatPage() | ||
27 | + } | ||
28 | + else | ||
29 | + { | ||
30 | + alert(datas.message) | ||
31 | + } | ||
32 | + } | ||
33 | + }) | ||
34 | + }) | ||
35 | +}) | ||
36 | +// 회원가입용 | ||
37 | +$(document).ready(function(){ | ||
38 | + $('#register_button').click(function(){ | ||
39 | + let sendName = $('#register_name').val(); | ||
40 | + let sendPassword = $('#register_password').val(); | ||
41 | + | ||
42 | + let sendData = { | ||
43 | + "name" : sendName, | ||
44 | + "password" : sendPassword | ||
45 | + } | ||
46 | + | ||
47 | + $.ajax({ | ||
48 | + contentType : "application/json; charset=utf-8", | ||
49 | + type : 'post', | ||
50 | + url : 'api/users/register', | ||
51 | + data : JSON.stringify(sendData), | ||
52 | + dataType : 'JSON', | ||
53 | + | ||
54 | + success : function(datas) { | ||
55 | + if (datas.success) | ||
56 | + { | ||
57 | + moveSquatPage() | ||
58 | + } | ||
59 | + else | ||
60 | + { | ||
61 | + alert("회원가입 실패 (이름 중복 의심)") | ||
62 | + } | ||
63 | + } | ||
64 | + }) | ||
65 | + }) | ||
66 | +}) |
views/squatPage/sound/0.wav
0 → 100644
No preview for this file type
views/squatPage/sound/1.wav
0 → 100644
No preview for this file type
views/squatPage/sound/2.wav
0 → 100644
No preview for this file type
views/squatPage/sound/3.wav
0 → 100644
No preview for this file type
views/squatPage/sound/4.wav
0 → 100644
No preview for this file type
views/squatPage/sound/5.wav
0 → 100644
No preview for this file type
views/squatPage/sound/6.wav
0 → 100644
No preview for this file type
views/squatPage/sound/7.wav
0 → 100644
No preview for this file type
views/squatPage/sound/8.wav
0 → 100644
No preview for this file type
views/squatPage/sound/9.wav
0 → 100644
No preview for this file type
views/squatPage/sound/bad.mp3
0 → 100644
No preview for this file type
views/squatPage/squat.css
0 → 100644
1 | +@import url("https://fonts.googleapis.com/css?family=Poppins:200,300,400,500,600,700,800,900&display=swap"); | ||
2 | + | ||
3 | +button { | ||
4 | + margin: 10px; | ||
5 | + margin-bottom: 20px; | ||
6 | +} | ||
7 | + | ||
8 | +.w-btn { | ||
9 | + position: relative; | ||
10 | + border: none; | ||
11 | + display: inline-block; | ||
12 | + padding: 15px 30px; | ||
13 | + border-radius: 15px; | ||
14 | + font-family: "paybooc-Light", sans-serif; | ||
15 | + box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2); | ||
16 | + text-decoration: none; | ||
17 | + font-weight: 600; | ||
18 | + transition: 0.25s; | ||
19 | +} | ||
20 | + | ||
21 | +.w-btn-outline { | ||
22 | + position: relative; | ||
23 | + padding: 15px 30px; | ||
24 | + border-radius: 15px; | ||
25 | + font-family: "paybooc-Light", sans-serif; | ||
26 | + box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2); | ||
27 | + text-decoration: none; | ||
28 | + font-weight: 600; | ||
29 | + transition: 0.25s; | ||
30 | +} | ||
31 | + | ||
32 | +.w-btn-indigo { | ||
33 | + background-color: aliceblue; | ||
34 | + color: #2e2f30; | ||
35 | +} | ||
36 | + | ||
37 | +.w-btn-indigo-outline { | ||
38 | + border: 3px solid aliceblue; | ||
39 | + color: #2e2f30; | ||
40 | +} | ||
41 | + | ||
42 | +.w-btn-indigo-outline:hover { | ||
43 | + color: #2e2f30; | ||
44 | + background: aliceblue; | ||
45 | +} | ||
46 | + | ||
47 | +.Title { | ||
48 | + font-size : 100px; | ||
49 | + font-family: fantasy; | ||
50 | + color : aliceblue; | ||
51 | + margin-top: 15%; | ||
52 | + text-align: center; | ||
53 | +} | ||
54 | + | ||
55 | +.click_title { | ||
56 | + font-size : 40px; | ||
57 | + font-family: fantasy; | ||
58 | + color : aliceblue; | ||
59 | + margin-top: 40px; | ||
60 | + margin-bottom: 20px; | ||
61 | + text-align: center; | ||
62 | + transition: 500ms; | ||
63 | +} | ||
64 | + | ||
65 | +.label-container { | ||
66 | + visibility: hidden; | ||
67 | +} | ||
68 | + | ||
69 | +.hidden { | ||
70 | + visibility: hidden; | ||
71 | +} | ||
72 | + | ||
73 | +.visible { | ||
74 | + visibility: visible; | ||
75 | +} | ||
76 | + | ||
77 | +.circle { | ||
78 | + margin: 20px; | ||
79 | + width: 100px; | ||
80 | + height: 100px; | ||
81 | + background-color: aliceblue; | ||
82 | + color: #2e2f30; | ||
83 | + text-align: center; | ||
84 | + border-radius: 50%; | ||
85 | + line-height: 100px; | ||
86 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
views/squatPage/squat.html
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html lang="ko"> | ||
3 | + <head> | ||
4 | + <meta charset="UTF-8"> | ||
5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
7 | + <link rel="stylesheet" href="squat.css"> | ||
8 | + <title>Squat Page</title> | ||
9 | + <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> | ||
10 | + </head> | ||
11 | + <body bgcolor= "#353535"> | ||
12 | + <div id="title" class="Title"> | ||
13 | + Squat Page | ||
14 | + | ||
15 | + <center> | ||
16 | + <button class="w-btn w-btn-indigo" type="button" onclick="init()">WEBCAM START</button> | ||
17 | + <button id = "savecount" class="w-btn w-btn-indigo" type="button" onclick="">스쿼트 횟수 저장</button> | ||
18 | + <div> | ||
19 | + <canvas id="canvas"></canvas> | ||
20 | + <iframe | ||
21 | + id="youtube" | ||
22 | + class="hidden" | ||
23 | + width="560" height="400" | ||
24 | + src="https://www.youtube.com/embed/9WhpAVOSyl8?start=22" | ||
25 | + title="YouTube video player" | ||
26 | + frameborder="0" | ||
27 | + allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen> | ||
28 | + </iframe> | ||
29 | + </div> | ||
30 | + <div id="counter" class="circle"></div> | ||
31 | + </center> | ||
32 | + </div> | ||
33 | + <div id="label-container" class="label-container"></div> | ||
34 | + | ||
35 | + <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.3.1/dist/tf.min.js"></script> | ||
36 | + <script src="https://cdn.jsdelivr.net/npm/@teachablemachine/pose@0.8/dist/teachablemachine-pose.min.js"></script> | ||
37 | + <script src="./squat.js"></script> | ||
38 | + </body> | ||
39 | +</html> |
views/squatPage/squat.js
0 → 100644
1 | +// More API functions here: | ||
2 | +// https://github.com/googlecreativelab/teachablemachine-community/tree/master/libraries/pose | ||
3 | + | ||
4 | +// the link to your model provided by Teachable Machine export panel | ||
5 | +const URL = "https://teachablemachine.withgoogle.com/models/xymjZj4q-/"; // 임시 URI - stand , squart, bent(허리 굽은 자세) 학습. | ||
6 | +let model, webcam, ctx, labelContainer, maxPredictions; | ||
7 | + | ||
8 | + | ||
9 | +// 갯수 count | ||
10 | +let count = 0; | ||
11 | +var counter = document.getElementById("counter"); | ||
12 | +counter.textContent = count; | ||
13 | +counter.className = "hidden"; | ||
14 | + | ||
15 | +async function init() { | ||
16 | + const modelURL = URL + "model.json"; | ||
17 | + const metadataURL = URL + "metadata.json"; | ||
18 | + | ||
19 | + var target = document.getElementById("youtube"); | ||
20 | + target.className = "visible"; | ||
21 | + | ||
22 | + var target2 = document.getElementById("title"); | ||
23 | + target2.className = "click_title"; | ||
24 | + | ||
25 | + counter.className = "circle"; | ||
26 | + | ||
27 | + // load the model and metadata | ||
28 | + // Refer to tmImage.loadFromFiles() in the API to support files from a file picker | ||
29 | + // Note: the pose library adds a tmPose object to your window (window.tmPose) | ||
30 | + model = await tmPose.load(modelURL, metadataURL); | ||
31 | + maxPredictions = model.getTotalClasses(); | ||
32 | + // Convenience function to setup a webcam | ||
33 | + const size = 400; | ||
34 | + const flip = true; // whether to flip the webcam | ||
35 | + webcam = new tmPose.Webcam(size, size, flip); // width, height, flip | ||
36 | + await webcam.setup(); // request access to the webcam | ||
37 | + await webcam.play(); | ||
38 | + webcam.style | ||
39 | + window.requestAnimationFrame(loop); | ||
40 | + // append/get elements to the DOM | ||
41 | + const canvas = document.getElementById("canvas"); | ||
42 | + canvas.width = size; canvas.height = size; | ||
43 | + ctx = canvas.getContext("2d"); | ||
44 | + labelContainer = document.getElementById("label-container"); | ||
45 | + for (let i = 0; i < maxPredictions; i++) { // and class labels | ||
46 | + labelContainer.appendChild(document.createElement("div")); | ||
47 | + } | ||
48 | +} | ||
49 | + | ||
50 | +async function loop(timestamp) { | ||
51 | + webcam.update(); // update the webcam frame | ||
52 | + await predict(); | ||
53 | + window.requestAnimationFrame(loop); | ||
54 | +} | ||
55 | + | ||
56 | +async function predict() { | ||
57 | + // Prediction #1: run input through posenet | ||
58 | + // estimatePose can take in an image, video or canvas html element | ||
59 | + const { pose, posenetOutput } = await model.estimatePose(webcam.canvas); | ||
60 | + // Prediction 2: run input through teachable machine classification model | ||
61 | + const prediction = await model.predict(posenetOutput); | ||
62 | + let status = "stand" | ||
63 | + if (prediction[0].probability.toFixed(2) > 0.9) { // 서있는 상태 | ||
64 | + if (status == "squat"){ // 전에 스쿼트 상태였다면, 일어날 때 카운트를 하나 올려줘야 함. | ||
65 | + count++; | ||
66 | + var audio = new Audio('./sound/' + count%10 + '.wav'); | ||
67 | + audio.play(); | ||
68 | + counter.textContent = count; | ||
69 | + console.log(count); | ||
70 | + } | ||
71 | + status = "stand" | ||
72 | + } else if (prediction[1].probability.toFixed(2) == 1.00) { // 스쿼트 자세 | ||
73 | + status = "squat" | ||
74 | + } else if (prediction[2].probability.toFixed(2) == 1.00) { // 굽은 자세(잘못된 케이스) | ||
75 | + if (status == "squart" || status == "stand") { // 굽은 자세로 잘못 수행하면, 소리 나도록 | ||
76 | + var audio = new Audio('./sound/bad.mp3'); | ||
77 | + audio.play(); | ||
78 | + } | ||
79 | + status = "bent" | ||
80 | + } | ||
81 | + | ||
82 | + for (let i = 0; i < maxPredictions; i++) { | ||
83 | + const classPrediction = | ||
84 | + prediction[i].className + ": " + prediction[i].probability.toFixed(2); | ||
85 | + labelContainer.childNodes[i].innerHTML = classPrediction; | ||
86 | + } | ||
87 | + | ||
88 | + // finally draw the poses | ||
89 | + drawPose(pose); | ||
90 | +} | ||
91 | + | ||
92 | +function drawPose(pose) { | ||
93 | + if (webcam.canvas) { | ||
94 | + ctx.drawImage(webcam.canvas, 0, 0); | ||
95 | + // draw the keypoints and skeleton | ||
96 | + if (pose) { | ||
97 | + const minPartConfidence = 0.5; | ||
98 | + tmPose.drawKeypoints(pose.keypoints, minPartConfidence, ctx); | ||
99 | + tmPose.drawSkeleton(pose.keypoints, minPartConfidence, ctx); | ||
100 | + } | ||
101 | + } | ||
102 | +} | ||
103 | + | ||
104 | + | ||
105 | +// 사용자 정보 API | ||
106 | + | ||
107 | +let userName = 0 | ||
108 | + | ||
109 | +$.get('/api/users/name', function(data) { | ||
110 | + userName = data.user.user_name | ||
111 | + console.log(data.user.user_name) | ||
112 | +}) | ||
113 | +$(document).ready(function(){ | ||
114 | + $('#savecount').click(function(){ | ||
115 | + | ||
116 | + | ||
117 | + $.ajax({ | ||
118 | + contentType : "application/json; charset=utf-8", | ||
119 | + type : 'get', | ||
120 | + url : '/api/users/name', | ||
121 | + dataType : 'JSON', | ||
122 | + | ||
123 | + success : function(datas) { | ||
124 | + | ||
125 | + let user_name = datas.user_name | ||
126 | + $.ajax({ | ||
127 | + contentType : "application/json; charset=utf-8", | ||
128 | + type : 'post', | ||
129 | + url : '/api/users/countupdate', | ||
130 | + dataType : 'JSON', | ||
131 | + data : JSON.stringify({ | ||
132 | + "name" : userName, | ||
133 | + "count" : count | ||
134 | + }), | ||
135 | + | ||
136 | + success : function(datas) | ||
137 | + { | ||
138 | + if (datas.success) | ||
139 | + { | ||
140 | + alert("저장 성공 !") | ||
141 | + } | ||
142 | + } | ||
143 | + }) | ||
144 | + } | ||
145 | + }) | ||
146 | + }) | ||
147 | +}) | ||
148 | + | ||
149 | + | ||
150 | + |
-
Please register or login to post a comment