ginipick commited on
Commit
7a3bccd
ยท
verified ยท
1 Parent(s): f343133

Create useHandDetection.js

Browse files
Files changed (1) hide show
  1. hooks/useHandDetection.js +238 -0
hooks/useHandDetection.js ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useEffect, useRef } from 'react';
2
+ import { HandLandmarker, FilesetResolver } from '@mediapipe/tasks-vision';
3
+ import { drawLandmarks, analyzeHandGesture } from '../utils/handUtils';
4
+
5
+ const useHandDetection = (videoRef, canvasRef, isMobile) => {
6
+ const [handLandmarker, setHandLandmarker] = useState(null);
7
+ const [handDetected, setHandDetected] = useState(false);
8
+ const [cardsDetected, setCardsDetected] = useState(false);
9
+ const [detectedCards, setDetectedCards] = useState([]);
10
+ const [isMouthOpen, setIsMouthOpen] = useState(false);
11
+ const [isLeftHand, setIsLeftHand] = useState(true);
12
+ const [thumbPosition, setThumbPosition] = useState({ x: 0, y: 0 });
13
+ const [isFirstLoad, setIsFirstLoad] = useState(true);
14
+ const [detectionMode, setDetectionMode] = useState('both'); // 'cards', 'hand', 'both'
15
+
16
+ const requestRef = useRef(null);
17
+ const lastDetectionTimeRef = useRef(0);
18
+ const lastCardDetectionTimeRef = useRef(0);
19
+ const isComponentMounted = useRef(true);
20
+
21
+ // Card detection using canvas analysis or Gemini Vision API
22
+ const detectPlayingCards = async (canvas) => {
23
+ try {
24
+ // Canvas๋ฅผ ์ด๋ฏธ์ง€๋กœ ๋ณ€ํ™˜
25
+ const imageData = canvas.toDataURL('image/jpeg', 0.8);
26
+
27
+ // Gemini Vision API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์นด๋“œ ๊ฐ์ง€
28
+ const response = await fetch('/api/gemini', {
29
+ method: 'POST',
30
+ headers: {
31
+ 'Content-Type': 'application/json',
32
+ },
33
+ body: JSON.stringify({
34
+ image: imageData,
35
+ prompt: 'Detect any playing cards in this image. List the cards you see (suit and rank). If no cards are visible, respond with "NO_CARDS".',
36
+ mode: 'card_detection'
37
+ }),
38
+ });
39
+
40
+ if (response.ok) {
41
+ const data = await response.json();
42
+ if (data.result && data.result !== 'NO_CARDS') {
43
+ return {
44
+ detected: true,
45
+ cards: data.result
46
+ };
47
+ }
48
+ }
49
+
50
+ return { detected: false, cards: [] };
51
+ } catch (error) {
52
+ console.error('Error detecting cards:', error);
53
+ return { detected: false, cards: [] };
54
+ }
55
+ };
56
+
57
+ // Initialize the HandLandmarker
58
+ useEffect(() => {
59
+ isComponentMounted.current = true;
60
+
61
+ const initializeHandLandmarker = async () => {
62
+ try {
63
+ const vision = await FilesetResolver.forVisionTasks(
64
+ "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"
65
+ );
66
+
67
+ if (!isComponentMounted.current) return;
68
+
69
+ const landmarker = await HandLandmarker.createFromOptions(vision, {
70
+ baseOptions: {
71
+ modelAssetPath: "https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/latest/hand_landmarker.task",
72
+ delegate: "GPU"
73
+ },
74
+ runningMode: "VIDEO",
75
+ numHands: 1,
76
+ minHandDetectionConfidence: 0.5,
77
+ minHandPresenceConfidence: 0.5,
78
+ minTrackingConfidence: 0.5
79
+ });
80
+
81
+ if (!isComponentMounted.current) return;
82
+
83
+ setHandLandmarker(landmarker);
84
+ console.log("Hand landmarker initialized successfully");
85
+
86
+ // Set first load to false after initialization
87
+ setTimeout(() => {
88
+ if (isComponentMounted.current) {
89
+ setIsFirstLoad(false);
90
+ }
91
+ }, 3000);
92
+ } catch (error) {
93
+ console.error("Error initializing hand landmarker:", error);
94
+ }
95
+ };
96
+
97
+ initializeHandLandmarker();
98
+
99
+ return () => {
100
+ isComponentMounted.current = false;
101
+ if (requestRef.current) {
102
+ cancelAnimationFrame(requestRef.current);
103
+ requestRef.current = null;
104
+ }
105
+ };
106
+ }, []);
107
+
108
+ // Process video frames and detect hand gestures and cards
109
+ useEffect(() => {
110
+ if (!handLandmarker || !videoRef.current || !canvasRef.current) return;
111
+
112
+ const video = videoRef.current;
113
+ const canvas = canvasRef.current;
114
+ const ctx = canvas.getContext('2d');
115
+
116
+ const detectFrame = async (now) => {
117
+ if (!isComponentMounted.current) return;
118
+
119
+ if (video.readyState < 2) {
120
+ requestRef.current = requestAnimationFrame(detectFrame);
121
+ return;
122
+ }
123
+
124
+ // Clear the canvas
125
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
126
+
127
+ // Draw the video frame on the canvas
128
+ const videoWidth = video.videoWidth;
129
+ const videoHeight = video.videoHeight;
130
+
131
+ let drawWidth = canvas.width;
132
+ let drawHeight = canvas.height;
133
+ let offsetX = 0;
134
+ let offsetY = 0;
135
+
136
+ ctx.drawImage(video, offsetX, offsetY, drawWidth, drawHeight);
137
+
138
+ // PRIORITY: Detect playing cards first (every 500ms)
139
+ if (now - lastCardDetectionTimeRef.current > 500) {
140
+ lastCardDetectionTimeRef.current = now;
141
+
142
+ const cardResult = await detectPlayingCards(canvas);
143
+ setCardsDetected(cardResult.detected);
144
+ setDetectedCards(cardResult.cards);
145
+
146
+ // If cards are detected, we can skip hand detection or make it secondary
147
+ if (cardResult.detected) {
148
+ setDetectionMode('cards');
149
+
150
+ // Draw card detection overlay
151
+ ctx.strokeStyle = '#00ff00';
152
+ ctx.lineWidth = 3;
153
+ ctx.font = '20px Arial';
154
+ ctx.fillStyle = '#00ff00';
155
+ ctx.fillText('๐ŸŽด Playing Cards Detected!', 10, 30);
156
+ } else {
157
+ setDetectionMode('hand');
158
+ }
159
+ }
160
+
161
+ // Only detect hands if no cards are detected or in 'both' mode
162
+ if (!cardsDetected || detectionMode === 'both') {
163
+ // Hand detection (every 100ms)
164
+ if (now - lastDetectionTimeRef.current > 100) {
165
+ lastDetectionTimeRef.current = now;
166
+
167
+ const results = handLandmarker.detectForVideo(video, now);
168
+
169
+ // Check if hands are detected
170
+ if (results.landmarks && results.landmarks.length > 0) {
171
+ const landmarks = results.landmarks[0];
172
+ setHandDetected(true);
173
+
174
+ // Draw hand landmarks
175
+ drawLandmarks(ctx, landmarks, canvas, isMobile);
176
+
177
+ // Analyze hand gesture
178
+ const { isOpen, isLeftHand: isLeft, thumbPosition: thumbPos } = analyzeHandGesture(landmarks);
179
+
180
+ // Update state with hand information
181
+ setIsLeftHand(isLeft);
182
+ setThumbPosition({
183
+ x: thumbPos.x * canvas.width,
184
+ y: thumbPos.y * canvas.height
185
+ });
186
+
187
+ // Update UI based on hand state
188
+ if (isOpen !== isMouthOpen) {
189
+ setIsMouthOpen(isOpen);
190
+ }
191
+ } else {
192
+ // No hands detected
193
+ setHandDetected(false);
194
+ if (isMouthOpen) {
195
+ setIsMouthOpen(false);
196
+ }
197
+ }
198
+ }
199
+ }
200
+
201
+ // Draw detection status
202
+ ctx.font = '14px Arial';
203
+ ctx.fillStyle = '#ffffff';
204
+ ctx.strokeStyle = '#000000';
205
+ ctx.lineWidth = 3;
206
+ const statusText = cardsDetected ? '๐ŸŽด Cards Priority Mode' : 'โœ‹ Hand Detection Mode';
207
+ ctx.strokeText(statusText, 10, canvas.height - 20);
208
+ ctx.fillText(statusText, 10, canvas.height - 20);
209
+
210
+ requestRef.current = requestAnimationFrame(detectFrame);
211
+ };
212
+
213
+ requestRef.current = requestAnimationFrame(detectFrame);
214
+
215
+ return () => {
216
+ if (requestRef.current) {
217
+ cancelAnimationFrame(requestRef.current);
218
+ requestRef.current = null;
219
+ }
220
+ };
221
+ }, [handLandmarker, isMouthOpen, isMobile, videoRef, canvasRef, cardsDetected, detectionMode]);
222
+
223
+ return {
224
+ handDetected,
225
+ cardsDetected,
226
+ detectedCards,
227
+ isMouthOpen,
228
+ isLeftHand,
229
+ thumbPosition,
230
+ isFirstLoad,
231
+ isComponentMounted,
232
+ detectionMode,
233
+ // ์šฐ์„ ์ˆœ์œ„ ๊ธฐ๋ฐ˜ ๊ฐ์ง€ ์ƒํƒœ ๋ฐ˜ํ™˜
234
+ primaryDetection: cardsDetected ? 'cards' : (handDetected ? 'hand' : 'none')
235
+ };
236
+ };
237
+
238
+ export default useHandDetection;