File size: 11,366 Bytes
ae78832
 
 
10fc241
 
ae78832
 
 
 
 
10fc241
 
 
 
 
 
 
 
 
ae78832
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10fc241
ae78832
 
10fc241
ae78832
10fc241
 
 
 
 
 
 
 
 
 
 
 
 
 
ae78832
 
 
 
10fc241
ae78832
 
10fc241
ae78832
10fc241
 
 
 
 
 
 
 
 
 
 
ae78832
 
 
 
10fc241
ae78832
 
 
 
10fc241
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
from fastapi import APIRouter, WebSocket, WebSocketDisconnect, UploadFile, File, HTTPException
import json
from fastapi.responses import JSONResponse
from lettersControllerS import detectFromImageBytes as detectLetters
from wordsControllerS import detectFromImageBytes as detectWords
from typing import List
from dotenv import load_dotenv
import httpx
import os

os.environ["HF_HOME"] = "/tmp" 
os.environ["HUGGINGFACE_HUB_CACHE"] = "/tmp/huggingface_cache" 
os.makedirs("/tmp/huggingface_cache", exist_ok=True) 
# Ensure these imports are correct
from lettersController import detectFromImage
from wordsController import detectWords
from glossController import translateGloss


router = APIRouter(prefix="/handsUPApi")

class ConnectionManager:
    def __init__(self):
        self.activeConnections: list[WebSocket] = []

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.activeConnections.append(websocket)

    def disconnect(self, websocket: WebSocket):
        if websocket in self.activeConnections:
            self.activeConnections.remove(websocket)

    async def sendJson(self, message: dict, websocket: WebSocket):
        await websocket.send_json(message)

manager = ConnectionManager()

@router.websocket("/ws_translate")
async def websocketEndpoint(websocket: WebSocket):
    await manager.connect(websocket)
    currentFrames = []
    model = None
    sequenceNum = None
    isProcessing = False
    isDynamic = False
    ignoreCount = 0
    stopped = False  

    try:
        while True:
            data = await websocket.receive()

            if 'text' in data:
                msg = json.loads(data['text'])

                if msg['type'] == 'start':
                    if isProcessing:
                        await manager.sendJson({'error': 'Processing in progress'}, websocket)
                        continue
                    currentFrames = []
                    model = msg['model']
                    sequenceNum = msg['sequenceNum']
                    isDynamic = False
                    ignoreCount = 0
                    stopped = False
                    print(f"Started: model={model}, sequenceNum={sequenceNum}")

                elif msg['type'] == 'process':
                    if isProcessing:
                        await manager.sendJson({'error': 'Processing in progress'}, websocket)
                        continue

                    if model in ['alpha', 'num'] and len(currentFrames) not in [1, 2, 10]:
                        await manager.sendJson({'error': f'Invalid frame count: {len(currentFrames)}'}, websocket)
                        currentFrames = []
                        continue
                    if model == 'glosses' and len(currentFrames) < sequenceNum:
                        await manager.sendJson({'error': f'Incomplete sequence: expected {sequenceNum}, got {len(currentFrames)}'}, websocket)
                        currentFrames = []
                        continue

                    isProcessing = True
                    print(f"Processing {len(currentFrames)} frames, model: {model}, isDynamic: {isDynamic}")
                    await manager.sendJson({'status': 'processing'}, websocket)

                    if model in ['alpha', 'num']:
                        result = await detectLetters(currentFrames, websocket, isDynamic)
                        if result.get('status') == 'waitMoreDynamic':
                            isDynamic = True
                        if result.get('status') not in ['waitMore', 'waitMoreDynamic']:
                            await manager.sendJson(result, websocket)
                            currentFrames = []
                            isDynamic = False
                            ignoreCount = 10 if result.get('letter') in ['J', 'Z'] else 6
                        await manager.sendJson({'status': 'ready'}, websocket)

                    elif model == 'glosses':
                        result = detectWords(currentFrames)
                        await manager.sendJson(result, websocket)
                        currentFrames = []
                        ignoreCount = 10
                        await manager.sendJson({'status': 'ready'}, websocket)

                    else:
                        await manager.sendJson({'error': 'Invalid model'}, websocket)

                    isProcessing = False

                elif msg['type'] == 'stop':
                    stopped = True
                    if isProcessing:
                        await manager.sendJson({'error': 'Processing in progress'}, websocket)
                        continue
                    if currentFrames:
                        isProcessing = True
                        print(f"Processing {len(currentFrames)} frames on stop, model: {model}, isDynamic: {isDynamic}")
                        await manager.sendJson({'status': 'processing'}, websocket)

                        if model in ['alpha', 'num']:
                            result = await detectLetters(currentFrames, websocket, isDynamic)
                            if result.get('status') not in ['waitMore', 'waitMoreDynamic']:
                                await manager.sendJson(result, websocket)
                        elif model == 'glosses':
                            result = detectWords(currentFrames)
                            await manager.sendJson(result, websocket)

                        currentFrames = []
                        isDynamic = False
                        ignoreCount = 0
                        isProcessing = False

                    break  

            elif 'bytes' in data and not isProcessing and not stopped:
                if ignoreCount > 0:
                    ignoreCount -= 1
                    continue

                imageBytes = data['bytes']
                currentFrames.append(imageBytes)

                if model is not None and sequenceNum is not None and len(currentFrames) > sequenceNum:
                    currentFrames = currentFrames[-sequenceNum:]

                is_about_to_process = False
                if model in ['alpha', 'num']:
                    if (len(currentFrames) == 2 and not isDynamic) or (len(currentFrames) == 10 and isDynamic):
                        is_about_to_process = True
                elif model == 'glosses' and len(currentFrames) >= sequenceNum:
                    is_about_to_process = True

                if not is_about_to_process:
                    await manager.sendJson({'status': 'collecting'}, websocket)

                if model in ['alpha', 'num']:
                    if len(currentFrames) in [1, 2] or (len(currentFrames) == 10 and isDynamic):
                        isProcessing = True
                        print(f"Processing {len(currentFrames)} frames, model: {model}, isDynamic: {isDynamic}")
                        await manager.sendJson({'status': 'processing'}, websocket)

                        result = await detectLetters(currentFrames, websocket, isDynamic)
                        if result.get('status') == 'waitMoreDynamic':
                            isDynamic = True
                        if result.get('status') not in ['waitMore', 'waitMoreDynamic']:
                            await manager.sendJson(result, websocket)
                            currentFrames = []
                            isDynamic = False
                            ignoreCount = 10 if result.get('letter') in ['J', 'Z'] else 6

                        isProcessing = False
                        await manager.sendJson({'status': 'ready'}, websocket)

                elif model == 'glosses' and len(currentFrames) >= sequenceNum:
                    isProcessing = True
                    print(f"Processing {len(currentFrames)} frames, model: {model}, isDynamic: {isDynamic}")
                    await manager.sendJson({'status': 'processing'}, websocket)

                    result = detectWords(currentFrames)
                    await manager.sendJson(result, websocket)

                    currentFrames = []
                    ignoreCount = 10
                    isProcessing = False
                    await manager.sendJson({'status': 'ready'}, websocket)

    except WebSocketDisconnect:
        manager.disconnect(websocket)
        currentFrames = []
        isProcessing = False
        isDynamic = False
        ignoreCount = 0
        stopped = True

async def sendToHF(url: str, frames: List[UploadFile]):
    files_to_send = [
        ('frames', (frame.filename, await frame.read(), frame.content_type))
        for frame in frames
    ]

    async with httpx.AsyncClient(timeout=300) as client:
        try:
            response = await client.post(url, files=files_to_send)
            response.raise_for_status()
            return response.json()
        except httpx.RequestError as e:
            print(f"Request error: {e}")
            raise HTTPException(status_code=500, detail=str(e))
        except httpx.HTTPStatusError as e:
            print(f"HTTP error: {e.response.text}")
            raise HTTPException(status_code=e.response.status_code, detail=e.response.text)


@router.post("/processLetters")
async def process_letters(frames: List[UploadFile] = File(...)):
    """Processes a sequence of frames to detect sign language letters."""
    sequence_num = 20
    if len(frames) != sequence_num:
        raise HTTPException(status_code=400, detail=f"Exactly {sequence_num} frames are required")

    # CRITICAL: Read the binary content of each file
    # We will pass a list of image bytes (memory buffers), NOT UploadFile objects.
    image_bytes_list = []
    try:
        for frame in frames:
            # frame.file is an async context manager, read() returns bytes
            contents = await frame.read()
            image_bytes_list.append(contents)
    except Exception as e:
        # Handle potential file read errors
        raise HTTPException(status_code=500, detail=f"Error reading uploaded file contents: {e}")

    # Pass the list of image bytes to the controller
    result = detectFromImage(image_bytes_list)
    return JSONResponse(content=result)

@router.post("/processWords")
async def process_words(frames: List[UploadFile] = File(...)):
    """Processes a sequence of frames to detect sign language words."""
    sequence_num = 90
    if len(frames) != sequence_num:
        raise HTTPException(status_code=400, detail=f"Exactly {sequence_num} frames are required")

    # CRITICAL: Read the binary content of each file
    image_bytes_list = []
    try:
        for frame in frames:
            contents = await frame.read()
            image_bytes_list.append(contents)
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error reading uploaded file contents: {e}")

    # Call the imported function directly
    result = detectWords(image_bytes_list)
    return JSONResponse(content=result)

@router.post("/sentence")
async def sign_sentence(data: dict):
    """Generates a signed sentence from a given gloss."""
    gloss_input = data.get("gloss")
    if not gloss_input:
        raise HTTPException(status_code=400, detail="No gloss provided")

    # Call the imported function directly
    result = translateGloss(gloss_input)
    return JSONResponse(content={"translation": result})