ginipick commited on
Commit
9463fc0
·
verified ·
1 Parent(s): 3420b15

Create useHandDetection.js

Browse files
Files changed (1) hide show
  1. hooks/useHandDetection.js +253 -0
hooks/useHandDetection.js ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useEffect, useRef } from 'react';
2
+ import * as tf from '@tensorflow/tfjs';
3
+ import * as cocossd from '@tensorflow-models/coco-ssd';
4
+
5
+ const useCardDetection = (videoRef, canvasRef, isMobile) => {
6
+ const [model, setModel] = useState(null);
7
+ const [cardDetected, setCardDetected] = useState(false);
8
+ const [detectedCards, setDetectedCards] = useState([]);
9
+ const [cardCount, setCardCount] = useState(0);
10
+ const [isFirstLoad, setIsFirstLoad] = useState(true);
11
+
12
+ const requestRef = useRef(null);
13
+ const lastDetectionTimeRef = useRef(0);
14
+ const isComponentMounted = useRef(true);
15
+
16
+ // Initialize the object detection model
17
+ useEffect(() => {
18
+ isComponentMounted.current = true;
19
+
20
+ const loadModel = async () => {
21
+ try {
22
+ // Load COCO-SSD model as a base
23
+ // In production, you'd want to use a custom card detection model
24
+ const loadedModel = await cocossd.load({
25
+ base: 'lite_mobilenet_v2',
26
+ modelUrl: undefined // Use default model
27
+ });
28
+
29
+ if (!isComponentMounted.current) return;
30
+
31
+ setModel(loadedModel);
32
+ console.log("Card detection model loaded successfully");
33
+
34
+ // Set first load to false after initialization
35
+ setTimeout(() => {
36
+ if (isComponentMounted.current) {
37
+ setIsFirstLoad(false);
38
+ }
39
+ }, 3000);
40
+ } catch (error) {
41
+ console.error("Error loading detection model:", error);
42
+ }
43
+ };
44
+
45
+ loadModel();
46
+
47
+ return () => {
48
+ isComponentMounted.current = false;
49
+ if (requestRef.current) {
50
+ cancelAnimationFrame(requestRef.current);
51
+ requestRef.current = null;
52
+ }
53
+ };
54
+ }, []);
55
+
56
+ // Process video frames and detect cards
57
+ useEffect(() => {
58
+ if (!model || !videoRef.current || !canvasRef.current) return;
59
+
60
+ const video = videoRef.current;
61
+ const canvas = canvasRef.current;
62
+ const ctx = canvas.getContext('2d');
63
+
64
+ const detectCards = async (now) => {
65
+ if (!isComponentMounted.current) return;
66
+
67
+ if (video.readyState < 2) {
68
+ requestRef.current = requestAnimationFrame(detectCards);
69
+ return;
70
+ }
71
+
72
+ // Only run detection every 200ms for performance
73
+ if (now - lastDetectionTimeRef.current > 200) {
74
+ lastDetectionTimeRef.current = now;
75
+
76
+ try {
77
+ // Detect objects in the video frame
78
+ const predictions = await model.detect(video);
79
+
80
+ // Clear the canvas
81
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
82
+
83
+ // Draw the video frame
84
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
85
+
86
+ // Filter for card-like objects
87
+ // In a real implementation, you'd have a model trained specifically for cards
88
+ const cardPredictions = predictions.filter(prediction => {
89
+ // This is a placeholder - in reality, you'd detect actual playing cards
90
+ // For demo purposes, we'll look for rectangular objects
91
+ const aspectRatio = prediction.bbox[2] / prediction.bbox[3];
92
+ return aspectRatio > 0.5 && aspectRatio < 0.8 &&
93
+ prediction.score > 0.5;
94
+ });
95
+
96
+ // Draw bounding boxes for detected cards
97
+ ctx.strokeStyle = '#00FF00';
98
+ ctx.lineWidth = 3;
99
+ ctx.font = '18px Arial';
100
+ ctx.fillStyle = '#00FF00';
101
+
102
+ const cards = [];
103
+
104
+ cardPredictions.forEach((prediction, index) => {
105
+ const [x, y, width, height] = prediction.bbox;
106
+
107
+ // Draw bounding box
108
+ ctx.strokeRect(x, y, width, height);
109
+
110
+ // Draw label
111
+ const label = `Card ${index + 1} (${Math.round(prediction.score * 100)}%)`;
112
+ ctx.fillText(label, x, y > 20 ? y - 5 : y + height + 20);
113
+
114
+ // Store card information
115
+ cards.push({
116
+ id: index,
117
+ bbox: prediction.bbox,
118
+ confidence: prediction.score,
119
+ center: {
120
+ x: x + width / 2,
121
+ y: y + height / 2
122
+ }
123
+ });
124
+ });
125
+
126
+ // Update state
127
+ setCardDetected(cards.length > 0);
128
+ setDetectedCards(cards);
129
+ setCardCount(cards.length);
130
+
131
+ } catch (error) {
132
+ console.error("Detection error:", error);
133
+ }
134
+ }
135
+
136
+ requestRef.current = requestAnimationFrame(detectCards);
137
+ };
138
+
139
+ requestRef.current = requestAnimationFrame(detectCards);
140
+
141
+ return () => {
142
+ if (requestRef.current) {
143
+ cancelAnimationFrame(requestRef.current);
144
+ requestRef.current = null;
145
+ }
146
+ };
147
+ }, [model, videoRef, canvasRef]);
148
+
149
+ // Custom card classification function (placeholder)
150
+ const classifyCard = async (imageData) => {
151
+ // In a real implementation, this would:
152
+ // 1. Extract the card region from the image
153
+ // 2. Run it through a card classification model
154
+ // 3. Return the suit and rank
155
+ return {
156
+ suit: 'unknown',
157
+ rank: 'unknown',
158
+ confidence: 0
159
+ };
160
+ };
161
+
162
+ // Function to analyze card patterns
163
+ const analyzeCardPattern = (cards) => {
164
+ // Analyze spatial arrangement of cards
165
+ if (cards.length === 0) return null;
166
+
167
+ // Sort cards by x position (left to right)
168
+ const sortedCards = [...cards].sort((a, b) => a.center.x - b.center.x);
169
+
170
+ // Calculate spread and alignment
171
+ const spread = cards.length > 1 ?
172
+ sortedCards[sortedCards.length - 1].center.x - sortedCards[0].center.x : 0;
173
+
174
+ const avgY = cards.reduce((sum, card) => sum + card.center.y, 0) / cards.length;
175
+ const alignment = cards.every(card => Math.abs(card.center.y - avgY) < 50) ? 'horizontal' : 'scattered';
176
+
177
+ return {
178
+ count: cards.length,
179
+ spread,
180
+ alignment,
181
+ sortedCards
182
+ };
183
+ };
184
+
185
+ return {
186
+ cardDetected,
187
+ detectedCards,
188
+ cardCount,
189
+ isFirstLoad,
190
+ isComponentMounted,
191
+ classifyCard,
192
+ analyzeCardPattern,
193
+ cardPattern: analyzeCardPattern(detectedCards)
194
+ };
195
+ };
196
+
197
+ export default useCardDetection;
198
+
199
+ // Utility functions for card detection
200
+ export const drawCardBoundingBox = (ctx, card, color = '#00FF00') => {
201
+ const [x, y, width, height] = card.bbox;
202
+
203
+ ctx.strokeStyle = color;
204
+ ctx.lineWidth = 2;
205
+ ctx.strokeRect(x, y, width, height);
206
+
207
+ // Draw corner markers
208
+ const cornerLength = 20;
209
+ ctx.lineWidth = 3;
210
+
211
+ // Top-left corner
212
+ ctx.beginPath();
213
+ ctx.moveTo(x, y + cornerLength);
214
+ ctx.lineTo(x, y);
215
+ ctx.lineTo(x + cornerLength, y);
216
+ ctx.stroke();
217
+
218
+ // Top-right corner
219
+ ctx.beginPath();
220
+ ctx.moveTo(x + width - cornerLength, y);
221
+ ctx.lineTo(x + width, y);
222
+ ctx.lineTo(x + width, y + cornerLength);
223
+ ctx.stroke();
224
+
225
+ // Bottom-left corner
226
+ ctx.beginPath();
227
+ ctx.moveTo(x, y + height - cornerLength);
228
+ ctx.lineTo(x, y + height);
229
+ ctx.lineTo(x + cornerLength, y + height);
230
+ ctx.stroke();
231
+
232
+ // Bottom-right corner
233
+ ctx.beginPath();
234
+ ctx.moveTo(x + width - cornerLength, y + height);
235
+ ctx.lineTo(x + width, y + height);
236
+ ctx.lineTo(x + width, y + height - cornerLength);
237
+ ctx.stroke();
238
+ };
239
+
240
+ // Card suit and rank detection utilities
241
+ export const CARD_SUITS = ['hearts', 'diamonds', 'clubs', 'spades'];
242
+ export const CARD_RANKS = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
243
+
244
+ // Placeholder for custom card model predictions
245
+ export const predictCardValue = async (model, imageData) => {
246
+ // This would use a trained model to predict card suit and rank
247
+ // For now, return a placeholder
248
+ return {
249
+ suit: CARD_SUITS[Math.floor(Math.random() * 4)],
250
+ rank: CARD_RANKS[Math.floor(Math.random() * 13)],
251
+ confidence: Math.random()
252
+ };
253
+ };