From 936d8e0531322a90627725efa50053aec501b839 Mon Sep 17 00:00:00 2001 From: DuOtto Date: Sun, 2 Aug 2026 16:37:29 +0200 Subject: [PATCH] Added continous hand tracking --- source/Helpers/ChArUco.py | 8 +-- source/Tbd/TrackingController.py | 43 +++++++++---- .../TrackingController.cpython-311.pyc | Bin 14312 -> 16030 bytes source/Tbd/__pycache__/helper.cpython-311.pyc | Bin 8995 -> 10867 bytes source/UI/SetupPageWidget.py | 60 ++++++++++++------ .../CameraSetupWidget.cpython-311.pyc | Bin 4376 -> 4597 bytes .../HomePageWidget.cpython-311.pyc | Bin 26768 -> 31790 bytes .../SetupPageWidget.cpython-311.pyc | Bin 12644 -> 14444 bytes 8 files changed, 74 insertions(+), 37 deletions(-) diff --git a/source/Helpers/ChArUco.py b/source/Helpers/ChArUco.py index 11cda97..fbfd858 100644 --- a/source/Helpers/ChArUco.py +++ b/source/Helpers/ChArUco.py @@ -2,10 +2,10 @@ import cv2 import numpy as np # ---- Board parameters (CHANGE THESE ONLY IF YOU REPRINT) ---- -squares_x = 6 # number of chessboard squares in X -squares_y = 9 # number of chessboard squares in Y -square_length = 25 # mm -marker_length = 18 # mm (must be < square_length) +squares_x = 4 # number of chessboard squares in X +squares_y = 5 # number of chessboard squares in Y +square_length = 50 # mm +marker_length = 36 # mm (must be < square_length) dictionary_id = cv2.aruco.DICT_4X4_50 dpi = 300 # print DPI # ------------------------------------------------------------ diff --git a/source/Tbd/TrackingController.py b/source/Tbd/TrackingController.py index ec15bc9..066a4f9 100644 --- a/source/Tbd/TrackingController.py +++ b/source/Tbd/TrackingController.py @@ -32,18 +32,25 @@ class TrackingController(QObject): # MediaPipe's legacy solutions.hands API no longer exists on the # installed mediapipe version, so hand detection runs on the newer - # Tasks API instead (IMAGE mode: each camera frame is detected - # independently, since frames from different cameras aren't a single - # monotonic video stream that VIDEO mode requires). - options = HandLandmarkerOptions( - base_options=BaseOptions(model_asset_path=MODEL_PATH), - running_mode=RunningMode.IMAGE, - num_hands=4, - min_hand_detection_confidence=0.5, - min_hand_presence_confidence=0.5, - min_tracking_confidence=0.5, - ) - self.detector = HandLandmarker.create_from_options(options) + # Tasks API instead. Each camera gets its own HandLandmarker running + # in VIDEO mode, fed only that camera's own frames with a monotonically + # increasing per-camera timestamp - this lets MediaPipe track hands + # frame-to-frame per view (much less jitter than re-detecting blind + # every frame), which wouldn't be valid if frames from different + # camera viewpoints were interleaved through a single VIDEO-mode + # detector. + self._frame_counter = defaultdict(int) + self.detectors = {} + for camera in self.cameraList: + options = HandLandmarkerOptions( + base_options=BaseOptions(model_asset_path=MODEL_PATH), + running_mode=RunningMode.VIDEO, + num_hands=12, + min_hand_detection_confidence=0.5, + min_hand_presence_confidence=0.5, + min_tracking_confidence=0.5, + ) + self.detectors[camera.index] = HandLandmarker.create_from_options(options) def start(self): # Guard: need projections @@ -72,6 +79,9 @@ class TrackingController(QObject): for camera in self.cameraList: camera.close() + for detector in self.detectors.values(): + detector.close() + if self.logger: self.logger.log("[Tracking] Stopped") def _tick(self): @@ -102,6 +112,10 @@ class TrackingController(QObject): if camera.capture is None: continue + detector = self.detectors.get(camera.index) + if detector is None: + continue + ok, frameBGR = camera.capture.read() if not ok or frameBGR is None: continue @@ -110,7 +124,10 @@ class TrackingController(QObject): frameRGB = cv2.cvtColor(frameBGR, cv2.COLOR_BGR2RGB) mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=frameRGB) - result = self.detector.detect(mp_image) + + self._frame_counter[camera.index] += 1 + timestamp_ms = self._frame_counter[camera.index] * self.tick_ms + result = detector.detect_for_video(mp_image, timestamp_ms) if not result.hand_landmarks: continue diff --git a/source/Tbd/__pycache__/TrackingController.cpython-311.pyc b/source/Tbd/__pycache__/TrackingController.cpython-311.pyc index 1519ae15adc355970805a41a24e66e4c82c9dc12..b48fa33b4c9c711b8c17e40b43cd5f0c9dc7243e 100644 GIT binary patch delta 5377 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 delta 3849 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 delta 562 zcmewyve=DpIWI340}!m3k)4$;Jdsa=(P*N&hA~qPYc5+9TP}MPdoD*52O~oYLke>a zXD(M1S1xxHcP>v9PcCm1FIb!dd04Y?JvI)tSYBv<0I&qxj|o#>4E4nwwQPgP9m5Hdk;LFfrOpe#yI>(P8sM zzBEQg=gmL(ix?SwCT9!oVGNn9AymN_K6#?p8-?UU;@WV!l*Qj9=c synFI84Wr2qHFoL?F#^4eNqhu}eE|_4nHiY4TO>a)ffQ*?KBc)507tHkoB#j- diff --git a/source/UI/SetupPageWidget.py b/source/UI/SetupPageWidget.py index 5d3d7d7..03acf6c 100644 --- a/source/UI/SetupPageWidget.py +++ b/source/UI/SetupPageWidget.py @@ -4,7 +4,7 @@ import os from Tbd.helper import CameraObject from UI.UILogger import UILogger -from PySide6.QtCore import Qt, QTimer +from PySide6.QtCore import Qt, QTimer, QThread, Signal from PySide6.QtGui import QImage, QPixmap from PySide6.QtWidgets import QLineEdit from PySide6.QtGui import QIntValidator @@ -12,6 +12,35 @@ from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout, QPushButton, QHBoxLa from typing import List +class LoadCamerasWorker(QThread): + """ + Opens/verifies/closes each camera off the UI thread - camera.open() does + MSMF format negotiation with retries+sleeps, which used to block the Qt + event loop (window shows as "Not Responding") for the whole duration. + """ + cameraOpened = Signal(object) + logMessage = Signal(str) + + def __init__(self, indexToAdd: List[int]): + super().__init__() + self.indexToAdd = indexToAdd + + def run(self): + for index in self.indexToAdd: + camera = CameraObject(capture=None, index=index) + + if not camera.open(): + self.logMessage.emit(f"cam{index}: failed to open, skipping.") + continue + + ret, frame = camera.capture.read() + if ret: + self.logMessage.emit(f"cam{index}: verified, frame shape={frame.shape}") + else: + self.logMessage.emit(f"cam{index}: opened but failed to read a frame.") + + camera.close() + self.cameraOpened.emit(camera) class SetupPage(QWidget): @@ -91,31 +120,22 @@ class SetupPage(QWidget): self.logger.log("Path not found") indexToAdd = [1, 2, 3, 4, 5, 6] - + #indexToAdd = [0, 3] # Cameras are verified one at a time and left closed afterwards - this # hardware can't sustain many concurrent capture graphs (confirmed via # both CAP_DSHOW and a raw DirectShow graph), so callers open a camera # right before they need it (CameraSetupWidget.Start, calibration capture) # and close it right after, instead of keeping all 6 open simultaneously. - for index in indexToAdd: - camera = CameraObject(capture=None, index=index) + self.loadBTN.setEnabled(False) + self._loadWorker = LoadCamerasWorker(indexToAdd) + self._loadWorker.logMessage.connect(self.logger.log) + self._loadWorker.cameraOpened.connect(self._onCameraLoaded) + self._loadWorker.finished.connect(lambda: self.loadBTN.setEnabled(True)) + self._loadWorker.start() - if not camera.open(): - self.logger.log(f"cam{index}: failed to open, skipping.") - continue - - ret, frame = camera.capture.read() - if ret: - self.logger.log(f"cam{index}: verified, frame shape={frame.shape}") - else: - self.logger.log(f"cam{index}: opened but failed to read a frame.") - - camera.close() - - self.cameraList.append(camera) - self._cameraList_changed() - - return + def _onCameraLoaded(self, camera: CameraObject): + self.cameraList.append(camera) + self._cameraList_changed() diff --git a/source/UI/__pycache__/CameraSetupWidget.cpython-311.pyc b/source/UI/__pycache__/CameraSetupWidget.cpython-311.pyc index 79c87144b8171ecf08fd5cb6dcb8ff4a24c54e6a..274e27241d90c9088fe77c3f29c07e9ef44eefe3 100644 GIT binary patch delta 470 zcmbQC^i`Q}IWI340}xDIo|BcvxsmT62V=qH3moc`?U?vPikVUvf*EQUvY0?hKwts$ zcvy-=fdaQ!@(WV)CW~_hDp>=$4GcHY?BcthN4;}+XSqq?K@qHixw2hnp3b)J^Zj}q%I$+;C z*xbTf!|2P*s`Y^ZPH-`?Dt%yp6Fh>fCLb8^5nx9Zi2$w8lq|BI?8|?EF?uqez-4nA TMxahiVg~z1kmwgIQedM1Tqku} delta 303 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 biboq!+c_FRB=6JVHRnC4=7Z0um-zl40Ckox delta 4769 zcmb7HdvF_Pwcl^0)!VWiKP1_Ty|S&?@+)!b)K22WwTbg`;zycy6I?~RcI4QSy;`S^ zks4D1B&qMMXHGB84HQC$2@MTQQ}bwHXbTi5Fi@okFdE<{b32#ITcHJV^T&mAb}hx3 z!Z57m-=6P$=bZ0#zH`og?lzmh&YYjl%d>I#{Q0Nf4U7A(IXCb&Ben5)`x)57@2HUN zqYjiNi0+XX44 z>Va8&tWmSPGCS6&&TS$t%-U?a6nJf3d*!_{AlbZ7V9!ciwRvEoRA6TKjeW$hH_);$ z0D}3L=!z(J7C284(zqdKKS>k~%*JnIw!&r3W3R8CA zFfoQ8D!UPtt&n-x11sHyEC{RJ8(H7N3HNPQkru)cNuCbyX})<)+7t?o%StfDlO8HU zysV3j!pmhS4|mEM*Z{057t6?Y+B6moO0txbr9rmA!{t6E!(-*oqe542F>LcbXxWQh z0i$vRp7Ht%_h4iBytc!l*T*K|j&thn-JBrm_ZxN(&*e za-oo%ggUin3wQJ!bX82jA1kZi7Zo%4=T%p>kK@idVWjdt>SIL^v=+gPUxdWQLO3M4 zSP4vuEwEk8hihVwx#TiV;5i0g6H8b=bc$|h@KwV*l_LBZC8AaV`g{lAgEiI6O_u7G z&)`_X6gQ0u*>TS~PtZnk3Y&ArVL)(0LA85Oh?}l@F5@M7E_;!;i59E}XgiV{_Zl<}DI2x(1|M1dj?%ZeNc$wRvTD=}i*Vk&Z@B%F5a8A72U zHA)g`hq}DQdeUrbS{NW6y+T3XxDr7Nv3vyap63DmGH!v@%|5eAGT$`7f#ylG%{pkv za5i|OxqqQ{HDeH6Q)z3sZfQ(e8k3erxUl9}(G&US^ONqo%tea>Zm+31jqf@8yE?0D zKh>Gx3a!VOj!8%5Vt!RBzb@I>{bqi5GQWGVz@0I1+gLCA$=1m?w@xm4#gw;ov9#>T z(M4a&Voh7dioFWE#R=P(^BoPZJKl44=Y0IquICG%?|OdfMLAQjj(ZR5XJ|g<=v?d> z)QIlIjSpx<`;B$mQ|p3fx6Jk8)Kl)3>yEWa$J&euYFp1^5PG5Y!#q0<8`{d-$pJN| z2=IzV6J%++NXi(&D8dN-YY%$H4E$SLg^5CvG6&b%)|e>VrOUwCUIwhamVE&N?f;D< z<*gqURvjlXvY?zGAcM>Oc7r%Py-5kfe{McdNn`X~p$Qy9D$P#b6X~L`qH`1fGY$qj zn=#bQcDAtzco9*8Oy`$R50Z7dF3a4^JUkXuRXLJ2jz+@~g-&0I6FiELHY;-McyK~i zo+8!^u}ov(NN{WfafMtxEod@&(;l$1gb_mYztk#RJp7urg62Xh`^S)s8CjyAKwV3MQm4kP-$DUH;~- zB8(t^+I5)K!ocpvVu~PFSXz+OQ@969;Rm%Fv;n`mTo>uIFdm#3Iu@gFB8s#6rFsP? z+he1>2t~&yHrK(izDAtF6MZ!+zld_mCkeETwTB~kwPP3(S|mkMo zM1o%-c$wfE2oOB#hF1mz-MQ+aaCbetGtkEWKex~{_&>afrXS*A!d(nadv~_HicMUr zHkdgW3o1(Rl=3}d^7_fK>3h8C4S05M1?z-w@2$caNB4VJ0i4?};?5Tw@WUJXecrf{ zyjFkSZ^l+GP0o_gR%4vIBwz(o0Nw-!5T-jY!gh7Ae3nh%BBis6}qUCab;9c+Q`iVpa#Xoc^Hd4ZfY$*j_uEj^pzl)3y1EG@QXuK=-oNgu-?|ETt<5{MdElk77Is4L_EQz zTtmqL`;=ya>jZBi{7Q=;{|+x4KGUFOn^&o45+ShS?wwppCDf%|)|)W=(C|)LzH*x2 zeF$m1x6z<_f2)$9mY}r)Eq%)*(Y z_nW%$2xD$|^=M81?qt>4R8@P@yIcQVcl4zkeMv{(qP-$zZ%o=-lkL5!_E56@#G<2M zuJ?)l^ZiNhK(cCGs%j$<+V8q!Fy+AbI|!Zc?cx2a7k=~JV+{SNMJEWpP0% zr};cWFinsPK})`=WzBoMa|%nxdKQQB;0`-Ey1w7T5Dj|Ro`rM40fz4SK&S-1vQhM~ zIR8;j;?>+?a~!q}PYYTpf)Zw9+=d(hUJq?VmoiFSSnNJ7Z7BXFnoirpJ%*J#Xd5`2q5yUbeje4Uyy_1)C`u2buZYMl_+|E|PWU-&|&|fT;GEU+iJ4$5;a2|FU)|d^UX4YFR2sX+qToYh0U>NuLrN9c}Yj zJlR=s2}j&6*_O5Q;(4=wSMjf-tFEi@E}q2Po%rO09|JR^&nMCC0G z&W{gOYwkjhleUdu7#F!27vMRm=AV(n_A%}a*T7+8W*)}hcb zv=H$r_(Y_wU30%RWYjLnDkhTdXi$+9%^{7*R;md!ucXrqIQ4Mx8aSg(MgK*5Cd~I! zW2)}W--5oVOZT=OI1z1l;2L>b6hr4Mw^-sjYrBC(Sv14dTb;KE&Td_FSA5L*6L-U# zSP#0JQtqa6JI;2`3pa{NG8~`h0>i{Ap>*CfZ-V-Xp}ZSKt|vEKICf$3!m%eeL)*kU zI5)A+yjY-1+??2(quVFjb98L7S;!P{);zdAc^{Oz3en|HxIM5(`99kZVywcoiB{P~ zP(wiL#k98r_Qlp>GS-}8Fky2U+MW-Svygb#=p?ysn!>T;4 zG}5y5q`vgo>POCuYkPEDK7b#p(MOtxLSu4J*%dq$ol-Ll{3l<~l5e04etvTO;jJ{l zHiGR0TL|Y;>0Q{?K3Fc0l zy1Zbsus*}(pqq!$={*m%3$_fG1C-SE_B1R}8JFR#d$HIjQ59$~El z&Nc^Qu;tXc6vqHl!yv|YJKl%_`Y_#I2f kcvPAilQ%1y7halaWnBl&443rxao%-H>v`uN^lNti22oJLLI3~& diff --git a/source/UI/__pycache__/SetupPageWidget.cpython-311.pyc b/source/UI/__pycache__/SetupPageWidget.cpython-311.pyc index cb626e37032264a267a3d99d1007fb806826120c..a3a70e41e6ae72f3867061986522e6d953d3498c 100644 GIT binary patch delta 5390 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 delta 3870 zcmai1eQaCR6@Sn6^XIeuoH(wXuiA+d!BKJ`!2Tm}6$1lR4R# zwq&d^E9;xmwv0Vy&p2WZ_AR8vj5FrUxMD8$ZBDy0QcPlf3ygVU9@e)4y)IUlsgKoX zyfJU4A=Z%b#e8hcmfn={$NW5J;08I_{tPEOl)8&2jU4wT{HiS$koj>gDt?5Yhoc5v z97tpok{F$sQIeW2?H`}cojEizHaL*TYXzc2dEI(^_~G2tltO^pa75G1M~Vo8@C((8;sX zZ2ydZ?6N_(B&&<-LMkgOvr$nO)Iwe%x-}k8Wm8%_t~cbe@g&py;gqVylhcXplp+%^ z=tCM1&|+PzvQ&*DE_zBFis&X)Nl#Weo7dTOTRfgjCsZ{aR|OyjgY+X~H{V46EN(LU zfS~?_?lCn}i}SqUCP$Y;ft3~Kw9qD@02hK2K$oiP4Wrf%CnnMgvC{|YH=7;E;iePy zFY{*ld3_IWpH;f*@N$P)sXYoYO8A6@oZ_{hp7ugAc5(7U$;R{r9f z;`=W1hodGE05;v2%4%c__Dq^Gt3{2vp{qwXS5+WwFreZl1cT>DnCAU`{3yNT9|2)E z{cR1nQM!;PAVW7N@_8jIlNP!q(Bxwic^H0@FqL*dcg{=C`Mvxhnh(r^05IS7HpqUN z(>RX)H58?%gf?1nG#f}0?RT}&3yog(u5{GV8;#bXBEO$o;vbji4K_}5gTPuV8e~B> z%a*sS7r~R>T=yn!-k6QGa~ke$t%W&qXx;=u+nx36G>$8pDBs@NsNsp%n)ctrk3#Ux zo1QkkFjIY>;m>fh#z(m`JSurqwiS&tctWiGfP2+&5zK!Rw}3UCyH);Ia~F-Y-|eF% zyNiwqf<>4&7X`Arx|F?WoMQAU>Tm)*M+#1tG_!E?Cg*Fi${&a8Bk5 zB$g$Y$7bDnV}4Y@>-gOfRuNoc!+bKd@>R!F3SlLSIGs7@#H3Q)TrN~F@jrsaY} zNyx!OZ9FllDI}Q8WhYZp1@O?RV9lpDah#Eh*3m~=+7<>tbr$KmF`b(t=sLQ1w2;*x zHyt1(M|2}7Lxzy)6Mn6$BU10X8uonr%salNUngCtsR8?|jL*B$1^_AA=MivK_8rIx(6;5yt^Banlg8Q!&-u^ng;8~Rpl%)fu1?d2&SMu$?bo{Ny z;04dBbYEGzuf(1k>qeD=M^>eyW$9>1I?5&tu1Z5?X{aO(Rm`aM=rytJs@V3%{cHZs zYrdw6U<|uH<^VoHfS%J0J*OKt%w4gS)G935 zjTCE2g(yy)Rv>P)1knhtdf8Cb2*s$7J|GUNRpTD$&v|cADGQ>91uTpg=-ZvIfkG!E z@9~FGA%YW01c4x^2(tiRlGqD`IsYvZ^F%qF$`w>rZC^w7-_c)nhUjxqkd76Tf0uqO za+LlpvI!H}4h?s+)@HECvsUCa>w2H#X3XpTS~BGC@ixXc7>az6o565rEpDSQY?!gq z%+^-S_q}SL%zcStW9Du24ONPCv?*kg`65qW+}dC=jB|`a`?gCqxYU^-?+ST*&Qsq@4DvRe$~Bw)!kcm_f|NQEwFai-HZFnVoO=$X#4*S@9> zSR#p0x8Bf0FgxG;J$D=<=HeT3Z=-J3NOzt29U!Qu=!4zS`@p7a;3MH8?bIVH z0q})k+gm9mj{e*8gQ9Xt(YfJ39_xxvT4lHjpC