From 4625ad2a8024310cd61c7dea6f21de92b5e6bb68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B2=9C=EA=B8=B0=EC=A0=95?= Date: Sun, 1 Dec 2024 16:24:12 +0900 Subject: [PATCH 1/2] =?UTF-8?q?AI:=20[fix]=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=EB=B0=A9=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ai/README.md | 5 + src/ai/__pycache__/constants.cpython-310.pyc | Bin 0 -> 859 bytes .../__pycache__/custom_utils.cpython-310.pyc | Bin 0 -> 4476 bytes src/ai/__pycache__/detectors.cpython-310.pyc | Bin 0 -> 10404 bytes src/ai/__pycache__/main.cpython-310.pyc | Bin 0 -> 10501 bytes src/ai/main.py | 117 +++++++++++++++--- 6 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 src/ai/README.md create mode 100644 src/ai/__pycache__/constants.cpython-310.pyc create mode 100644 src/ai/__pycache__/custom_utils.cpython-310.pyc create mode 100644 src/ai/__pycache__/detectors.cpython-310.pyc create mode 100644 src/ai/__pycache__/main.cpython-310.pyc diff --git a/src/ai/README.md b/src/ai/README.md new file mode 100644 index 0000000..c3e6225 --- /dev/null +++ b/src/ai/README.md @@ -0,0 +1,5 @@ +AI 서버 실행 방법 +================ +1. requirements.txt에 있는 라이브러리 설치 +2. src/ai 디렉토리로 이동 +3. uvicorn main:app 0.0.0.0:8000 --workers 4 명령어 수행 diff --git a/src/ai/__pycache__/constants.cpython-310.pyc b/src/ai/__pycache__/constants.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..73792bc01d3059062fc458794592a354aa935fe0 GIT binary patch literal 859 zcmZ8fO>f#j5Vf%(hLnZ|LlvoNd(DA3aXCg&jlHpH!5jIbBzv(C>yRkKM%WzMf7HLU z*Pi$Vm3r!!P;7Ts`)1~iXW#6u<7TtL!4Q5A=BmVTzp|h>DiC~zmD~eRj&PJGJlG1Y z5P=GFo`?`tfvtf(09K+RsnaU0%`2oq56B_#uOQz9`x^2`uv%0iZ-6_7)uweKLrn+# zE^WBn>EWgdVjCfY_W(Z{vv9HaxO|ACaFaE}#QVo2W#V$Wc-|akpUD}h0QTuy))`^^ zHP9UF+eYA74stC#GFaC^H%Rl45ttf5f#ExvH^eq;mw6q^JSLaP$u=i+#$L{!5Du-V`O`UwmT*MU+3gx{|IB`x|*3k|q@#MwmRM@lsLG)qYRyxw_jw|8&vor|T%}T`0+Frc57|SsW$lG)j}R<##4s kKF4&u2(Nh9GC2bf1(>|Z*D|~HL_y-W-Y&|rFd>qJzqX#`VE_OC literal 0 HcmV?d00001 diff --git a/src/ai/__pycache__/custom_utils.cpython-310.pyc b/src/ai/__pycache__/custom_utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6372162bf2bbc9ee065e64a1deb7659e1003c41 GIT binary patch literal 4476 zcmc&%-ER}w6`#2?w#Og&Ae*wJTeV*LK-`9erKlg2vY`u7SL)J;097rJJNS;B36AZ( zGX{dj6$^$&rD$ObZK#q}f@rsqkji#x#Y#NnkLXM1sZ}2;w|S&zyViy=Tt7_jf*SrK>BU;EDb1n?+@-qWqZ}>mP>3DE`ht5Uy~wq-4Xlnq{)B zWi`&Y)=;uK*Lei*2#@j@-cfGwINmXy;7Pm|9sMeXQOsby3*?_{e0cDj#tH8kP9Vu~bX`~}1t}Tsrq{OwQv5u6uw$$iI ziIdV|l*f7E8mnubN|L%BzIpN`1srT6uE6RMX|c{VFGA9y22KZ$IYkZYO5IT*3w1@Y zR>r%q&X}uB>K^knFX|B=;oa9FC$zdzkJl5P;auZA-M7{W;dVX zFwE~lpW#8%iC8_g?$^zG`Id&_bstnNsnFL`@2zj~dc0n5OF_el^wo}G%M)ol!A~DO z{_TTw>)pB5Pv%EPnM;O9##w`PN%-AlKA^|1BJd%^r{ddzn76GQ3m znP-R6M=GUK`tkj@T5mUK#^Vnf!NvP4x0YI0XIfY94kx7g1ET2cl^lJnWS04qDNZ}- zL2l>W%-+pDpAP09wywP&{OE3Q`$Br~+)#QtlMe3uc;&_%nb5jr9rq_NE7`;mW6JZ+wwMIkckKbR{IVyAv5)qKhs{Z9u%OZ;Q>a<*E^zx}u*&$-+ZQ`I6 zL8LiLZh6GN9aGZ!#cIm3o(;)MvxH{D@gDmMlS0L6Rp2Mvgg_ za7OYIwerY8%Q@{^ljo`x#~R*OszB`Q2h*OD ziTkWvg=IpR(|+Weqf9ble zc5B_*HnpE6nD`=8Y)CDXR!F%_EyuwCcnIE?5sDE)5z1|ZaTlPRR4=Iqm6HRIM(RN`v>MM^i*xDL2Nzau&ptnH7R
)<3kSUE)~LWKJyAO0T%NebqBH{_82 zIEWt>JvgdN#E)C$8o}7Le51W&UxO3)>N#JXhFf-T+zzoF6Mc2USI>%~x82y!ITB}E}1WeoU3@!>I|FDg9tX+k7Q3`jsj2}%fH52%XQ{tDF+RhwW*CfbMh zTlZ&!xkrSa;Obn^SV#wRjo`ys39;YZ3>I!ZA5QIU{qLn=Y45Xg=LQ=o6^F@uHMNKE zw0{p@5>%OH*jnDP|3K0tq@j!;B3AQ{hBM6Q;fWh1tKjCWY0Hlz^iG6qqG+DC!ua|0 z`EQ}~U+s2mihReQ*&)sS$$oW^&j0IZY&d_)wS)l;f9C=SMU^7XoNHXiTNbT9R0=9b zIovQj#Fk4ck8$G~TVSq+a!sQmQI3eq5$t0lbWh_bNj;q?kdXjZG@g_a&`?6A;}c`0H8azC`*!f|b$JFI#Qc8>x91{eqosFm1Xu5E1{#T%=4I@GJ=q4zh9hg& zA}?UbN9${m8Mt7(7Fh$w5)(91>;YL)CA7j$N@4<+C8b8piy-H}u$Iz*JE|fijy9_# zBz93;>E=1xd8U1W>ajIj1f@nnh09YWoFx&NAOmLT4Z7F{fv-2*HL0lZTNH$rySOOnBw1)76jN!Nsqrke7I>$1cG;PJRx#SqIwbI*7~OB&$ay zk1HPd7+srAD4<}S9xq);f9R3e}W;Q z7hSo&A&Y%H++MjZUFb$I-=Gv1T%CV@sGY!#XD!ogp1Gg;=xV+2tP6?b3cA_Qr(4&N z0NNI%2X|D>($tQj^o{}wKYCZ(2}|t2)rRg~nTY7cI-7I^O**NjY?4=?iy~{2B3zSV zaI>3;Oqw)Y0=O9g=j-$hU#Bnl5vd<3TRU%8#ZDSX$rb>tR;@DkHK#U}G5qe0dyq{Q z>~aysqia{4P6Nf0lkHmVLDlS{eAY0+*U*!VfXhv1QD@?{&PPH-3RKs{vW;Lbzf`mw z+~KSrz!c{*AV8$5vpz<(q(K!VOKGSq@z*u+T@1*?#Y<#&1cgU+y6sA)J0WXJSqR8% oAmJ=$tTRDwlC;Cvhl(9#@qUCN#i7kL6hp<28T*Ysqc=71Z!#WL%K!iX literal 0 HcmV?d00001 diff --git a/src/ai/__pycache__/detectors.cpython-310.pyc b/src/ai/__pycache__/detectors.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f1c0b09720323c94b32ae261f2de0dd5cc40cf9 GIT binary patch literal 10404 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 literal 0 HcmV?d00001 diff --git a/src/ai/__pycache__/main.cpython-310.pyc b/src/ai/__pycache__/main.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c632413cae1090a8d5e20e3d85fec8c97ed6d26 GIT binary patch literal 10501 zcmb_iTW}lKdEOg<#g!mPQPjouTDEM$rbtUxoP?2V*_31}v1nJ4e z6q!ylQ*!p-v*&*MzyCY`K{+0e2>2V0KR=uOydeA=ef0hX@o@x?^@%75hG2*VK^O6s z3Q|$l<)Wf1#eg0t2KAsw>2e`dRCSeyl|r}}*26q379x5C>48GD7}H~ZTwIUyIum*V z?_i;?nADRzEmY_)rt}mKtA&9xRJU3f^xxa~TPzIeL%jTUeLH^->%&G^-(e2V?ZmUo zh!{~Lc1acmQ(hMI-KIEq&)mKGef+KI_w)B2{Q>j7xd)B7kudu3pS+~#_nP<5?M1l% zlBjFu19J}{t<&MSUN_JN@t&!n^$NxS2E`L;8OIeZYS|?!ON{D;T?tdsYPF9#dVG_0Qn@ zUgJJ|-?uF3Pnb_&T%R>lhGyK4zJ89ky@$8GyYYGBfi=;1&{T}Q=ERI*JY+n)qM)rW z@NdZ&G14oFe#m&lcyvY34;!P#KD?ha#*A^ipE4fvzs1u+djH3?E2h$l8#NLi^neH%!-{am(rpeekN;~C(DLez=vvN zZPU&dO@x(KUU>cmyrYIWldTqPBcHSJIq)JY=S<6bv0N@3yI|(3c9|hse5CfsS=+8y z6XW9tA0IpL7#?~Z7%Sx~WBKxUwvr#uoi(#|zBFsq(tAg;R*tu1Y5TOjBL#E8ER?cn z+50Y{iR>)CKIz~4%D%nDeR~aU?{gD-PfqMTotE7I&+i6j%B+~R-4OMArf7c*eGtyf z(yR!=zdV&z^ms9QAycXrGc#HAT2C@_CSNi$g>1#Dw^NGEvblM5xhEklvVK(mu>_HpyDgkWofc1GDI~c{u4-g;IajIHB8%lhdH;TE ztYTMpA&sVX|9-9c=Rav)`cP}GU2lHpy4HGkrTJ%X<&eDLPsP_H9%~Q+3f22BUJqd# zNK@&M8!eQ}^O-CwS4)N-qtRxv(-sXx4`bSmj9q0VH%6Vz%$gRyOgCmOnwesG!7Q33 zYz<~sus$Zzvo+HVmZ#@1DYpc+6AgF8C!ZgG$ugOR)vT7rkDAuJU9OB{r4Q~qu4T3wzJK3AyIL~$?H{*TZakYGFJ|+lvC5*W9x7yu(?<5mgEXKdge3O<2YFeex(}J?Q!7A4HgyAOI zIy;GtWm|5Tbk?%7#R_dYHIpHo&1Bq21{=w!7APFcWX@HyT$}MF=dri?GMQ|tRJNi1 z<&uS1h2X@MEt}mvPCk(u`u>MB2-^J-K^0XoCNhfMG+qUfNAOt3A==}`4v_$tvO%L( z%m9EkU<3iIL5i7?jF6$?E5y^oriyf!CB%e{80I7H_D_0%iSC}vmViVoM=R6*qcG|J zBp!=)XI^lG3{>;9a7&y59;ULulLeDtgK(EdjabDp3o|_frm@l_=1X}ylcAwmRBJ@6 z?cCJwF36PR&wPDR4foW(9`K;ElDKI+0Y>w;*+lMCHP@z0K z3!rqBd}*efmf09$8MV&#lj!bdKIo8N?F`cRB1=mNF(M9$wH=#B?&r_8Ew@)H95`MiA+i zoJUq~r(&NDiOO$lyEhM_o81lh)@{S8(LN?($5-tL0$4Q+q=D606YH`gvwI!6Avw~T z*j`6lZa}BK)pMlp3pbSZ3i^YrB~Js{HNQE{E^9SuLQ4m{C6i~t62h<%MrO_`m-w*V zfMHHoXVXg0z(0r1Y`KUrUe>R@f?QS%LRyN6ikK94(nQ?5c_MlXO{F93L8^W)2|_G3 zPT~;~w2#>nBtA|wM~p|2FpY<6qYA`G$%(*FfKmL%G`CQ{D3O8lWi}7< zRi$RP;4)h4gVj49-d=zAif@o>@$vK4_*Z?Mt9uhc`+CE63?qVYfUh1HGFH##Yv%}S zQhU8>C&Pe=hSb)?xz==6hpRA`BDS76ts=wMYlEG)}&>);qWF z{Pecgdhczmb?v?853gy>+t*vmH=r4V&`l5S`Nq>@%Ty@5gOEVb!Nt7dY}sP_c>S2EgaC*e9jG+P#} zVH?()_Vgv`>!PiYb~J)U5V|7T*yCP)M>Im0Wats}V@){dTe$C?tIM^g9Cx!ey#f|X1#9p)$*v%0y7xpsOkWvJ(Ny9JOj(r(>S;2o-Yf91mev8)J zd1wp$zk=swp|q3h(?#e^06LT4^+9L5b&+P(1Fr>2N$*V>vbhD)psiy&E&sT;Wsdvt zsST4)mXe^zL}Vgz7LHhA^*u867?cI|;KdarQaI_eNTBrLo7{K#q9d-Mb}x zXx1#57b@(@HL8^MskAgC)E#;18tJ4d0o?BifqlQg2q_ZOtny0oX~38aa0a`g0Un^)$Nd12089KMmWV& zL3lQ2NT=Ms_At<8&YR_IY0(WXWD8a3DDf5+f&k67bEWcmR|OHC;ZUMcbSRrI(`j0d zPZsj%<7xB{6jE0zWea5bnWrbebZqKq1_%DkOQ)XameCD*bJQg-98%bE)Rzu&BId?& zRmM95;yNChVvaP^6 z`L^rTP+h49>cM)buGYi#2<&Lk5x{3fPnV&K_dpjzi-)0$QRrena;Q9q zfGPe6dTRK)O*0VMs)mg-kXEOvbda9V)~~EI8>_9GD_ZNP*P1_i-{(hFVr^TcZZ@xs z=pm>jKNb7|!dypP)i+C4^&qdR`BzJ=#?|%jeyeqTMQh%=(t7hcF@aWNrS--dZ1uM{ zakT2=|A&HG*!wDx%?WKogROVI(Y$mUd}96Lhg))l0jQ_7Sjy$gZi3VSo-3#n+%FZk zk5x-J%wk|j-Y_?*$k&YA0ay|c11u%f2xt{+B z(1T3mG0Gv*WNw7qL>W#M+yU-a>Uh;Mg**U_07UQhw+-m*73!)^qD$mChd+k6zrkZo zK}bs~Fht?ZLlTF7GZ;vSgJ2oNFr)x659L!uN=TB#u|^F3S_&n^6#N9k$n_vcnc#mU z`uFe`7Z3!a za)atnVz2hBU%l46b$fI}9&LS@YDB*Gp>3I0S6UPNgL*Oz zfAdC7y>olXudR9cTI-$F=EW=Bn#XuSH&DsjxwEdkm_5&u7!)Tso7fTY7eWy0K?u%{ z;vkuPJ2+U}Rd^G2beR$WnUTW*eqho_u%<8J<;wHs;w?#!LTeU$tM6%G_l$ZH`DE-g zpD^taYJ2Wd`7Ov&dcaeBC0B+Ab;#4AjOU!qcq$5Kb;nPanJHvvEoM@e2K>yu{!Y%N zm`4FDcd$1pa~4`#W{YmhFW2Kzc9VWw$0O?YcY>XQ?FM`O%Isn4O8D7RCyr)Lz3}pB zKZv8xX?M`Cbff1v#(|~XnRcFD&$Y!)q0)51o8UacY>vb{i2{iti4uvkB+4Wz5Ux7Q z@vaYMc&)m5Hf0r60m$?+HPD9|jx z%TbRCUm34QoXA{XJ?cd9^)Qj0xug*UH1>mp#qAWn2io5Nri2}v8*~x?zdk2!gysXR z==9m!;E5|b;l_|dvFdft^>F;$Q`9=z4)eCd9?@|^bKBY1NR~wOBguP+Lzt7KTwWeS ztoP$d;Tdp(bHnVPNm4)RgZ2(*5XU`XS*mZdcRJg=@T+otsJ^{E>j_TV9>5!wi@ zwknHbB_qpNb}r*q(iy|NYZbI1nmy?Vv~C81`@Ls&wSrIJ3>SmRifN!-VnL zJxf-lYnIjyw{`RFUi0<_yi2Rs&^NgUEEHBQ6smNi)cnbZobukh+*QUi)Z@1ojA48!t4YAmjh6YaC`USOfj~n3A z&gfjujzZ{aZo!_!O(Da%k32(}XP!NE;MB9vxCy=*9$0dO^8G}NLm#nb_5}zx%=3X5 zo^=DX9+n%xt&CYGAWdOwx*cbA9C=^c!yEQfP_z}vRC{q0`e>R~UE%}(n>8|TaSL|EKN_&0(yN$q$& z&L^w$neeTvcP_U0EW1JPi(N)9bQ!bA-lqP1hr~BYaBIUY5H~(#hIDV?x|0y@1iQP8 zty1Z`sPHu6z&Zu%c?v|xl0@VPYz(9vlX1xa2V9U_m=q4!+!ePZ{r6xnDGwqIRtKpO zkl~aZ!x3DSll)0ZjB4IMgyV=HBFyd2zav1dHIQG?#!=fRzY1a`JHX>Wfqn98ZX&;M z%yIJT<>dKn9JYJn!YEDTH-cCqzdboYeoZ*+ddY9BLw+f~OMcs=xJ!O<=mz=iYuDEy zzYNZ9kl=PakzXSKw=|w~u=MJSL+7c(?5E@b@4r%N4q8Y2KV7v8zFK`jePf`J3s$GYps3Ix{2dC zr`X*h9j((w_-{GDrh7q68{u4eR0CHY)d={~-LhTK*G9PMKx-YGqb_<-r?vj{t@XFB zYaO;7Qzo#+*ks2v2Z<&x9=CI z;q_}**O$@h<{N(kqmA>*%4+i$E2CQLFISo?m;GV%=xgU7F_rFTHWY?cNpOb-@ou(2 z;yj59Bo;~3NPLxqLxM;#tCJu%JNp`mB@(Ygq~pIov1MPU(#s@PNZf@d#5%u3ctXY^ z)%W``SM^in)ASKNTr`b5e#Zl6fmE#Mp&9(<2%ii(@^hOEFWm^Dtszlt9PuYXx0p<~cZAZHs*wMLeg=%e)=jCQ4&W;WJ&BN@d$}8k{~ZDcY*NZ2e$z|JEU_$#Se76UvWH%ts+>|jw%ep zio{@P=ppI7DU5#!mIe=94+Vb{4AQlb+Hu+-?$@!Pm&)D2aL~Xe$+2)Os>ZzXNjVV; VZX?^K!g3`vb+`Jk`e5wX{{WkfxFi4o literal 0 HcmV?d00001 diff --git a/src/ai/main.py b/src/ai/main.py index c98b449..d281d74 100644 --- a/src/ai/main.py +++ b/src/ai/main.py @@ -131,7 +131,7 @@ async def websocket_endpoint(websocket: WebSocket, user_id: str): data = await websocket.receive_text() # data는 Base64로 인코딩된 이미지 데이터라고 가정 image_bytes = base64.b64decode(data) - timestamp = time.time() + timestamp = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') logging.debug(f"Received frame from {user_id} at {timestamp}") # 이미지 디코딩 @@ -184,6 +184,98 @@ async def get_cheating_result(user_id: str): logging.error(f"Error sending cheating result: {e}") +# async def process_frame(user_id, image, frame_timestamp): +# loop = asyncio.get_event_loop() +# try: +# image_shape, detections, face_present, head_pose, eye_center, gaze_point, hand_landmarks_list = await loop.run_in_executor( +# executor, +# process_image, +# image +# ) +# logging.debug(f"{user_id}: 프레임 처리 완료") +# except Exception as e: +# logging.error(f"{user_id}: 프레임 처리 중 오류 발생: {e}") +# return +# +# # 부정행위 업데이트 +# try: +# update_cheating(user_id, detections, face_present, head_pose, eye_center, gaze_point, hand_landmarks_list, +# image_shape) +# logging.debug(f"{user_id}: 부정행위 업데이트 완료") +# except Exception as e: +# logging.error(f"{user_id}: 부정행위 업데이트 중 오류 발생: {e}") +# return +# +# # 부정행위 메시지 전송 +# if cheating_messages[user_id]: +# current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') +# cheating_result = { +# "user_id": user_id, +# "cheating_counts": cheating_counts[user_id], +# "timestamp": current_time, +# "messages": cheating_messages[user_id] +# } +# try: +# await get_cheating_result(user_id) +# await manager.send_message(user_id, cheating_result) +# cheating_messages[user_id].clear() +# logging.debug(f"{user_id}: 부정행위 메시지 전송 및 초기화 완료") +# except Exception as e: +# logging.error(f"{user_id}: 부정행위 메시지 전송 중 오류 발생: {e}") +# + +# 이전 부정행위 카운트를 저장하는 딕셔너리 +previous_cheating_counts = defaultdict(lambda: { + 'look_around': 0, + 'repeated_gaze': 0, + 'object': 0, + 'face_absence_long': 0, + 'face_absence_repeat': 0, + 'hand_gesture': 0, + 'head_turn_long': 0, + 'head_turn_repeat': 0, + 'eye_movement': 0 +}) + +# 부정행위 메시지 전송 +async def send_cheating_result_if_changed(user_id: str): + global previous_cheating_counts + + # 현재 부정행위 카운트 가져오기 + current_counts = cheating_counts[user_id] + + # 변화 감지 + counts_changed = any( + current_counts[key] != previous_cheating_counts[user_id][key] + for key in current_counts + ) + + if counts_changed: + # 부정행위 메시지 전송 + current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + cheating_result = { + "user_id": user_id, + "cheating_counts": cheating_counts[user_id], + "timestamp": current_time, + "messages": cheating_messages[user_id] + } + try: + # 백엔드에 결과 전송 + await get_cheating_result(user_id) + # 웹소켓으로 프론트엔드에 전송 + await manager.send_message(user_id, cheating_result) + + # 이전 카운트를 업데이트 + previous_cheating_counts[user_id] = current_counts.copy() + logging.debug(f"{user_id}: 부정행위 메시지 전송 및 이전 상태 업데이트 완료") + + # 메시지 초기화 + cheating_messages[user_id].clear() + + except Exception as e: + logging.error(f"{user_id}: 부정행위 메시지 전송 중 오류 발생: {e}") + +# `update_cheating` 함수에서 호출 async def process_frame(user_id, image, frame_timestamp): loop = asyncio.get_event_loop() try: @@ -202,27 +294,12 @@ async def process_frame(user_id, image, frame_timestamp): update_cheating(user_id, detections, face_present, head_pose, eye_center, gaze_point, hand_landmarks_list, image_shape) logging.debug(f"{user_id}: 부정행위 업데이트 완료") - except Exception as e: - logging.error(f"{user_id}: 부정행위 업데이트 중 오류 발생: {e}") - return - # 부정행위 메시지 전송 - if cheating_messages[user_id]: - current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - cheating_result = { - "user_id": user_id, - "cheating_counts": cheating_counts[user_id], - "timestamp": current_time, - "messages": cheating_messages[user_id] - } - try: - await get_cheating_result(user_id) - await manager.send_message(user_id, cheating_result) - cheating_messages[user_id].clear() - logging.debug(f"{user_id}: 부정행위 메시지 전송 및 초기화 완료") - except Exception as e: - logging.error(f"{user_id}: 부정행위 메시지 전송 중 오류 발생: {e}") + # 변경된 경우에만 전송 + await send_cheating_result_if_changed(user_id) + except Exception as e: + logging.error(f"{user_id}: 부정행위 업데이트 중 오류 발생: {e}") def process_image(image): """ From 61eb00b05be07a6ca0d3a49f4e071ea91974462b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B2=9C=EA=B8=B0=EC=A0=95?= Date: Sun, 1 Dec 2024 16:49:42 +0900 Subject: [PATCH 2/2] =?UTF-8?q?AI:=20[feature]=20=EB=B6=80=EC=A0=95?= =?UTF-8?q?=ED=96=89=EC=9C=84=20=EC=84=A0=ED=83=9D=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ai/__pycache__/main.cpython-310.pyc | Bin 10501 -> 11169 bytes src/ai/main.py | 157 +++++++++++++++++------- 2 files changed, 113 insertions(+), 44 deletions(-) diff --git a/src/ai/__pycache__/main.cpython-310.pyc b/src/ai/__pycache__/main.cpython-310.pyc index 5c632413cae1090a8d5e20e3d85fec8c97ed6d26..c043b874f764024e1fd24173b88ed0b677677067 100644 GIT binary patch delta 5823 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 diff --git a/src/ai/main.py b/src/ai/main.py index d281d74..68eb72c 100644 --- a/src/ai/main.py +++ b/src/ai/main.py @@ -54,7 +54,6 @@ 'face_absence': None, 'head_turn': None, 'hand_gesture': None, - 'eye_movement': None, 'repeated_gaze': None, 'object': None }) @@ -66,8 +65,7 @@ 'face_absence_repeat': False, 'hand_gesture': False, 'head_turn_long': False, - 'head_turn_repeat': False, - 'eye_movement': False + 'head_turn_repeat': False }) cheating_counts = defaultdict(lambda: { 'look_around': 0, @@ -77,8 +75,18 @@ 'face_absence_repeat': 0, 'hand_gesture': 0, 'head_turn_long': 0, - 'head_turn_repeat': 0, - 'eye_movement': 0 + 'head_turn_repeat': 0 +}) +cheating_settings_cache = defaultdict(lambda: { + 'look_around': False, + 'repeated_gaze': False, + 'object': False, + 'face_absence_long': False, + 'face_absence_repeat': False, + 'hand_gesture': False, + 'head_turn_long': False, + 'head_turn_repeat': False, + 'eye_movement': False }) gaze_history = defaultdict(list) @@ -122,6 +130,40 @@ async def send_message(self, user_id: str, message: dict): executor = ProcessPoolExecutor(max_workers=4) logging.info("ProcessPoolExecutor 초기화 완료") +@app.websocket("/ws/{user_id}/{exam_id}") +async def websocket_endpoint(websocket: WebSocket, user_id: str, exam_id: str): + await manager.connect(user_id, websocket) + + # Fetch cheating settings from backend + cheating_settings = await fetch_cheating_settings(exam_id) + if cheating_settings: + cheating_settings_cache[exam_id] = cheating_settings + logging.info(f"Cached cheating settings for examId {exam_id}: {cheating_settings}") + else: + logging.error(f"Could not fetch cheating settings for examId {exam_id}") + await websocket.close() + return + + try: + while True: + # WebSocket 데이터 처리 로직 + data = await websocket.receive_text() + image_bytes = base64.b64decode(data) + timestamp = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') + + image_np = np.frombuffer(image_bytes, np.uint8) + image = cv2.imdecode(image_np, cv2.IMREAD_COLOR) + + if image is None: + await websocket.send_json({"error": "이미지 디코딩 실패"}) + continue + + await process_frame(user_id, exam_id, image, timestamp) + except WebSocketDisconnect: + manager.disconnect(user_id) + except Exception as e: + logging.error(f"WebSocket {user_id} 연결 중 오류 발생: {e}") + manager.disconnect(user_id) @app.websocket("/ws/{user_id}") async def websocket_endpoint(websocket: WebSocket, user_id: str): @@ -237,6 +279,18 @@ async def get_cheating_result(user_id: str): 'eye_movement': 0 }) +async def fetch_cheating_settings(exam_id): + async with aiohttp.ClientSession() as session: + try: + async with session.get(f"{BACKEND_API_URL}/exams/{exam_id}/cheatingTypes") as resp: + if resp.status == 200: + return await resp.json() + else: + logging.error(f"Failed to fetch cheating settings for examId {exam_id}. Status: {resp.status}") + except Exception as e: + logging.error(f"Error fetching cheating settings for examId {exam_id}: {e}") + return None + # 부정행위 메시지 전송 async def send_cheating_result_if_changed(user_id: str): global previous_cheating_counts @@ -304,12 +358,6 @@ async def process_frame(user_id, image, frame_timestamp): def process_image(image): """ 이미지를 처리하여 부정행위 탐지에 필요한 정보를 반환 - - Args: - image (numpy.ndarray): 입력 이미지. - - Returns: - tuple: (image_shape, detections, face_present, head_pose, eye_center, gaze_point, hand_landmarks_list) """ image_shape = image.shape image_for_detection = image.copy() @@ -326,7 +374,7 @@ def process_image(image): # 손 랜드마크 추출 hands_results = hands.process(image_rgb) - # 객체 탐지 (YOLO11 사용) + # 객체 탐지 (YOLO 사용) object_results = model(image_for_detection, device=device) logging.debug(f"객체 탐지 완료: {len(object_results)} 결과") @@ -359,49 +407,70 @@ def process_image(image): head_pose = {'pitch': pitch, 'yaw': yaw, 'roll': roll} logging.debug(f"머리 자세: Pitch={pitch}, Yaw={yaw}, Roll={roll}") - # 눈동자 위치 계산 - eye_center = calculate_eye_position(landmarks) - logging.debug(f"눈동자 중심: {eye_center}") - # 시선 위치 추정 gaze_point = get_gaze_position(landmarks) logging.debug(f"시선 위치: {gaze_point}") - return image_shape, detections, face_present, head_pose, eye_center, gaze_point, hand_landmarks_list - - -def update_cheating(user_id, detections, face_present, head_pose, eye_center, gaze_point, hand_landmarks_list, - image_shape): - """ - 감지된 정보를 기반으로 부정행위를 업데이트 - - Args: - user_id (str): 사용자 ID. - detections (list): 감지된 객체의 리스트. - face_present (bool): 얼굴 존재 여부. - head_pose (dict): 머리 자세 (pitch, yaw, roll). - eye_center (tuple): 눈동자의 중심 좌표 (x, y). - gaze_point (tuple): 시선 위치의 좌표 (x, y). - hand_landmarks_list (list): 손 랜드마크 리스트. - image_shape (tuple): 이미지의 형태 (높이, 너비, 채널). - """ - detect_object_presence(user_id, detections, cheating_flags, cheating_counts, cheating_messages, image_shape) - detect_face_absence(user_id, face_present, start_times, cheating_flags, cheating_counts, face_absence_history, - cheating_messages) + return image_shape, detections, face_present, head_pose, gaze_point, hand_landmarks_list +# def update_cheating(user_id, detections, face_present, head_pose, eye_center, gaze_point, hand_landmarks_list, +# image_shape): +# """ +# 감지된 정보를 기반으로 부정행위를 업데이트 +# +# Args: +# user_id (str): 사용자 ID. +# detections (list): 감지된 객체의 리스트. +# face_present (bool): 얼굴 존재 여부. +# head_pose (dict): 머리 자세 (pitch, yaw, roll). +# eye_center (tuple): 눈동자의 중심 좌표 (x, y). +# gaze_point (tuple): 시선 위치의 좌표 (x, y). +# hand_landmarks_list (list): 손 랜드마크 리스트. +# image_shape (tuple): 이미지의 형태 (높이, 너비, 채널). +# """ +# detect_object_presence(user_id, detections, cheating_flags, cheating_counts, cheating_messages, image_shape) +# detect_face_absence(user_id, face_present, start_times, cheating_flags, cheating_counts, face_absence_history, +# cheating_messages) +# if head_pose: +# pitch = head_pose['pitch'] +# yaw = head_pose['yaw'] +# detect_look_around(user_id, pitch, yaw, start_times, cheating_flags, cheating_counts, cheating_messages) +# detect_head_turn(user_id, pitch, yaw, start_times, cheating_flags, cheating_counts, head_turn_history, +# cheating_messages) +# detect_eye_movement(user_id, eye_center, image_shape, start_times, cheating_flags, cheating_counts, +# cheating_messages) +# if gaze_point: +# grid_row = int(gaze_point[1] / (image_shape[0] / GRID_ROWS)) +# grid_col = int(gaze_point[0] / (image_shape[1] / GRID_COLS)) +# grid_position = (grid_row, grid_col) +# detect_repeated_gaze(user_id, grid_position, gaze_history, start_times, cheating_flags, cheating_counts, +# cheating_messages, pitch) +# if hand_landmarks_list: +# detect_hand_gestures(user_id, hand_landmarks_list, start_times, cheating_flags, cheating_counts, +# cheating_messages) +def update_cheating(user_id, exam_id, detections, face_present, head_pose, eye_center, gaze_point, hand_landmarks_list, image_shape): + # Fetch cheating settings for the current exam + cheating_settings = cheating_settings_cache.get(exam_id, {}) + + # 동적으로 탐지 로직 활성화 + if cheating_settings.get('object'): + detect_object_presence(user_id, detections, cheating_flags, cheating_counts, cheating_messages, image_shape) + if cheating_settings.get('face_absence_long') or cheating_settings.get('face_absence_repeat'): + detect_face_absence(user_id, face_present, start_times, cheating_flags, cheating_counts, face_absence_history, + cheating_messages) if head_pose: pitch = head_pose['pitch'] yaw = head_pose['yaw'] - detect_look_around(user_id, pitch, yaw, start_times, cheating_flags, cheating_counts, cheating_messages) - detect_head_turn(user_id, pitch, yaw, start_times, cheating_flags, cheating_counts, head_turn_history, - cheating_messages) - detect_eye_movement(user_id, eye_center, image_shape, start_times, cheating_flags, cheating_counts, - cheating_messages) - if gaze_point: + if cheating_settings.get('look_around'): + detect_look_around(user_id, pitch, yaw, start_times, cheating_flags, cheating_counts, cheating_messages) + if cheating_settings.get('head_turn_long') or cheating_settings.get('head_turn_repeat'): + detect_head_turn(user_id, pitch, yaw, start_times, cheating_flags, cheating_counts, head_turn_history, + cheating_messages) + if gaze_point and cheating_settings.get('repeated_gaze'): grid_row = int(gaze_point[1] / (image_shape[0] / GRID_ROWS)) grid_col = int(gaze_point[0] / (image_shape[1] / GRID_COLS)) grid_position = (grid_row, grid_col) detect_repeated_gaze(user_id, grid_position, gaze_history, start_times, cheating_flags, cheating_counts, cheating_messages, pitch) - if hand_landmarks_list: + if cheating_settings.get('hand_gesture'): detect_hand_gestures(user_id, hand_landmarks_list, start_times, cheating_flags, cheating_counts, cheating_messages) \ No newline at end of file