fagrrr commited on
Commit
c802d72
·
1 Parent(s): 6053897

Initial Hugging Face deployment

Browse files
.gitattributes CHANGED
@@ -1,35 +1,4 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
  *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.sh text eol=lf
2
+ Procfile text eol=lf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  *.pt filter=lfs diff=lfs merge=lfs -text
4
  *.pth filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
.gitignore ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .venv/
2
+ venv/
3
+ __pycache__/
4
+ *.pyc
5
+ .env
6
+ *.npz
7
+ *.pth
8
+ #*.pt
9
+ *.onnx
10
+ *.mp4
11
+ # Datasets
12
+ assets/text_to_sign/wlasl2000_filtered_keypoints/
13
+
14
+ # OS junk
15
+ .DS_Store
16
+ Thumbs.db
Dockerfile ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN apt-get update && apt-get install -y \
6
+ libgl1 \
7
+ libglib2.0-0 \
8
+ ffmpeg \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ COPY requirements.txt .
12
+
13
+ RUN pip install --upgrade pip
14
+ RUN pip install --no-cache-dir -r requirements.txt
15
+
16
+ COPY . .
17
+
18
+ RUN chmod +x start.sh
19
+
20
+ EXPOSE 7860
21
+
22
+ CMD ["bash", "start.sh"]
app/api/sign_to_text.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, UploadFile, File
2
+ import tempfile
3
+ import os
4
+ from app.services.sign_to_text_service import run_pipeline
5
+
6
+ router = APIRouter()
7
+
8
+
9
+ @router.post("/")
10
+ async def sign_to_text(video: UploadFile = File(...)):
11
+
12
+ # read file into memory
13
+ contents = await video.read()
14
+
15
+ # create a safe temporary file (auto deleted)
16
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp:
17
+ tmp.write(contents)
18
+ temp_path = tmp.name
19
+
20
+ try:
21
+ result = run_pipeline(temp_path)
22
+ return result
23
+
24
+ finally:
25
+ os.remove(temp_path)
app/api/text_to_sign.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter
2
+ from fastapi.responses import FileResponse
3
+ from pydantic import BaseModel
4
+ from app.services.text_to_sign_service import generate_video
5
+
6
+ router = APIRouter()
7
+
8
+ class TextRequest(BaseModel):
9
+ text: str
10
+
11
+
12
+ @router.post("/")
13
+ def text_to_sign(req: TextRequest):
14
+
15
+ video_path = generate_video(req.text)
16
+
17
+ return FileResponse(
18
+ path=video_path,
19
+ media_type="video/mp4",
20
+ filename="sign.mp4"
21
+ )
app/core/config.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
4
+
5
+ I3D_WEIGHTS = os.path.join(
6
+ BASE_DIR,
7
+ # "assets/I3D_300Weights/FINAL_nslt_300_iters2997_top156.14_top579.94_top1086.98.pt"
8
+ "assets/I3D_300Weights/FINAL_nslt_100_iters=896_top1=65.89_top5=84.11_top10=89.92 (1).pt"
9
+ )
10
+
11
+ CLASS_LIST = os.path.join(
12
+ BASE_DIR,
13
+ "assets/I3D_modelfiles/wlasl_class_list.txt"
14
+ )
app/core/logger.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import logging
2
+
3
+ logging.basicConfig(level=logging.INFO)
4
+
5
+ def log(msg):
6
+ logging.info(msg)
app/main.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from app.api import sign_to_text, text_to_sign
3
+ from fastapi.staticfiles import StaticFiles
4
+
5
+ app = FastAPI(title="Signova API")
6
+
7
+ app.include_router(sign_to_text.router, prefix="/sign-to-text")
8
+ app.include_router(text_to_sign.router, prefix="/text-to-sign")
9
+
10
+ @app.get("/")
11
+ def home():
12
+ return {"message": "Signova API running"}
app/models/i3d_model.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from assets.I3D_modelfiles.pytorch_i3d import InceptionI3d
3
+
4
+ class I3DModel:
5
+ def __init__(self, weights_path, device):
6
+ self.device = device
7
+
8
+ self.model = InceptionI3d(400, in_channels=3)
9
+ self.model.replace_logits(100)
10
+ self.model.load_state_dict(torch.load(weights_path, map_location=device))
11
+ self.model.to(device)
12
+ self.model.eval()
13
+
14
+ def __call__(self, x):
15
+ return self.model(x)
app/services/sign_to_text_service.py ADDED
@@ -0,0 +1,461 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import torch
3
+ import torch.nn.functional as F
4
+ import numpy as np
5
+ import os
6
+ import requests
7
+
8
+ from dotenv import load_dotenv
9
+
10
+ from app.models.i3d_model import I3DModel
11
+ from app.core.config import I3D_WEIGHTS, CLASS_LIST
12
+ from app.utils.mediapipe_utils import extract_hand_status
13
+
14
+
15
+ # ================= LOAD ENV =================
16
+
17
+ load_dotenv()
18
+
19
+
20
+ # ================= CONFIG =================
21
+
22
+ clip_length = 64
23
+
24
+ device = torch.device(
25
+ "cuda" if torch.cuda.is_available() else "cpu"
26
+ )
27
+
28
+ MIN_PAUSE_FRAMES = 5
29
+ MIN_SIGN_FRAMES = 10
30
+
31
+ TARGET_FPS = 25
32
+
33
+
34
+ # ================= LOAD MODEL =================
35
+
36
+ print(f"Loading I3D model on {device}...")
37
+
38
+ i3d = I3DModel(I3D_WEIGHTS, device)
39
+
40
+ with open(CLASS_LIST, "r", encoding="utf-8") as f:
41
+
42
+ gloss_list = []
43
+
44
+ for line in f.readlines():
45
+
46
+ line = line.strip()
47
+
48
+ if not line:
49
+ continue
50
+
51
+ # FIX:
52
+ # "205 FEEL" -> FEEL
53
+ parts = line.split(maxsplit=1)
54
+
55
+ if len(parts) == 2:
56
+ gloss = parts[1]
57
+ else:
58
+ gloss = parts[0]
59
+
60
+ gloss_list.append(gloss.upper())
61
+
62
+
63
+ # ================= LOAD VIDEO =================
64
+
65
+ def load_video(video_path):
66
+
67
+ print("Processing video...")
68
+
69
+ cap = cv2.VideoCapture(video_path)
70
+
71
+ if not cap.isOpened():
72
+ raise Exception(f"Cannot open video: {video_path}")
73
+
74
+ original_fps = cap.get(cv2.CAP_PROP_FPS)
75
+
76
+ if original_fps <= 0:
77
+ original_fps = 30
78
+
79
+ print(f"Original FPS: {original_fps}")
80
+
81
+ frame_skip = max(1, round(original_fps / TARGET_FPS))
82
+
83
+ print(f"Frame skip: {frame_skip}")
84
+
85
+ frames = []
86
+ hand_flags = []
87
+
88
+ frame_idx = 0
89
+
90
+ while True:
91
+
92
+ ret, frame = cap.read()
93
+
94
+ if not ret:
95
+ break
96
+
97
+ # IMPORTANT:
98
+ # match kaggle decoding behavior
99
+ if frame_idx % frame_skip != 0:
100
+ frame_idx += 1
101
+ continue
102
+
103
+ resized = cv2.resize(frame, (224, 224))
104
+
105
+ norm = (resized.astype(np.float32) / 255.0) * 2 - 1
106
+
107
+ frames.append(norm)
108
+
109
+ hand_flags.append(
110
+ extract_hand_status(frame)
111
+ )
112
+
113
+ frame_idx += 1
114
+
115
+ if len(frames) % 100 == 0:
116
+ print(f"Processed {len(frames)} frames...")
117
+
118
+ cap.release()
119
+
120
+ frames = np.array(frames)
121
+
122
+ print(f"Loaded {len(frames)} frames")
123
+
124
+ return frames, hand_flags
125
+
126
+
127
+ # ================= SEGMENTATION =================
128
+
129
+ def segment_frames(frames, hand_flags):
130
+
131
+ segments = []
132
+
133
+ start = 0
134
+ pause = 0
135
+
136
+ for i in range(len(frames)):
137
+
138
+ # TRUE = pause
139
+ if hand_flags[i]:
140
+
141
+ pause += 1
142
+
143
+ else:
144
+
145
+ if pause >= MIN_PAUSE_FRAMES:
146
+
147
+ end = i - pause
148
+
149
+ if end - start >= MIN_SIGN_FRAMES:
150
+ segments.append((start, end))
151
+
152
+ start = i
153
+
154
+ pause = 0
155
+
156
+ if len(frames) - start >= MIN_SIGN_FRAMES:
157
+ segments.append((start, len(frames) - 1))
158
+
159
+ return segments
160
+
161
+
162
+ # ================= MODEL =================
163
+
164
+ def predict_segment_topk(segment_frames, topk=5):
165
+
166
+ if len(segment_frames) == 0:
167
+ return []
168
+
169
+ indices = np.linspace(
170
+ 0,
171
+ len(segment_frames) - 1,
172
+ clip_length
173
+ ).astype(int)
174
+
175
+ clip = segment_frames[indices]
176
+
177
+ clip = clip.transpose(3, 0, 1, 2)
178
+
179
+ clip_tensor = (
180
+ torch.from_numpy(clip)
181
+ .unsqueeze(0)
182
+ .float()
183
+ .to(device)
184
+ )
185
+
186
+ with torch.no_grad():
187
+
188
+ logits = i3d(clip_tensor)
189
+
190
+ logits = torch.mean(logits, dim=2)
191
+
192
+ probs = F.softmax(logits, dim=1)
193
+
194
+ top_probs, top_indices = torch.topk(
195
+ probs,
196
+ k=topk,
197
+ dim=1
198
+ )
199
+
200
+ top_probs = top_probs.squeeze().cpu().numpy()
201
+ top_indices = top_indices.squeeze().cpu().numpy()
202
+
203
+ results = []
204
+
205
+ for idx, prob in zip(top_indices, top_probs):
206
+
207
+ gloss = gloss_list[int(idx)]
208
+
209
+ results.append((gloss, float(prob)))
210
+
211
+ return results
212
+
213
+
214
+ # ================= SMART SELECTION =================
215
+
216
+ def select_best_gloss(topk_results, context):
217
+
218
+ candidates = [g for g, _ in topk_results]
219
+
220
+ top1 = candidates[0]
221
+
222
+ if len(context) == 0:
223
+ return top1
224
+
225
+ context_set = set(context)
226
+
227
+ def score(word, is_top1=False):
228
+
229
+ s = 0
230
+
231
+ if word in context_set:
232
+ s += 2
233
+
234
+ if is_top1:
235
+ s += 1
236
+
237
+ return s
238
+
239
+ best_word = top1
240
+ best_score = score(top1, True)
241
+
242
+ for i, word in enumerate(candidates):
243
+
244
+ s = score(word, i == 0)
245
+
246
+ if s > best_score:
247
+
248
+ best_score = s
249
+ best_word = word
250
+
251
+ return best_word
252
+
253
+
254
+ # ================= GROQ =================
255
+
256
+ def translate_with_groq(gloss):
257
+
258
+ gloss = gloss.strip()
259
+
260
+ if not gloss:
261
+ return ""
262
+
263
+ # ==========================
264
+ # SINGLE WORD -> SKIP LLM
265
+ # ==========================
266
+ words = gloss.split()
267
+
268
+ if len(words) == 1:
269
+
270
+ word = words[0]
271
+
272
+ return word.replace("_", " ").title()
273
+
274
+ api_key = os.getenv("GROQ_API_KEY")
275
+
276
+ if not api_key:
277
+ return "Missing GROQ_API_KEY"
278
+
279
+ url = "https://api.groq.com/openai/v1/chat/completions"
280
+
281
+ headers = {
282
+ "Authorization": f"Bearer {api_key}",
283
+ "Content-Type": "application/json"
284
+ }
285
+
286
+ data = {
287
+ "model": "llama-3.1-8b-instant",
288
+ "messages": [
289
+ {
290
+ "role": "system",
291
+ "content": """
292
+ You are an ASL gloss to English translator.
293
+
294
+ Rules:
295
+ - Return ONLY the English translation.
296
+ - Never explain the translation.
297
+ - Never say:
298
+ 'The ASL gloss for...'
299
+ 'Translation:'
300
+ 'English:'
301
+ 'This means...'
302
+ - If input is a sentence, produce one natural English sentence.
303
+ - Output only the translated text.
304
+ """
305
+ },
306
+ {
307
+ "role": "user",
308
+ "content": gloss
309
+ }
310
+ ],
311
+ "temperature": 0.2,
312
+ "max_tokens": 100
313
+ }
314
+
315
+ try:
316
+
317
+ r = requests.post(
318
+ url,
319
+ headers=headers,
320
+ json=data
321
+ )
322
+
323
+ if r.status_code != 200:
324
+ return r.text
325
+
326
+ result = (
327
+ r.json()["choices"][0]["message"]["content"]
328
+ .strip()
329
+ )
330
+
331
+ bad_prefixes = [
332
+ "The ASL gloss for",
333
+ "Translation:",
334
+ "English:",
335
+ "This means"
336
+ ]
337
+
338
+ for prefix in bad_prefixes:
339
+
340
+ if result.startswith(prefix):
341
+ return gloss
342
+
343
+ return result
344
+
345
+ except Exception as e:
346
+ return str(e)
347
+ api_key = os.getenv("GROQ_API_KEY")
348
+
349
+ if not api_key:
350
+ return "Missing GROQ_API_KEY"
351
+
352
+ url = "https://api.groq.com/openai/v1/chat/completions"
353
+
354
+ headers = {
355
+ "Authorization": f"Bearer {api_key}",
356
+ "Content-Type": "application/json"
357
+ }
358
+
359
+ data = {
360
+ "model": "llama-3.1-8b-instant",
361
+ "messages": [
362
+ {
363
+ "role": "system",
364
+ "content": "You convert ASL gloss into natural English."
365
+ },
366
+ {
367
+ "role": "user",
368
+ "content": f"""
369
+ Convert ASL gloss to English:
370
+
371
+ {gloss}
372
+
373
+ Rules:
374
+ - one sentence
375
+ - correct grammar
376
+ """
377
+ }
378
+ ],
379
+ "temperature": 0.2,
380
+ "max_tokens": 100
381
+ }
382
+
383
+ try:
384
+
385
+ r = requests.post(
386
+ url,
387
+ headers=headers,
388
+ json=data
389
+ )
390
+
391
+ if r.status_code != 200:
392
+ return r.text
393
+
394
+ return r.json()["choices"][0]["message"]["content"].strip()
395
+
396
+ except Exception as e:
397
+ return str(e)
398
+
399
+
400
+ # ================= MAIN =================
401
+
402
+ def run_pipeline(video_path):
403
+
404
+ print("\n" + "=" * 60)
405
+ print("STARTING SIGN TO TEXT")
406
+ print("=" * 60)
407
+
408
+ frames, hand_flags = load_video(video_path)
409
+
410
+ print("Hand flags sample:", hand_flags[:20])
411
+
412
+ segments = segment_frames(frames, hand_flags)
413
+
414
+ print("Segments:", segments)
415
+
416
+ final_glosses = []
417
+
418
+ for idx, (s, e) in enumerate(segments):
419
+
420
+ segment_frames_data = frames[s:e]
421
+
422
+ results = predict_segment_topk(
423
+ segment_frames_data,
424
+ topk=5
425
+ )
426
+
427
+ print(f"\n--- Segment {idx+1} ({s}-{e}) ---")
428
+
429
+ for i, (g, p) in enumerate(results):
430
+
431
+ print(f"{i+1}. {g} ({p:.4f})")
432
+
433
+ if len(results) == 0:
434
+ continue
435
+
436
+ selected = select_best_gloss(
437
+ results,
438
+ final_glosses
439
+ )
440
+
441
+ print(f"Selected: {selected}")
442
+
443
+ final_glosses.append(selected)
444
+
445
+ gloss_sentence = " ".join(final_glosses)
446
+
447
+ print("\nFINAL GLOSS:", gloss_sentence)
448
+
449
+ english = ""
450
+
451
+ if gloss_sentence.strip():
452
+ english = translate_with_groq(
453
+ gloss_sentence
454
+ )
455
+
456
+ print("ENGLISH:", english)
457
+
458
+ return {
459
+ "gloss": gloss_sentence,
460
+ "english": english
461
+ }
app/services/text_to_sign_service.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ from pipelines.text_to_sign.run import run_pipeline
2
+
3
+ from app.models.i3d_model import I3DModel # (optional future use)
4
+ from app.core.config import CLASS_LIST
5
+
6
+
7
+ def generate_video(text):
8
+ return run_pipeline(text)
app/utils/mediapipe_utils.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ hands = None
3
+ HAND_LOWER_THRESHOLD = 0.85
4
+ def get_hands():
5
+ global hands
6
+
7
+ if hands is None:
8
+ import mediapipe as mp
9
+
10
+ print("Loading MediaPipe...")
11
+ hands = mp.solutions.hands.Hands(
12
+ static_image_mode=False,
13
+ max_num_hands=2
14
+ )
15
+
16
+ return hands
17
+
18
+ def extract_hand_status(frame):
19
+
20
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
21
+
22
+ results = get_hands().process(frame_rgb)
23
+
24
+ if results.multi_hand_landmarks:
25
+ for hand in results.multi_hand_landmarks:
26
+ for lm in hand.landmark:
27
+ if lm.y < HAND_LOWER_THRESHOLD:
28
+ return False
29
+
30
+ return True
app/utils/preprocessing.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ def sample_clip(frames, clip_length=64):
4
+ idx = np.linspace(0, len(frames)-1, clip_length).astype(int)
5
+ return frames[idx]
app/utils/video_utils.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #unused file
2
+ import cv2
3
+ import numpy as np
4
+
5
+ def load_video(path, size=(224,224)):
6
+ cap = cv2.VideoCapture(path)
7
+ frames = []
8
+
9
+ while True:
10
+ ret, frame = cap.read()
11
+ if not ret:
12
+ break
13
+
14
+ frame = cv2.resize(frame, size)
15
+ frame = (frame.astype(np.float32) / 255.0) * 2 - 1
16
+ frames.append(frame)
17
+
18
+ cap.release()
19
+ return np.array(frames)
assets/I3D_300Weights/FINAL_nslt_100_iters=896_top1=65.89_top5=84.11_top10=89.92 (1).pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a61d7dda5f875ce5ebd9d407c56874f77d1cd2aeb4bc7cd0d98a6e1ca4669a0c
3
+ size 49677763
assets/I3D_300Weights/FINAL_nslt_300_iters2997_top156.14_top579.94_top1086.98.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:493baa483880d9a0ac88ff30a749e2a89287a737c16bd9d1e9e119723c617662
3
+ size 50498038
assets/I3D_modelfiles/language.py ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Sat Mar 19 09:37:10 2022
4
+
5
+ @author: 24412
6
+ """
7
+
8
+ from itertools import chain
9
+ import math
10
+ import os
11
+
12
+
13
+ from dotenv import load_dotenv
14
+ import nltk
15
+ import random
16
+ import attr
17
+
18
+ from collections import Counter
19
+
20
+ load_dotenv("posts/nlp/.env", override=True)
21
+ @attr.s(auto_attribs=True)
22
+ class NGrams:
23
+ """The N-Gram Language Model
24
+
25
+ Args:
26
+ data: the training data
27
+ n: the size of the n-grams
28
+ start_token: string to represent the start of a sentence
29
+ end_token: string to represent the end of a sentence
30
+ """
31
+ data: list
32
+ n: int
33
+ start_token: str="<s>"
34
+ end_token: str="<e>"
35
+ _start_tokens: list=None
36
+ _end_tokens: list=None
37
+ _sentences: list=None
38
+ _n_grams: list=None
39
+ _counts: dict=None
40
+
41
+ @property
42
+ def start_tokens(self) -> list:
43
+ """List of 'n' start tokens"""
44
+ if self._start_tokens is None:
45
+ self._start_tokens = [self.start_token] * self.n
46
+ return self._start_tokens
47
+
48
+ @property
49
+ def end_tokens(self) -> list:
50
+ """List of 1 end-tokens"""
51
+ if self._end_tokens is None:
52
+ self._end_tokens = [self.end_token]
53
+ return self._end_tokens
54
+
55
+ @property
56
+ def sentences(self) -> list:
57
+ """The data augmented with tags and converted to tuples"""
58
+ if self._sentences is None:
59
+ self._sentences = [tuple(self.start_tokens + sentence + self.end_tokens)
60
+ for sentence in self.data]
61
+ return self._sentences
62
+
63
+ @property
64
+ def n_grams(self) -> list:
65
+ """The n-grams from the data
66
+
67
+ Warning:
68
+ this flattens the n-grams so there isn't any sentence structure
69
+ """
70
+ if self._n_grams is None:
71
+ self._n_grams = chain.from_iterable([
72
+ [sentence[cut: cut + self.n] for cut in range(0, len(sentence) - (self.n - 1))]
73
+ for sentence in self.sentences
74
+ ])
75
+ return self._n_grams
76
+
77
+ @property
78
+ def counts(self) -> Counter:
79
+ """A count of all n-grams in the data
80
+
81
+ Returns:
82
+ A dictionary that maps a tuple of n-words to its frequency
83
+ """
84
+ if self._counts is None:
85
+ self._counts = Counter(self.n_grams)
86
+ return self._counts
87
+
88
+ @attr.s(auto_attribs=True)
89
+ class Tokenizer:
90
+ """Tokenizes string sentences
91
+
92
+ Args:
93
+ source: string data to tokenize
94
+ end_of_sentence: what to split sentences on
95
+
96
+ """
97
+ source: str
98
+ end_of_sentence: str="\n"
99
+ _sentences: list=None
100
+ _tokenized: list=None
101
+ _training_data: list=None
102
+
103
+
104
+ @property
105
+ def sentences(self) -> list:
106
+ """The data split into sentences"""
107
+ if self._sentences is None:
108
+ self._sentences = self.source.split(self.end_of_sentence)
109
+ self._sentences = (sentence.strip() for sentence in self._sentences)
110
+ self._sentences = [sentence for sentence in self._sentences if sentence]
111
+ return self._sentences
112
+
113
+ @property
114
+ def tokenized(self) -> list:
115
+ """List of tokenized sentence"""
116
+ if self._tokenized is None:
117
+ self._tokenized = [nltk.word_tokenize(sentence.lower())
118
+ for sentence in self.sentences]
119
+ return self._tokenized
120
+
121
+
122
+ @attr.s(auto_attribs=True)
123
+ class TrainTestSplit:
124
+ """splits up the training and testing sets
125
+
126
+ Args:
127
+ data: list of data to split
128
+ training_fraction: how much to put in the training set
129
+ seed: something to seed the random call
130
+ """
131
+ data: list
132
+ training_fraction: float=0.8
133
+ seed: int=87
134
+ _shuffled: list=None
135
+ _training: list=None
136
+ _testing: list=None
137
+ _split: int=None
138
+
139
+ @property
140
+ def shuffled(self) -> list:
141
+ """The data shuffled"""
142
+ if self._shuffled is None:
143
+ random.seed(self.seed)
144
+ self._shuffled = random.sample(self.data, k=len(self.data))
145
+ return self._shuffled
146
+
147
+ @property
148
+ def split(self) -> int:
149
+ """The slice value for training and testing"""
150
+ if self._split is None:
151
+ self._split = int(len(self.data) * self.training_fraction)
152
+ return self._split
153
+
154
+ @property
155
+ def training(self) -> list:
156
+ """The Training Portion of the Set"""
157
+ if self._training is None:
158
+ self._training = self.shuffled[0:self.split]
159
+ return self._training
160
+
161
+ @property
162
+ def testing(self) -> list:
163
+ """The testing data"""
164
+ if self._testing is None:
165
+ self._testing = self.shuffled[self.split:]
166
+ return self._testing
167
+ def estimate_probability(word: str,
168
+ previous_n_gram: tuple,
169
+ n_gram_counts: dict,
170
+ n_plus1_gram_counts: dict,
171
+ vocabulary_size: int,
172
+ k: float=1.0) -> float:
173
+ """
174
+ Estimate the probabilities of a next word using the n-gram counts with k-smoothing
175
+
176
+ Args:
177
+ word: next word
178
+ previous_n_gram: A sequence of words of length n
179
+ n_gram_counts: Dictionary of counts of n-grams
180
+ n_plus1_gram_counts: Dictionary of counts of (n+1)-grams
181
+ vocabulary_size: number of words in the vocabulary
182
+ k: positive constant, smoothing parameter
183
+
184
+ Returns:
185
+ A probability
186
+ """
187
+ previous_n_gram = tuple(previous_n_gram)
188
+ previous_n_gram_count = n_gram_counts.get(previous_n_gram, 0)
189
+
190
+ n_plus1_gram = previous_n_gram + (word,)
191
+ n_plus1_gram_count = n_plus1_gram_counts.get(n_plus1_gram, 0)
192
+ return (n_plus1_gram_count + k)/(previous_n_gram_count + k * vocabulary_size)
193
+
194
+
195
+ def estimate_probabilities(previous_n_gram, n_gram_counts, n_plus1_gram_counts, vocabulary, k=1.0):
196
+ """
197
+ Estimate the probabilities of next words using the n-gram counts with k-smoothing
198
+
199
+ Args:
200
+ previous_n_gram: A sequence of words of length n
201
+ n_gram_counts: Dictionary of counts of (n+1)-grams
202
+ n_plus1_gram_counts: Dictionary of counts of (n+1)-grams
203
+ vocabulary: List of words
204
+ k: positive constant, smoothing parameter
205
+
206
+ Returns:
207
+ A dictionary mapping from next words to the probability.
208
+ """
209
+
210
+ # convert list to tuple to use it as a dictionary key
211
+ previous_n_gram = tuple(previous_n_gram)
212
+
213
+ # add <e> <unk> to the vocabulary
214
+ # <s> is not needed since it should not appear as the next word
215
+ vocabulary = vocabulary + ["<e>", "<unk>"]
216
+ vocabulary_size = len(vocabulary)
217
+
218
+ probabilities = {}
219
+ for word in vocabulary:
220
+ probability = estimate_probability(word, previous_n_gram,
221
+ n_gram_counts, n_plus1_gram_counts,
222
+ vocabulary_size, k=k)
223
+ probabilities[word] = probability
224
+
225
+ return probabilities
226
+
227
+ def suggest_a_word(previous_tokens, n_gram_counts, n_plus1_gram_counts, vocabulary, k=1.0, start_with=None):
228
+ """
229
+ Get suggestion for the next word
230
+
231
+ Args:
232
+ previous_tokens: The sentence you input where each token is a word. Must have length > n
233
+ n_gram_counts: Dictionary of counts of (n+1)-grams
234
+ n_plus1_gram_counts: Dictionary of counts of (n+1)-grams
235
+ vocabulary: List of words
236
+ k: positive constant, smoothing parameter
237
+ start_with: If not None, specifies the first few letters of the next word
238
+
239
+ Returns:
240
+ A tuple of
241
+ - string of the most likely next word
242
+ - corresponding probability
243
+ """
244
+
245
+ # length of previous words
246
+ n = len(list(n_gram_counts.keys())[0])
247
+
248
+ # From the words that the user already typed
249
+ # get the most recent 'n' words as the previous n-gram
250
+ previous_n_gram = previous_tokens[-n:]
251
+
252
+ # Estimate the probabilities that each word in the vocabulary
253
+ # is the next word,
254
+ # given the previous n-gram, the dictionary of n-gram counts,
255
+ # the dictionary of n plus 1 gram counts, and the smoothing constant
256
+ probabilities = estimate_probabilities(previous_n_gram,
257
+ n_gram_counts, n_plus1_gram_counts,
258
+ vocabulary, k=k)
259
+
260
+ # Initialize suggested word to None
261
+ # This will be set to the word with highest probability
262
+ suggestion = None
263
+
264
+ # Initialize the highest word probability to 0
265
+ # this will be set to the highest probability
266
+ # of all words to be suggested
267
+ max_prob = 0
268
+
269
+ ### START CODE HERE (Replace instances of 'None' with your code) ###
270
+
271
+ # For each word and its probability in the probabilities dictionary:
272
+ for word, prob in probabilities.items(): # complete this line
273
+
274
+ # If the optional start_with string is set
275
+ if start_with is not None: # complete this line
276
+
277
+ # Check if the beginning of word does not match with the letters in 'start_with'
278
+ if not word.startswith(start_with): # complete this line
279
+
280
+ # if they don't match, skip this word (move onto the next word)
281
+ continue # complete this line
282
+
283
+ # Check if this word's probability
284
+ # is greater than the current maximum probability
285
+ if prob > max_prob: # complete this line
286
+
287
+ # If so, save this word as the best suggestion (so far)
288
+ suggestion = word
289
+
290
+ # Save the new maximum probability
291
+ max_prob = prob
292
+
293
+ ### END CODE HERE
294
+
295
+ return suggestion, max_prob
296
+
297
+
298
+ def get_suggestions(previous_tokens, n_gram_counts_list, vocabulary, k=1.0, start_with=None):
299
+ #for i in range(model_counts-1):
300
+ n_gram_counts = n_gram_counts_list[0]
301
+ n_plus1_gram_counts = n_gram_counts_list[1]
302
+
303
+ suggestion = suggest_a_word(previous_tokens, n_gram_counts,
304
+ n_plus1_gram_counts, vocabulary,
305
+ k=k, start_with=start_with)
306
+ return " " + str(suggestion[0])
307
+
308
+
assets/I3D_modelfiles/pytorch_i3d.py ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ from torch.autograd import Variable
5
+
6
+ import numpy as np
7
+
8
+ import os
9
+ import sys
10
+ from collections import OrderedDict
11
+
12
+
13
+ class MaxPool3dSamePadding(nn.MaxPool3d):
14
+
15
+ def compute_pad(self, dim, s):
16
+ if s % self.stride[dim] == 0:
17
+ return max(self.kernel_size[dim] - self.stride[dim], 0)
18
+ else:
19
+ return max(self.kernel_size[dim] - (s % self.stride[dim]), 0)
20
+
21
+ def forward(self, x):
22
+ # compute 'same' padding
23
+ (batch, channel, t, h, w) = x.size()
24
+ #print t,h,w
25
+ out_t = np.ceil(float(t) / float(self.stride[0]))
26
+ out_h = np.ceil(float(h) / float(self.stride[1]))
27
+ out_w = np.ceil(float(w) / float(self.stride[2]))
28
+ #print out_t, out_h, out_w
29
+ pad_t = self.compute_pad(0, t)
30
+ pad_h = self.compute_pad(1, h)
31
+ pad_w = self.compute_pad(2, w)
32
+ #print pad_t, pad_h, pad_w
33
+
34
+ pad_t_f = pad_t // 2
35
+ pad_t_b = pad_t - pad_t_f
36
+ pad_h_f = pad_h // 2
37
+ pad_h_b = pad_h - pad_h_f
38
+ pad_w_f = pad_w // 2
39
+ pad_w_b = pad_w - pad_w_f
40
+
41
+ pad = (pad_w_f, pad_w_b, pad_h_f, pad_h_b, pad_t_f, pad_t_b)
42
+ #print x.size()
43
+ #print pad
44
+ x = F.pad(x, pad)
45
+ return super(MaxPool3dSamePadding, self).forward(x)
46
+
47
+
48
+ class Unit3D(nn.Module):
49
+
50
+ def __init__(self, in_channels,
51
+ output_channels,
52
+ kernel_shape=(1, 1, 1),
53
+ stride=(1, 1, 1),
54
+ padding=0,
55
+ activation_fn=F.relu,
56
+ use_batch_norm=True,
57
+ use_bias=False,
58
+ name='unit_3d'):
59
+
60
+ """Initializes Unit3D module."""
61
+ super(Unit3D, self).__init__()
62
+
63
+ self._output_channels = output_channels
64
+ self._kernel_shape = kernel_shape
65
+ self._stride = stride
66
+ self._use_batch_norm = use_batch_norm
67
+ self._activation_fn = activation_fn
68
+ self._use_bias = use_bias
69
+ self.name = name
70
+ self.padding = padding
71
+
72
+ self.conv3d = nn.Conv3d(in_channels=in_channels,
73
+ out_channels=self._output_channels,
74
+ kernel_size=self._kernel_shape,
75
+ stride=self._stride,
76
+ padding=0, # we always want padding to be 0 here. We will dynamically pad based on input size in forward function
77
+ bias=self._use_bias)
78
+
79
+ if self._use_batch_norm:
80
+ self.bn = nn.BatchNorm3d(self._output_channels, eps=0.001, momentum=0.01)
81
+
82
+ def compute_pad(self, dim, s):
83
+ if s % self._stride[dim] == 0:
84
+ return max(self._kernel_shape[dim] - self._stride[dim], 0)
85
+ else:
86
+ return max(self._kernel_shape[dim] - (s % self._stride[dim]), 0)
87
+
88
+
89
+ def forward(self, x):
90
+ # compute 'same' padding
91
+ (batch, channel, t, h, w) = x.size()
92
+ #print t,h,w
93
+ out_t = np.ceil(float(t) / float(self._stride[0]))
94
+ out_h = np.ceil(float(h) / float(self._stride[1]))
95
+ out_w = np.ceil(float(w) / float(self._stride[2]))
96
+ #print out_t, out_h, out_w
97
+ pad_t = self.compute_pad(0, t)
98
+ pad_h = self.compute_pad(1, h)
99
+ pad_w = self.compute_pad(2, w)
100
+ #print pad_t, pad_h, pad_w
101
+
102
+ pad_t_f = pad_t // 2
103
+ pad_t_b = pad_t - pad_t_f
104
+ pad_h_f = pad_h // 2
105
+ pad_h_b = pad_h - pad_h_f
106
+ pad_w_f = pad_w // 2
107
+ pad_w_b = pad_w - pad_w_f
108
+
109
+ pad = (pad_w_f, pad_w_b, pad_h_f, pad_h_b, pad_t_f, pad_t_b)
110
+ #print x.size()
111
+ #print pad
112
+ x = F.pad(x, pad)
113
+ #print x.size()
114
+
115
+ x = self.conv3d(x)
116
+ if self._use_batch_norm:
117
+ x = self.bn(x)
118
+ if self._activation_fn is not None:
119
+ x = self._activation_fn(x)
120
+ return x
121
+
122
+
123
+
124
+ class InceptionModule(nn.Module):
125
+ def __init__(self, in_channels, out_channels, name):
126
+ super(InceptionModule, self).__init__()
127
+
128
+ self.b0 = Unit3D(in_channels=in_channels, output_channels=out_channels[0], kernel_shape=[1, 1, 1], padding=0,
129
+ name=name+'/Branch_0/Conv3d_0a_1x1')
130
+ self.b1a = Unit3D(in_channels=in_channels, output_channels=out_channels[1], kernel_shape=[1, 1, 1], padding=0,
131
+ name=name+'/Branch_1/Conv3d_0a_1x1')
132
+ self.b1b = Unit3D(in_channels=out_channels[1], output_channels=out_channels[2], kernel_shape=[3, 3, 3],
133
+ name=name+'/Branch_1/Conv3d_0b_3x3')
134
+ self.b2a = Unit3D(in_channels=in_channels, output_channels=out_channels[3], kernel_shape=[1, 1, 1], padding=0,
135
+ name=name+'/Branch_2/Conv3d_0a_1x1')
136
+ self.b2b = Unit3D(in_channels=out_channels[3], output_channels=out_channels[4], kernel_shape=[3, 3, 3],
137
+ name=name+'/Branch_2/Conv3d_0b_3x3')
138
+ self.b3a = MaxPool3dSamePadding(kernel_size=[3, 3, 3],
139
+ stride=(1, 1, 1), padding=0)
140
+ self.b3b = Unit3D(in_channels=in_channels, output_channels=out_channels[5], kernel_shape=[1, 1, 1], padding=0,
141
+ name=name+'/Branch_3/Conv3d_0b_1x1')
142
+ self.name = name
143
+
144
+ def forward(self, x):
145
+ b0 = self.b0(x)
146
+ b1 = self.b1b(self.b1a(x))
147
+ b2 = self.b2b(self.b2a(x))
148
+ b3 = self.b3b(self.b3a(x))
149
+ return torch.cat([b0,b1,b2,b3], dim=1)
150
+
151
+
152
+ class InceptionI3d(nn.Module):
153
+ """Inception-v1 I3D architecture.
154
+ The model is introduced in:
155
+ Quo Vadis, Action Recognition? A New Model and the Kinetics Dataset
156
+ Joao Carreira, Andrew Zisserman
157
+ https://arxiv.org/pdf/1705.07750v1.pdf.
158
+ See also the Inception architecture, introduced in:
159
+ Going deeper with convolutions
160
+ Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed,
161
+ Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, Andrew Rabinovich.
162
+ http://arxiv.org/pdf/1409.4842v1.pdf.
163
+ """
164
+
165
+ # Endpoints of the model in order. During construction, all the endpoints up
166
+ # to a designated `final_endpoint` are returned in a dictionary as the
167
+ # second return value.
168
+ VALID_ENDPOINTS = (
169
+ 'Conv3d_1a_7x7',
170
+ 'MaxPool3d_2a_3x3',
171
+ 'Conv3d_2b_1x1',
172
+ 'Conv3d_2c_3x3',
173
+ 'MaxPool3d_3a_3x3',
174
+ 'Mixed_3b',
175
+ 'Mixed_3c',
176
+ 'MaxPool3d_4a_3x3',
177
+ 'Mixed_4b',
178
+ 'Mixed_4c',
179
+ 'Mixed_4d',
180
+ 'Mixed_4e',
181
+ 'Mixed_4f',
182
+ 'MaxPool3d_5a_2x2',
183
+ 'Mixed_5b',
184
+ 'Mixed_5c',
185
+ 'Logits',
186
+ 'Predictions',
187
+ )
188
+
189
+ def __init__(self, num_classes=400, spatial_squeeze=True,
190
+ final_endpoint='Logits', name='inception_i3d', in_channels=3, dropout_keep_prob=0.5):
191
+ """Initializes I3D model instance.
192
+ Args:
193
+ num_classes: The number of outputs in the logit layer (default 400, which
194
+ matches the Kinetics dataset).
195
+ spatial_squeeze: Whether to squeeze the spatial dimensions for the logits
196
+ before returning (default True).
197
+ final_endpoint: The model contains many possible endpoints.
198
+ `final_endpoint` specifies the last endpoint for the model to be built
199
+ up to. In addition to the output at `final_endpoint`, all the outputs
200
+ at endpoints up to `final_endpoint` will also be returned, in a
201
+ dictionary. `final_endpoint` must be one of
202
+ InceptionI3d.VALID_ENDPOINTS (default 'Logits').
203
+ name: A string (optional). The name of this module.
204
+ Raises:
205
+ ValueError: if `final_endpoint` is not recognized.
206
+ """
207
+
208
+ if final_endpoint not in self.VALID_ENDPOINTS:
209
+ raise ValueError('Unknown final endpoint %s' % final_endpoint)
210
+
211
+ super(InceptionI3d, self).__init__()
212
+ self._num_classes = num_classes
213
+ self._spatial_squeeze = spatial_squeeze
214
+ self._final_endpoint = final_endpoint
215
+ self.logits = None
216
+
217
+ if self._final_endpoint not in self.VALID_ENDPOINTS:
218
+ raise ValueError('Unknown final endpoint %s' % self._final_endpoint)
219
+
220
+ self.end_points = {}
221
+ end_point = 'Conv3d_1a_7x7'
222
+ self.end_points[end_point] = Unit3D(in_channels=in_channels, output_channels=64, kernel_shape=[7, 7, 7],
223
+ stride=(2, 2, 2), padding=(3,3,3), name=name+end_point)
224
+ if self._final_endpoint == end_point: return
225
+
226
+ end_point = 'MaxPool3d_2a_3x3'
227
+ self.end_points[end_point] = MaxPool3dSamePadding(kernel_size=[1, 3, 3], stride=(1, 2, 2),
228
+ padding=0)
229
+ if self._final_endpoint == end_point: return
230
+
231
+ end_point = 'Conv3d_2b_1x1'
232
+ self.end_points[end_point] = Unit3D(in_channels=64, output_channels=64, kernel_shape=[1, 1, 1], padding=0,
233
+ name=name+end_point)
234
+ if self._final_endpoint == end_point: return
235
+
236
+ end_point = 'Conv3d_2c_3x3'
237
+ self.end_points[end_point] = Unit3D(in_channels=64, output_channels=192, kernel_shape=[3, 3, 3], padding=1,
238
+ name=name+end_point)
239
+ if self._final_endpoint == end_point: return
240
+
241
+ end_point = 'MaxPool3d_3a_3x3'
242
+ self.end_points[end_point] = MaxPool3dSamePadding(kernel_size=[1, 3, 3], stride=(1, 2, 2),
243
+ padding=0)
244
+ if self._final_endpoint == end_point: return
245
+
246
+ end_point = 'Mixed_3b'
247
+ self.end_points[end_point] = InceptionModule(192, [64,96,128,16,32,32], name+end_point)
248
+ if self._final_endpoint == end_point: return
249
+
250
+ end_point = 'Mixed_3c'
251
+ self.end_points[end_point] = InceptionModule(256, [128,128,192,32,96,64], name+end_point)
252
+ if self._final_endpoint == end_point: return
253
+
254
+ end_point = 'MaxPool3d_4a_3x3'
255
+ self.end_points[end_point] = MaxPool3dSamePadding(kernel_size=[3, 3, 3], stride=(2, 2, 2),
256
+ padding=0)
257
+ if self._final_endpoint == end_point: return
258
+
259
+ end_point = 'Mixed_4b'
260
+ self.end_points[end_point] = InceptionModule(128+192+96+64, [192,96,208,16,48,64], name+end_point)
261
+ if self._final_endpoint == end_point: return
262
+
263
+ end_point = 'Mixed_4c'
264
+ self.end_points[end_point] = InceptionModule(192+208+48+64, [160,112,224,24,64,64], name+end_point)
265
+ if self._final_endpoint == end_point: return
266
+
267
+ end_point = 'Mixed_4d'
268
+ self.end_points[end_point] = InceptionModule(160+224+64+64, [128,128,256,24,64,64], name+end_point)
269
+ if self._final_endpoint == end_point: return
270
+
271
+ end_point = 'Mixed_4e'
272
+ self.end_points[end_point] = InceptionModule(128+256+64+64, [112,144,288,32,64,64], name+end_point)
273
+ if self._final_endpoint == end_point: return
274
+
275
+ end_point = 'Mixed_4f'
276
+ self.end_points[end_point] = InceptionModule(112+288+64+64, [256,160,320,32,128,128], name+end_point)
277
+ if self._final_endpoint == end_point: return
278
+
279
+ end_point = 'MaxPool3d_5a_2x2'
280
+ self.end_points[end_point] = MaxPool3dSamePadding(kernel_size=[2, 2, 2], stride=(2, 2, 2),
281
+ padding=0)
282
+ if self._final_endpoint == end_point: return
283
+
284
+ end_point = 'Mixed_5b'
285
+ self.end_points[end_point] = InceptionModule(256+320+128+128, [256,160,320,32,128,128], name+end_point)
286
+ if self._final_endpoint == end_point: return
287
+
288
+ end_point = 'Mixed_5c'
289
+ self.end_points[end_point] = InceptionModule(256+320+128+128, [384,192,384,48,128,128], name+end_point)
290
+ if self._final_endpoint == end_point: return
291
+
292
+ end_point = 'Logits'
293
+ self.avg_pool = nn.AvgPool3d(kernel_size=[2, 7, 7],
294
+ stride=(1, 1, 1))
295
+ self.dropout = nn.Dropout(dropout_keep_prob)
296
+ self.logits = Unit3D(in_channels=384+384+128+128, output_channels=self._num_classes,
297
+ kernel_shape=[1, 1, 1],
298
+ padding=0,
299
+ activation_fn=None,
300
+ use_batch_norm=False,
301
+ use_bias=True,
302
+ name='logits')
303
+
304
+ self.build()
305
+
306
+
307
+ def replace_logits(self, num_classes):
308
+ self._num_classes = num_classes
309
+ self.logits = Unit3D(in_channels=384+384+128+128, output_channels=self._num_classes,
310
+ kernel_shape=[1, 1, 1],
311
+ padding=0,
312
+ activation_fn=None,
313
+ use_batch_norm=False,
314
+ use_bias=True,
315
+ name='logits')
316
+
317
+ def build(self):
318
+ for k in self.end_points.keys():
319
+ self.add_module(k, self.end_points[k])
320
+
321
+ def forward(self, x, pretrained=False, n_tune_layers=-1):
322
+ if pretrained:
323
+ assert n_tune_layers >= 0
324
+
325
+ freeze_endpoints = self.VALID_ENDPOINTS[:-n_tune_layers]
326
+ tune_endpoints = self.VALID_ENDPOINTS[-n_tune_layers:]
327
+ else:
328
+ freeze_endpoints = []
329
+ tune_endpoints = self.VALID_ENDPOINTS
330
+
331
+ # backbone, no gradient part
332
+ with torch.no_grad():
333
+ for end_point in freeze_endpoints:
334
+ if end_point in self.end_points:
335
+ x = self._modules[end_point](x) # use _modules to work with dataparallel
336
+
337
+ # backbone, gradient part
338
+ for end_point in tune_endpoints:
339
+ if end_point in self.end_points:
340
+ x = self._modules[end_point](x) # use _modules to work with dataparallel
341
+
342
+ # head
343
+ x = self.logits(self.dropout(self.avg_pool(x)))
344
+ if self._spatial_squeeze:
345
+ logits = x.squeeze(3).squeeze(3)
346
+ # logits is batch X time X classes, which is what we want to work with
347
+ return logits
348
+
349
+
350
+ def extract_features(self, x):
351
+ for end_point in self.VALID_ENDPOINTS:
352
+ if end_point in self.end_points:
353
+ x = self._modules[end_point](x)
354
+ return self.avg_pool(x)
assets/I3D_modelfiles/run.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Fri Feb 25 20:10:06 2022
4
+
5
+ @author: 24412
6
+ """
7
+
8
+ import math
9
+ import os
10
+ import argparse
11
+
12
+ import matplotlib.pyplot as plt
13
+
14
+ import torch
15
+ import torch.nn as nn
16
+
17
+
18
+ import numpy as np
19
+
20
+ import torch.nn.functional as F
21
+ from pytorch_i3d import InceptionI3d
22
+
23
+ import cv2
24
+ from keytotext import pipeline
25
+
26
+ import language
27
+ from dotenv import load_dotenv
28
+
29
+ from itertools import chain
30
+
31
+ import pickle
32
+
33
+ load_dotenv("posts/nlp/.env", override=True)
34
+
35
+
36
+ os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
37
+ os.environ["CUDA_VISIBLE_DEVICES"] = '0'
38
+ parser = argparse.ArgumentParser()
39
+ parser.add_argument('-mode', type=str, help='rgb or flow')
40
+ parser.add_argument('-save_model', type=str)
41
+ parser.add_argument('-root', type=str)
42
+
43
+ args = parser.parse_args()
44
+
45
+ def load_rgb_frames_from_video():
46
+
47
+ vidcap = cv2.VideoCapture(0)
48
+
49
+
50
+ frames = []
51
+
52
+ offset = 0
53
+ text = " "
54
+ batch = 40
55
+ text_list = []
56
+ word_list = []
57
+ sentence = ""
58
+ text_count = 0
59
+
60
+ """
61
+ To maintain the continous flow of actions we bring in the the batch size and offest modulo factor.
62
+ the batch size and the offset can be varied.
63
+
64
+ """
65
+
66
+ while True:
67
+ ret, frame1 = vidcap.read()
68
+ offset = offset + 1
69
+ font = cv2.FONT_HERSHEY_TRIPLEX
70
+
71
+ if ret == True:
72
+
73
+ w, h, c = frame1.shape
74
+ sc = 224 / w
75
+ sx = 224 / h
76
+ frame = cv2.resize(frame1, dsize=(0, 0), fx=sx, fy=sc)
77
+ frame1 = cv2.resize(frame1, dsize = (1280,720))
78
+
79
+ frame = (frame / 255.) * 2 - 1
80
+
81
+ if offset > batch:
82
+ frames.pop(0)
83
+ frames.append(frame)
84
+
85
+ if offset % 20 == 0:
86
+ text = run_on_tensor(torch.from_numpy((np.asarray(frames, dtype=np.float32)).transpose([3, 0, 1, 2])))
87
+ if text != " ":
88
+ text_count = text_count + 1
89
+
90
+ if bool(text_list) != False and bool(word_list) != False and text_list[-1] != text and word_list[-1] != text or bool(text_list) == False:
91
+ text_list.append(text)
92
+ word_list.append(text)
93
+ sentence = sentence + " " + text
94
+
95
+ word = language.get_suggestions(text_list, n_gram_counts_list, vocabulary, k = 1.0)
96
+ if(word != " ."):
97
+ sentence += word
98
+ text_list.append(word)
99
+
100
+ if(text_count > 2):
101
+ sentence = nlp(text_list,**params)
102
+ cv2.putText(frame1, sentence, (120, 520), font, 0.9, (0, 255, 255), 2, cv2.LINE_4)
103
+
104
+
105
+ else:
106
+ frames.append(frame)
107
+ if offset == batch:
108
+ text = run_on_tensor(torch.from_numpy((np.asarray(frames, dtype=np.float32)).transpose([3, 0, 1, 2])))
109
+ if text != " ":
110
+ text_count = text_count + 1
111
+ if bool(text_list) != False and bool(word_list) != False and text_list[-1] != text and word_list[-1] != text or bool(text_list) == False:
112
+ text_list.append(text)
113
+ word_list.append(text)
114
+ sentence = sentence + " " + text
115
+
116
+ word = language.get_suggestions(text_list, n_gram_counts_list, vocabulary, k = 1.0)
117
+ if(word != " ." ):
118
+ sentence += word
119
+ text_list.append(word)
120
+
121
+ if(text_count > 2):
122
+ sentence = nlp(text_list,**params)
123
+ cv2.putText(frame1, sentence, (120, 520), font, 0.9, (0, 255, 255), 2, cv2.LINE_4)
124
+
125
+
126
+ if cv2.waitKey(1) & 0xFF == ord('q'):
127
+ break
128
+
129
+ cv2.putText(frame1, sentence, (120, 520), font, 0.9, (0, 255, 255), 2, cv2.LINE_4)
130
+ cv2.imshow('frame', frame1)
131
+
132
+ if len(text_list) > 10:
133
+ text_list.pop()
134
+ text_list.pop()
135
+ text_list.pop()
136
+
137
+ else:
138
+ break
139
+
140
+ vidcap.release()
141
+ cv2.destroyAllWindows()
142
+
143
+
144
+
145
+ def load_model(weights, num_classes):
146
+
147
+ #Loading the Inception 3D Model
148
+
149
+ global i3d
150
+ i3d = InceptionI3d(400, in_channels=3)
151
+
152
+ i3d.replace_logits(num_classes)
153
+ i3d.load_state_dict(torch.load(weights)) # nslt_2000_000700.pt nslt_1000_010800 nslt_300_005100.pt(best_results) nslt_300_005500.pt(results_reported) nslt_2000_011400
154
+ i3d.cuda()
155
+ i3d = nn.DataParallel(i3d)
156
+ i3d.eval()
157
+
158
+ #Loading the KeytoText model
159
+
160
+ global nlp
161
+ nlp = pipeline("k2t-new") # The pre-trained models available are 'k2t', 'k2t-base', 'mrm8488/t5-base-finetuned-common_gen', 'k2t-new'
162
+ global params
163
+ params = {"do_sample":True, "num_beams": 5, "no_repeat_ngram_size":2, "early_stopping":True}
164
+
165
+ #Loading the NGram model
166
+
167
+ with open("NLP/nlp_data_processed", "rb") as fp: # Unpickling
168
+ train_data_processed = pickle.load(fp)
169
+
170
+ global n_gram_counts_list
171
+ with open("NLP/nlp_gram_counts", "rb") as fp: # Unpickling
172
+ n_gram_counts_list = pickle.load(fp)
173
+
174
+ global vocabulary
175
+ vocabulary = list(set(chain.from_iterable(train_data_processed)))
176
+
177
+
178
+ load_rgb_frames_from_video()
179
+
180
+
181
+ def run_on_tensor(ip_tensor):
182
+
183
+ ip_tensor = ip_tensor[None, :]
184
+
185
+ t = ip_tensor.shape[2]
186
+ ip_tensor.cuda()
187
+ per_frame_logits = i3d(ip_tensor)
188
+
189
+ predictions = F.upsample(per_frame_logits, t, mode='linear')
190
+
191
+ predictions = predictions.transpose(2, 1)
192
+ out_labels = np.argsort(predictions.cpu().detach().numpy()[0])
193
+ arr = predictions.cpu().detach().numpy()[0]
194
+
195
+ print(float(max(F.softmax(torch.from_numpy(arr[0]), dim=0))))
196
+ print(wlasl_dict[out_labels[0][-1]])
197
+
198
+ """
199
+
200
+ The 0.5 is threshold value, it varies if the batch sizes are reduced.
201
+
202
+ """
203
+ if max(F.softmax(torch.from_numpy(arr[0]), dim=0)) > 0.5:
204
+ return wlasl_dict[out_labels[0][-1]]
205
+ else:
206
+ return " "
207
+
208
+
209
+ def create_WLASL_dictionary():
210
+
211
+ global wlasl_dict
212
+ wlasl_dict = {}
213
+
214
+ with open('preprocess/wlasl_class_list.txt') as file:
215
+ for line in file:
216
+ split_list = line.split()
217
+ if len(split_list) != 2:
218
+ key = int(split_list[0])
219
+ value = split_list[1] + " " + split_list[2]
220
+ else:
221
+ key = int(split_list[0])
222
+ value = split_list[1]
223
+ wlasl_dict[key] = value
224
+
225
+
226
+ if __name__ == '__main__':
227
+
228
+ # ================== test i3d on a dataset ==============
229
+ # need to add argparse
230
+
231
+ mode = 'rgb'
232
+ num_classes = 2000
233
+ save_model = './checkpoints/'
234
+
235
+ root = '../../data/WLASL2000'
236
+
237
+ train_split = 'preprocess/nslt_{}.json'.format(num_classes)
238
+ weights = 'archived/asl2000/FINAL_nslt_2000_iters=5104_top1=32.48_top5=57.31_top10=66.31.pt'
239
+
240
+
241
+ create_WLASL_dictionary()
242
+
243
+ load_model(weights, num_classes)
244
+
245
+
246
+
assets/I3D_modelfiles/wlasl_class_list.txt ADDED
@@ -0,0 +1,2000 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 0 book
2
+ 1 drink
3
+ 2 computer
4
+ 3 before
5
+ 4 chair
6
+ 5 go
7
+ 6 clothes
8
+ 7 who
9
+ 8 candy
10
+ 9 cousin
11
+ 10 deaf
12
+ 11 fine
13
+ 12 help
14
+ 13 no
15
+ 14 thin
16
+ 15 walk
17
+ 16 year
18
+ 17 yes
19
+ 18 all
20
+ 19 black
21
+ 20 cool
22
+ 21 finish
23
+ 22 hot
24
+ 23 like
25
+ 24 many
26
+ 25 mother
27
+ 26 now
28
+ 27 orange
29
+ 28 table
30
+ 29 thanksgiving
31
+ 30 what
32
+ 31 woman
33
+ 32 bed
34
+ 33 blue
35
+ 34 bowling
36
+ 35 can
37
+ 36 dog
38
+ 37 family
39
+ 38 fish
40
+ 39 graduate
41
+ 40 hat
42
+ 41 hearing
43
+ 42 kiss
44
+ 43 language
45
+ 44 later
46
+ 45 man
47
+ 46 shirt
48
+ 47 study
49
+ 48 tall
50
+ 49 white
51
+ 50 wrong
52
+ 51 accident
53
+ 52 apple
54
+ 53 bird
55
+ 54 change
56
+ 55 color
57
+ 56 corn
58
+ 57 cow
59
+ 58 dance
60
+ 59 dark
61
+ 60 doctor
62
+ 61 eat
63
+ 62 enjoy
64
+ 63 forget
65
+ 64 give
66
+ 65 last
67
+ 66 meet
68
+ 67 pink
69
+ 68 pizza
70
+ 69 play
71
+ 70 school
72
+ 71 secretary
73
+ 72 short
74
+ 73 time
75
+ 74 want
76
+ 75 work
77
+ 76 africa
78
+ 77 basketball
79
+ 78 birthday
80
+ 79 brown
81
+ 80 but
82
+ 81 cheat
83
+ 82 city
84
+ 83 cook
85
+ 84 decide
86
+ 85 full
87
+ 86 how
88
+ 87 jacket
89
+ 88 letter
90
+ 89 medicine
91
+ 90 need
92
+ 91 paint
93
+ 92 paper
94
+ 93 pull
95
+ 94 purple
96
+ 95 right
97
+ 96 same
98
+ 97 son
99
+ 98 tell
100
+ 99 thursday
101
+ 100 visit
102
+ 101 wait
103
+ 102 water
104
+ 103 wife
105
+ 104 yellow
106
+ 105 backpack
107
+ 106 bar
108
+ 107 brother
109
+ 108 cat
110
+ 109 check
111
+ 110 class
112
+ 111 cry
113
+ 112 different
114
+ 113 door
115
+ 114 green
116
+ 115 hair
117
+ 116 have
118
+ 117 headache
119
+ 118 inform
120
+ 119 knife
121
+ 120 laugh
122
+ 121 learn
123
+ 122 movie
124
+ 123 rabbit
125
+ 124 read
126
+ 125 red
127
+ 126 room
128
+ 127 run
129
+ 128 show
130
+ 129 sick
131
+ 130 snow
132
+ 131 take
133
+ 132 tea
134
+ 133 teacher
135
+ 134 week
136
+ 135 why
137
+ 136 with
138
+ 137 write
139
+ 138 yesterday
140
+ 139 again
141
+ 140 bad
142
+ 141 ball
143
+ 142 bathroom
144
+ 143 blanket
145
+ 144 buy
146
+ 145 call
147
+ 146 coffee
148
+ 147 cold
149
+ 148 college
150
+ 149 copy
151
+ 150 cute
152
+ 151 daughter
153
+ 152 example
154
+ 153 far
155
+ 154 first
156
+ 155 friend
157
+ 156 good
158
+ 157 happy
159
+ 158 home
160
+ 159 know
161
+ 160 late
162
+ 161 leave
163
+ 162 list
164
+ 163 losear
165
+ 164 name
166
+ 165 old
167
+ 166 person
168
+ 167 police
169
+ 168 problem
170
+ 169 remember
171
+ 170 share
172
+ 171 soon
173
+ 172 stay
174
+ 173 sunday
175
+ 174 test
176
+ 175 tired
177
+ 176 trade
178
+ 177 travel
179
+ 178 window
180
+ 179 you
181
+ 180 about
182
+ 181 approve
183
+ 182 arrive
184
+ 183 balance
185
+ 184 banana
186
+ 185 beard
187
+ 186 because
188
+ 187 boy
189
+ 188 business
190
+ 189 careful
191
+ 190 center
192
+ 191 chat
193
+ 192 children
194
+ 193 christmas
195
+ 194 clock
196
+ 195 close
197
+ 196 convince
198
+ 197 country
199
+ 198 crash
200
+ 199 day
201
+ 200 discuss
202
+ 201 dress
203
+ 202 drive
204
+ 203 drop
205
+ 204 fat
206
+ 205 feel
207
+ 206 football
208
+ 207 future
209
+ 208 game
210
+ 209 girl
211
+ 210 government
212
+ 211 hear
213
+ 212 here
214
+ 213 hope
215
+ 214 house
216
+ 215 husband
217
+ 216 interest
218
+ 217 join
219
+ 218 light
220
+ 219 live
221
+ 220 make
222
+ 221 mean
223
+ 222 more
224
+ 223 most
225
+ 224 music
226
+ 225 new
227
+ 226 none
228
+ 227 office
229
+ 228 order
230
+ 229 pants
231
+ 230 party
232
+ 231 past
233
+ 232 pencil
234
+ 233 plan
235
+ 234 please
236
+ 235 practice
237
+ 236 president
238
+ 237 restaurant
239
+ 238 ride
240
+ 239 russia
241
+ 240 salt
242
+ 241 sandwich
243
+ 242 sign
244
+ 243 since
245
+ 244 small
246
+ 245 some
247
+ 246 south
248
+ 247 student
249
+ 248 teach
250
+ 249 theory
251
+ 250 tomato
252
+ 251 train
253
+ 252 ugly
254
+ 253 war
255
+ 254 where
256
+ 255 your
257
+ 256 always
258
+ 257 animal
259
+ 258 argue
260
+ 259 baby
261
+ 260 back
262
+ 261 bake
263
+ 262 bath
264
+ 263 behind
265
+ 264 bring
266
+ 265 catch
267
+ 266 cereal
268
+ 267 champion
269
+ 268 cheese
270
+ 269 cough
271
+ 270 crazy
272
+ 271 delay
273
+ 272 delicious
274
+ 273 disappear
275
+ 274 divorce
276
+ 275 draw
277
+ 276 east
278
+ 277 easy
279
+ 278 egg
280
+ 279 environment
281
+ 280 father
282
+ 281 fault
283
+ 282 flower
284
+ 283 friendly
285
+ 284 glasses
286
+ 285 halloween
287
+ 286 hard
288
+ 287 heart
289
+ 288 hour
290
+ 289 humble
291
+ 290 hurry
292
+ 291 improve
293
+ 292 internet
294
+ 293 jump
295
+ 294 kill
296
+ 295 law
297
+ 296 match
298
+ 297 meat
299
+ 298 milk
300
+ 299 money
301
+ 300 month
302
+ 301 moon
303
+ 302 move
304
+ 303 near
305
+ 304 nephew
306
+ 305 nice
307
+ 306 niece
308
+ 307 noon
309
+ 308 north
310
+ 309 not
311
+ 310 nurse
312
+ 311 off
313
+ 312 ok
314
+ 313 patient
315
+ 314 pay
316
+ 315 perspective
317
+ 316 potato
318
+ 317 sad
319
+ 318 saturday
320
+ 319 save
321
+ 320 scissors
322
+ 321 secret
323
+ 322 shoes
324
+ 323 shop
325
+ 324 silly
326
+ 325 sister
327
+ 326 sleep
328
+ 327 sorry
329
+ 328 straight
330
+ 329 sweet
331
+ 330 talk
332
+ 331 temperature
333
+ 332 tent
334
+ 333 thank you
335
+ 334 think
336
+ 335 throw
337
+ 336 today
338
+ 337 traffic
339
+ 338 understand
340
+ 339 use
341
+ 340 voice
342
+ 341 vomit
343
+ 342 vote
344
+ 343 wednesday
345
+ 344 west
346
+ 345 wet
347
+ 346 when
348
+ 347 which
349
+ 348 win
350
+ 349 word
351
+ 350 afternoon
352
+ 351 age
353
+ 352 alone
354
+ 353 appointment
355
+ 354 australia
356
+ 355 avoid
357
+ 356 balloon
358
+ 357 basement
359
+ 358 bear
360
+ 359 believe
361
+ 360 better
362
+ 361 blind
363
+ 362 bored
364
+ 363 bowl
365
+ 364 box
366
+ 365 bracelet
367
+ 366 bread
368
+ 367 cafeteria
369
+ 368 car
370
+ 369 chicken
371
+ 370 child
372
+ 371 choose
373
+ 372 church
374
+ 373 cookie
375
+ 374 cup
376
+ 375 cut
377
+ 376 decorate
378
+ 377 deep
379
+ 378 deer
380
+ 379 dentist
381
+ 380 dirty
382
+ 381 dive
383
+ 382 down
384
+ 383 dry
385
+ 384 ear
386
+ 385 earn
387
+ 386 earring
388
+ 387 elephant
389
+ 388 english
390
+ 389 escape
391
+ 390 expensive
392
+ 391 explain
393
+ 392 fast
394
+ 393 fight
395
+ 394 find
396
+ 395 fishing
397
+ 396 floor
398
+ 397 fly
399
+ 398 follow
400
+ 399 from
401
+ 400 get
402
+ 401 happen
403
+ 402 hello
404
+ 403 hit
405
+ 404 hospital
406
+ 405 idea
407
+ 406 important
408
+ 407 investigate
409
+ 408 japan
410
+ 409 jealous
411
+ 410 king
412
+ 411 kitchen
413
+ 412 large
414
+ 413 last year
415
+ 414 lemon
416
+ 415 lettuce
417
+ 416 marry
418
+ 417 meeting
419
+ 418 minute
420
+ 419 mirror
421
+ 420 miss
422
+ 421 monkey
423
+ 422 morning
424
+ 423 motorcycle
425
+ 424 necklace
426
+ 425 never
427
+ 426 newspaper
428
+ 427 night
429
+ 428 onion
430
+ 429 people
431
+ 430 pepper
432
+ 431 phone
433
+ 432 picture
434
+ 433 plus
435
+ 434 point
436
+ 435 poor
437
+ 436 possible
438
+ 437 quiet
439
+ 438 rain
440
+ 439 ready
441
+ 440 research
442
+ 441 scared
443
+ 442 science
444
+ 443 score
445
+ 444 sentence
446
+ 445 shape
447
+ 446 sit
448
+ 447 slow
449
+ 448 snake
450
+ 449 soap
451
+ 450 soda
452
+ 451 speech
453
+ 452 star
454
+ 453 stink
455
+ 454 struggle
456
+ 455 stubborn
457
+ 456 sunset
458
+ 457 surgery
459
+ 458 there
460
+ 459 tiger
461
+ 460 toast
462
+ 461 toilet
463
+ 462 tomorrow
464
+ 463 town
465
+ 464 transfer
466
+ 465 tree
467
+ 466 truck
468
+ 467 uncle
469
+ 468 vacation
470
+ 469 weather
471
+ 470 weekend
472
+ 471 accept
473
+ 472 adult
474
+ 473 after
475
+ 474 ago
476
+ 475 allow
477
+ 476 america
478
+ 477 angel
479
+ 478 answer
480
+ 479 any
481
+ 480 area
482
+ 481 art
483
+ 482 asl
484
+ 483 aunt
485
+ 484 awful
486
+ 485 awkward
487
+ 486 bacon
488
+ 487 bark
489
+ 488 bedroom
490
+ 489 beer
491
+ 490 belt
492
+ 491 big
493
+ 492 bitter
494
+ 493 both
495
+ 494 cake
496
+ 495 california
497
+ 496 canada
498
+ 497 carrot
499
+ 498 cause
500
+ 499 challenge
501
+ 500 cheap
502
+ 501 chocolate
503
+ 502 clean
504
+ 503 cloud
505
+ 504 compare
506
+ 505 complex
507
+ 506 contact
508
+ 507 continue
509
+ 508 corner
510
+ 509 correct
511
+ 510 curse
512
+ 511 dead
513
+ 512 demand
514
+ 513 depend
515
+ 514 desert
516
+ 515 desk
517
+ 516 develop
518
+ 517 dictionary
519
+ 518 doll
520
+ 519 duty
521
+ 520 easter
522
+ 521 egypt
523
+ 522 elevator
524
+ 523 email
525
+ 524 end
526
+ 525 enter
527
+ 526 europe
528
+ 527 event
529
+ 528 exchange
530
+ 529 experience
531
+ 530 face
532
+ 531 fail
533
+ 532 feed
534
+ 533 few
535
+ 534 flag
536
+ 535 food
537
+ 536 for
538
+ 537 form
539
+ 538 freeze
540
+ 539 friday
541
+ 540 front
542
+ 541 giraffe
543
+ 542 god
544
+ 543 grammar
545
+ 544 grandfather
546
+ 545 great
547
+ 546 greece
548
+ 547 guess
549
+ 548 guitar
550
+ 549 hammer
551
+ 550 helicopter
552
+ 551 high
553
+ 552 history
554
+ 553 hungry
555
+ 554 hurt
556
+ 555 introduce
557
+ 556 invest
558
+ 557 lazy
559
+ 558 library
560
+ 559 lie
561
+ 560 lobster
562
+ 561 love
563
+ 562 lucky
564
+ 563 magazine
565
+ 564 measure
566
+ 565 microwave
567
+ 566 my
568
+ 567 neighbor
569
+ 568 number
570
+ 569 one
571
+ 570 outside
572
+ 571 peach
573
+ 572 pig
574
+ 573 polite
575
+ 574 postpone
576
+ 575 power
577
+ 576 present
578
+ 577 price
579
+ 578 prison
580
+ 579 push
581
+ 580 raccoon
582
+ 581 really
583
+ 582 reason
584
+ 583 religion
585
+ 584 respect
586
+ 585 rule
587
+ 586 salad
588
+ 587 schedule
589
+ 588 sell
590
+ 589 serious
591
+ 590 shower
592
+ 591 single
593
+ 592 smart
594
+ 593 smile
595
+ 594 soft
596
+ 595 sound
597
+ 596 soup
598
+ 597 spain
599
+ 598 spray
600
+ 599 squirrel
601
+ 600 stomach
602
+ 601 story
603
+ 602 strange
604
+ 603 street
605
+ 604 stress
606
+ 605 strict
607
+ 606 strong
608
+ 607 sugar
609
+ 608 summer
610
+ 609 suspect
611
+ 610 sweetheart
612
+ 611 tease
613
+ 612 that
614
+ 613 themselves
615
+ 614 therapy
616
+ 615 thirsty
617
+ 616 ticket
618
+ 617 top
619
+ 618 tuesday
620
+ 619 turkey
621
+ 620 university
622
+ 621 until
623
+ 622 watch
624
+ 623 weak
625
+ 624 wedding
626
+ 625 will
627
+ 626 wind
628
+ 627 world
629
+ 628 worry
630
+ 629 wow
631
+ 630 young
632
+ 631 add
633
+ 632 airplane
634
+ 633 already
635
+ 634 also
636
+ 635 analyze
637
+ 636 and
638
+ 637 angry
639
+ 638 appear
640
+ 639 arm
641
+ 640 army
642
+ 641 arrest
643
+ 642 asia
644
+ 643 ask
645
+ 644 attitude
646
+ 645 attract
647
+ 646 bald
648
+ 647 barely
649
+ 648 baseball
650
+ 649 beg
651
+ 650 benefit
652
+ 651 bite
653
+ 652 blame
654
+ 653 boat
655
+ 654 body
656
+ 655 borrow
657
+ 656 boss
658
+ 657 bother
659
+ 658 bottle
660
+ 659 bottom
661
+ 660 brag
662
+ 661 build
663
+ 662 bus
664
+ 663 butter
665
+ 664 calm
666
+ 665 cancel
667
+ 666 candle
668
+ 667 cards
669
+ 668 choice
670
+ 669 comb
671
+ 670 comfortable
672
+ 671 conflict
673
+ 672 cover
674
+ 673 deodorant
675
+ 674 dessert
676
+ 675 destroy
677
+ 676 diamond
678
+ 677 dollar
679
+ 678 drawer
680
+ 679 dream
681
+ 680 drunk
682
+ 681 duck
683
+ 682 during
684
+ 683 eagle
685
+ 684 early
686
+ 685 equal
687
+ 686 every
688
+ 687 excited
689
+ 688 exercise
690
+ 689 experiment
691
+ 690 expert
692
+ 691 fact
693
+ 692 fear
694
+ 693 feedback
695
+ 694 fold
696
+ 695 forest
697
+ 696 france
698
+ 697 frog
699
+ 698 funny
700
+ 699 garage
701
+ 700 goal
702
+ 701 golf
703
+ 702 gone
704
+ 703 gossip
705
+ 704 grandmother
706
+ 705 grapes
707
+ 706 group
708
+ 707 guilty
709
+ 708 hamburger
710
+ 709 hate
711
+ 710 head
712
+ 711 her
713
+ 712 horse
714
+ 713 ignore
715
+ 714 impossible
716
+ 715 in
717
+ 716 independent
718
+ 717 inside
719
+ 718 insurance
720
+ 719 island
721
+ 720 lecture
722
+ 721 lesson
723
+ 722 limit
724
+ 723 lion
725
+ 724 listen
726
+ 725 march
727
+ 726 math
728
+ 727 maybe
729
+ 728 mechanic
730
+ 729 mom
731
+ 730 mouse
732
+ 731 much
733
+ 732 muscle
734
+ 733 museum
735
+ 734 must
736
+ 735 mustache
737
+ 736 negative
738
+ 737 nervous
739
+ 738 neutral
740
+ 739 nose
741
+ 740 often
742
+ 741 operate
743
+ 742 opinion
744
+ 743 other
745
+ 744 pass
746
+ 745 pillow
747
+ 746 prepare
748
+ 747 pretty
749
+ 748 priest
750
+ 749 program
751
+ 750 promise
752
+ 751 put
753
+ 752 queen
754
+ 753 quit
755
+ 754 radio
756
+ 755 rat
757
+ 756 reject
758
+ 757 require
759
+ 758 rest
760
+ 759 retire
761
+ 760 rise
762
+ 761 river
763
+ 762 rough
764
+ 763 see
765
+ 764 seem
766
+ 765 send
767
+ 766 sew
768
+ 767 sheep
769
+ 768 shine
770
+ 769 shock
771
+ 770 should
772
+ 771 silent
773
+ 772 skinny
774
+ 773 skirt
775
+ 774 sky
776
+ 775 sour
777
+ 776 special
778
+ 777 spirit
779
+ 778 staff
780
+ 779 stand
781
+ 780 steal
782
+ 781 stop
783
+ 782 stupid
784
+ 783 summon
785
+ 784 sunrise
786
+ 785 swallow
787
+ 786 television
788
+ 787 to
789
+ 788 together
790
+ 789 translate
791
+ 790 triangle
792
+ 791 turtle
793
+ 792 underwear
794
+ 793 up
795
+ 794 upset
796
+ 795 violin
797
+ 796 volunteer
798
+ 797 warm
799
+ 798 warn
800
+ 799 wash
801
+ 800 wine
802
+ 801 witness
803
+ 802 wood
804
+ 803 zero
805
+ 804 across
806
+ 805 actor
807
+ 806 agree
808
+ 807 alarm
809
+ 808 allergy
810
+ 809 almost
811
+ 810 announce
812
+ 811 apartment
813
+ 812 attention
814
+ 813 audience
815
+ 814 august
816
+ 815 autumn
817
+ 816 away
818
+ 817 beautiful
819
+ 818 become
820
+ 819 below
821
+ 820 best
822
+ 821 bet
823
+ 822 bicycle
824
+ 823 biology
825
+ 824 boyfriend
826
+ 825 break
827
+ 826 bridge
828
+ 827 brush
829
+ 828 building
830
+ 829 busy
831
+ 830 camera
832
+ 831 camp
833
+ 832 caption
834
+ 833 care
835
+ 834 carry
836
+ 835 celebrate
837
+ 836 certificate
838
+ 837 chain
839
+ 838 chance
840
+ 839 character
841
+ 840 chase
842
+ 841 classroom
843
+ 842 clear
844
+ 843 climb
845
+ 844 command
846
+ 845 community
847
+ 846 complain
848
+ 847 compromise
849
+ 848 contribute
850
+ 849 cop
851
+ 850 cost
852
+ 851 couch
853
+ 852 cracker
854
+ 853 curious
855
+ 854 curriculum
856
+ 855 dad
857
+ 856 deny
858
+ 857 describe
859
+ 858 diaper
860
+ 859 diarrhea
861
+ 860 disagree
862
+ 861 dissolve
863
+ 862 divide
864
+ 863 dolphin
865
+ 864 double
866
+ 865 doubt
867
+ 866 dumb
868
+ 867 earth
869
+ 868 earthquake
870
+ 869 eight
871
+ 870 embarrass
872
+ 871 emotion
873
+ 872 encourage
874
+ 873 energy
875
+ 874 enough
876
+ 875 evaluate
877
+ 876 evidence
878
+ 877 exact
879
+ 878 exaggerate
880
+ 879 expect
881
+ 880 fancy
882
+ 881 favorite
883
+ 882 finally
884
+ 883 fox
885
+ 884 french
886
+ 885 fruit
887
+ 886 function
888
+ 887 gather
889
+ 888 germany
890
+ 889 gloves
891
+ 890 goat
892
+ 891 goodbye
893
+ 892 grow
894
+ 893 gum
895
+ 894 half
896
+ 895 hawaii
897
+ 896 herself
898
+ 897 highway
899
+ 898 homework
900
+ 899 honor
901
+ 900 ice cream
902
+ 901 if
903
+ 902 increase
904
+ 903 india
905
+ 904 information
906
+ 905 interpret
907
+ 906 interview
908
+ 907 invite
909
+ 908 israel
910
+ 909 jesus
911
+ 910 keep
912
+ 911 keyboard
913
+ 912 land
914
+ 913 lawyer
915
+ 914 lead
916
+ 915 lesbian
917
+ 916 line
918
+ 917 lock
919
+ 918 loud
920
+ 919 lousy
921
+ 920 lunch
922
+ 921 machine
923
+ 922 mad
924
+ 923 memorize
925
+ 924 minus
926
+ 925 monday
927
+ 926 mountain
928
+ 927 mouth
929
+ 928 mushroom
930
+ 929 napkin
931
+ 930 neck
932
+ 931 next
933
+ 932 ocean
934
+ 933 open
935
+ 934 overwhelm
936
+ 935 owe
937
+ 936 page
938
+ 937 pain
939
+ 938 parade
940
+ 939 parents
941
+ 940 perfect
942
+ 941 period
943
+ 942 philosophy
944
+ 943 photographer
945
+ 944 place
946
+ 945 policy
947
+ 946 popular
948
+ 947 pray
949
+ 948 predict
950
+ 949 presentation
951
+ 950 print
952
+ 951 printer
953
+ 952 private
954
+ 953 procrastinate
955
+ 954 project
956
+ 955 protect
957
+ 956 prove
958
+ 957 provide
959
+ 958 psychology
960
+ 959 punish
961
+ 960 puzzled
962
+ 961 question
963
+ 962 quick
964
+ 963 rainbow
965
+ 964 rake
966
+ 965 rehearse
967
+ 966 remove
968
+ 967 revenge
969
+ 968 review
970
+ 969 rich
971
+ 970 roof
972
+ 971 rooster
973
+ 972 rude
974
+ 973 say
975
+ 974 scientist
976
+ 975 scotland
977
+ 976 screwdriver
978
+ 977 second
979
+ 978 senate
980
+ 979 separate
981
+ 980 service
982
+ 981 shrimp
983
+ 982 shy
984
+ 983 side
985
+ 984 situation
986
+ 985 slave
987
+ 986 soccer
988
+ 987 socks
989
+ 988 solid
990
+ 989 sometimes
991
+ 990 spider
992
+ 991 spin
993
+ 992 square
994
+ 993 stairs
995
+ 994 stare
996
+ 995 start
997
+ 996 store
998
+ 997 straw
999
+ 998 structure
1000
+ 999 suggest
1001
+ 1000 sun
1002
+ 1001 support
1003
+ 1002 suppose
1004
+ 1003 sure
1005
+ 1004 surprise
1006
+ 1005 sweater
1007
+ 1006 swim
1008
+ 1007 symbol
1009
+ 1008 taste
1010
+ 1009 team
1011
+ 1010 telephone
1012
+ 1011 tennis
1013
+ 1012 their
1014
+ 1013 then
1015
+ 1014 thermometer
1016
+ 1015 thing
1017
+ 1016 third
1018
+ 1017 thousand
1019
+ 1018 three
1020
+ 1019 tie
1021
+ 1020 topic
1022
+ 1021 touch
1023
+ 1022 tournament
1024
+ 1023 try
1025
+ 1024 two
1026
+ 1025 type
1027
+ 1026 umbrella
1028
+ 1027 vegetable
1029
+ 1028 vocabulary
1030
+ 1029 waste
1031
+ 1030 we
1032
+ 1031 wear
1033
+ 1032 weekly
1034
+ 1033 welcome
1035
+ 1034 winter
1036
+ 1035 without
1037
+ 1036 wolf
1038
+ 1037 worm
1039
+ 1038 wristwatch
1040
+ 1039 yourself
1041
+ 1040 above
1042
+ 1041 accomplish
1043
+ 1042 adopt
1044
+ 1043 advantage
1045
+ 1044 alphabet
1046
+ 1045 anniversary
1047
+ 1046 another
1048
+ 1047 appropriate
1049
+ 1048 arrogant
1050
+ 1049 artist
1051
+ 1050 background
1052
+ 1051 bee
1053
+ 1052 behavior
1054
+ 1053 bell
1055
+ 1054 birth
1056
+ 1055 bless
1057
+ 1056 blood
1058
+ 1057 boots
1059
+ 1058 brave
1060
+ 1059 breathe
1061
+ 1060 bright
1062
+ 1061 bull
1063
+ 1062 butterfly
1064
+ 1063 card
1065
+ 1064 category
1066
+ 1065 catholic
1067
+ 1066 cent
1068
+ 1067 chemistry
1069
+ 1068 cherry
1070
+ 1069 china
1071
+ 1070 chop
1072
+ 1071 christian
1073
+ 1072 cigarette
1074
+ 1073 clever
1075
+ 1074 clown
1076
+ 1075 coach
1077
+ 1076 coat
1078
+ 1077 cochlear implant
1079
+ 1078 coconut
1080
+ 1079 common
1081
+ 1080 commute
1082
+ 1081 control
1083
+ 1082 count
1084
+ 1083 culture
1085
+ 1084 daily
1086
+ 1085 defend
1087
+ 1086 degree
1088
+ 1087 demonstrate
1089
+ 1088 department
1090
+ 1089 die
1091
+ 1090 dig
1092
+ 1091 dinner
1093
+ 1092 dinosaur
1094
+ 1093 diploma
1095
+ 1094 director
1096
+ 1095 disconnect
1097
+ 1096 discover
1098
+ 1097 don't want
1099
+ 1098 drum
1100
+ 1099 each
1101
+ 1100 educate
1102
+ 1101 electrician
1103
+ 1102 empty
1104
+ 1103 england
1105
+ 1104 establish
1106
+ 1105 excuse
1107
+ 1106 eyeglasses
1108
+ 1107 faculty
1109
+ 1108 fall in love
1110
+ 1109 famous
1111
+ 1110 farm
1112
+ 1111 fence
1113
+ 1112 fix
1114
+ 1113 flexible
1115
+ 1114 flirt
1116
+ 1115 flood
1117
+ 1116 fork
1118
+ 1117 four
1119
+ 1118 fun
1120
+ 1119 funeral
1121
+ 1120 furniture
1122
+ 1121 gallaudet
1123
+ 1122 general
1124
+ 1123 ghost
1125
+ 1124 give up
1126
+ 1125 glass
1127
+ 1126 gorilla
1128
+ 1127 gray
1129
+ 1128 guide
1130
+ 1129 habit
1131
+ 1130 health
1132
+ 1131 hearing aid
1133
+ 1132 heaven
1134
+ 1133 heavy
1135
+ 1134 helmet
1136
+ 1135 hide
1137
+ 1136 high school
1138
+ 1137 hockey
1139
+ 1138 hold
1140
+ 1139 honest
1141
+ 1140 hug
1142
+ 1141 hunt
1143
+ 1142 image
1144
+ 1143 influence
1145
+ 1144 interesting
1146
+ 1145 interpreter
1147
+ 1146 iran
1148
+ 1147 italy
1149
+ 1148 jewish
1150
+ 1149 judge
1151
+ 1150 key
1152
+ 1151 label
1153
+ 1152 leaf
1154
+ 1153 left
1155
+ 1154 lend
1156
+ 1155 less
1157
+ 1156 linguistics
1158
+ 1157 lonely
1159
+ 1158 long
1160
+ 1159 magic
1161
+ 1160 mainstream
1162
+ 1161 major
1163
+ 1162 manager
1164
+ 1163 maximum
1165
+ 1164 me
1166
+ 1165 mexico
1167
+ 1166 middle
1168
+ 1167 mind
1169
+ 1168 misunderstand
1170
+ 1169 monster
1171
+ 1170 multiply
1172
+ 1171 myself
1173
+ 1172 nothing
1174
+ 1173 october
1175
+ 1174 odd
1176
+ 1175 offer
1177
+ 1176 on
1178
+ 1177 only
1179
+ 1178 opposite
1180
+ 1179 out
1181
+ 1180 overlook
1182
+ 1181 path
1183
+ 1182 percent
1184
+ 1183 physics
1185
+ 1184 piano
1186
+ 1185 pick
1187
+ 1186 pie
1188
+ 1187 plate
1189
+ 1188 pocket
1190
+ 1189 popcorn
1191
+ 1190 positive
1192
+ 1191 pregnant
1193
+ 1192 prevent
1194
+ 1193 proof
1195
+ 1194 pumpkin
1196
+ 1195 purpose
1197
+ 1196 race
1198
+ 1197 realize
1199
+ 1198 recognize
1200
+ 1199 refuse
1201
+ 1200 relationship
1202
+ 1201 repeat
1203
+ 1202 replace
1204
+ 1203 report
1205
+ 1204 represent
1206
+ 1205 request
1207
+ 1206 responsibility
1208
+ 1207 result
1209
+ 1208 reveal
1210
+ 1209 ring
1211
+ 1210 rob
1212
+ 1211 rock
1213
+ 1212 role
1214
+ 1213 rope
1215
+ 1214 rush
1216
+ 1215 salute
1217
+ 1216 satisfy
1218
+ 1217 search
1219
+ 1218 selfish
1220
+ 1219 sensitive
1221
+ 1220 serve
1222
+ 1221 seven
1223
+ 1222 shampoo
1224
+ 1223 she
1225
+ 1224 shelf
1226
+ 1225 shopping
1227
+ 1226 sign language
1228
+ 1227 similar
1229
+ 1228 sing
1230
+ 1229 six
1231
+ 1230 skeleton
1232
+ 1231 sketch
1233
+ 1232 skill
1234
+ 1233 sleepy
1235
+ 1234 slip
1236
+ 1235 smell
1237
+ 1236 sneeze
1238
+ 1237 snob
1239
+ 1238 specific
1240
+ 1239 stamp
1241
+ 1240 steel
1242
+ 1241 strawberry
1243
+ 1242 subtract
1244
+ 1243 subway
1245
+ 1244 suffer
1246
+ 1245 surface
1247
+ 1246 sweep
1248
+ 1247 swing
1249
+ 1248 switzerland
1250
+ 1249 tale
1251
+ 1250 tan
1252
+ 1251 teeth
1253
+ 1252 terrible
1254
+ 1253 thankful
1255
+ 1254 they
1256
+ 1255 through
1257
+ 1256 tiptoe
1258
+ 1257 title
1259
+ 1258 tooth
1260
+ 1259 toothbrush
1261
+ 1260 total
1262
+ 1261 tough
1263
+ 1262 tradition
1264
+ 1263 trophy
1265
+ 1264 trouble
1266
+ 1265 true
1267
+ 1266 trust
1268
+ 1267 under
1269
+ 1268 verb
1270
+ 1269 wall
1271
+ 1270 watermelon
1272
+ 1271 way
1273
+ 1272 weird
1274
+ 1273 while
1275
+ 1274 wide
1276
+ 1275 willing
1277
+ 1276 wish
1278
+ 1277 wonderful
1279
+ 1278 workshop
1280
+ 1279 worse
1281
+ 1280 wrench
1282
+ 1281 a
1283
+ 1282 a lot
1284
+ 1283 abdomen
1285
+ 1284 able
1286
+ 1285 accountant
1287
+ 1286 action
1288
+ 1287 active
1289
+ 1288 activity
1290
+ 1289 address
1291
+ 1290 affect
1292
+ 1291 afraid
1293
+ 1292 against
1294
+ 1293 agenda
1295
+ 1294 ahead
1296
+ 1295 aim
1297
+ 1296 alcohol
1298
+ 1297 algebra
1299
+ 1298 all day
1300
+ 1299 amazing
1301
+ 1300 angle
1302
+ 1301 apart
1303
+ 1302 apostrophe
1304
+ 1303 appetite
1305
+ 1304 appreciate
1306
+ 1305 april
1307
+ 1306 archery
1308
+ 1307 around
1309
+ 1308 article
1310
+ 1309 assistant
1311
+ 1310 attend
1312
+ 1311 auction
1313
+ 1312 authority
1314
+ 1313 average
1315
+ 1314 awake
1316
+ 1315 b
1317
+ 1316 babysitter
1318
+ 1317 battle
1319
+ 1318 beginning
1320
+ 1319 belief
1321
+ 1320 bible
1322
+ 1321 bike
1323
+ 1322 blend
1324
+ 1323 bone
1325
+ 1324 bra
1326
+ 1325 brief
1327
+ 1326 broke
1328
+ 1327 bug
1329
+ 1328 bully
1330
+ 1329 button
1331
+ 1330 cabbage
1332
+ 1331 cabinet
1333
+ 1332 calculate
1334
+ 1333 calculator
1335
+ 1334 calculus
1336
+ 1335 camping
1337
+ 1336 canoe
1338
+ 1337 careless
1339
+ 1338 ceiling
1340
+ 1339 cemetery
1341
+ 1340 chapter
1342
+ 1341 choir
1343
+ 1342 circle
1344
+ 1343 come
1345
+ 1344 come here
1346
+ 1345 company
1347
+ 1346 complete
1348
+ 1347 concept
1349
+ 1348 concern
1350
+ 1349 confront
1351
+ 1350 confused
1352
+ 1351 congress
1353
+ 1352 connect
1354
+ 1353 conquer
1355
+ 1354 constitution
1356
+ 1355 contract
1357
+ 1356 court
1358
+ 1357 crab
1359
+ 1358 cross
1360
+ 1359 cruel
1361
+ 1360 crush
1362
+ 1361 date
1363
+ 1362 december
1364
+ 1363 decrease
1365
+ 1364 deduct
1366
+ 1365 defeat
1367
+ 1366 deposit
1368
+ 1367 depressed
1369
+ 1368 detach
1370
+ 1369 determine
1371
+ 1370 diabetes
1372
+ 1371 dime
1373
+ 1372 dirt
1374
+ 1373 disgust
1375
+ 1374 dismiss
1376
+ 1375 dizzy
1377
+ 1376 document
1378
+ 1377 dorm
1379
+ 1378 dormitory
1380
+ 1379 downstairs
1381
+ 1380 economy
1382
+ 1381 education
1383
+ 1382 effort
1384
+ 1383 eighteen
1385
+ 1384 engage
1386
+ 1385 engagement
1387
+ 1386 engineer
1388
+ 1387 envelope
1389
+ 1388 erase
1390
+ 1389 eternity
1391
+ 1390 evening
1392
+ 1391 everyday
1393
+ 1392 expand
1394
+ 1393 eye
1395
+ 1394 eyes
1396
+ 1395 fake
1397
+ 1396 farmer
1398
+ 1397 february
1399
+ 1398 federal
1400
+ 1399 final
1401
+ 1400 florida
1402
+ 1401 flute
1403
+ 1402 forbid
1404
+ 1403 forever
1405
+ 1404 fourth
1406
+ 1405 free
1407
+ 1406 french fries
1408
+ 1407 gamble
1409
+ 1408 gas
1410
+ 1409 gasoline
1411
+ 1410 generation
1412
+ 1411 geography
1413
+ 1412 german
1414
+ 1413 girlfriend
1415
+ 1414 gold
1416
+ 1415 grandma
1417
+ 1416 grass
1418
+ 1417 grow up
1419
+ 1418 gun
1420
+ 1419 haircut
1421
+ 1420 hard of hearing
1422
+ 1421 hippopotamus
1423
+ 1422 his
1424
+ 1423 honey
1425
+ 1424 hurricane
1426
+ 1425 identify
1427
+ 1426 include
1428
+ 1427 infection
1429
+ 1428 innocent
1430
+ 1429 insect
1431
+ 1430 instead
1432
+ 1431 institute
1433
+ 1432 international
1434
+ 1433 interrupt
1435
+ 1434 involve
1436
+ 1435 jail
1437
+ 1436 january
1438
+ 1437 joke
1439
+ 1438 june
1440
+ 1439 kangaroo
1441
+ 1440 karate
1442
+ 1441 kick
1443
+ 1442 kneel
1444
+ 1443 knock
1445
+ 1444 laptop
1446
+ 1445 lift
1447
+ 1446 lightning
1448
+ 1447 lip
1449
+ 1448 lipstick
1450
+ 1449 local
1451
+ 1450 manage
1452
+ 1451 mature
1453
+ 1452 member
1454
+ 1453 message
1455
+ 1454 metal
1456
+ 1455 microphone
1457
+ 1456 mix
1458
+ 1457 monthly
1459
+ 1458 motor
1460
+ 1459 nation
1461
+ 1460 network
1462
+ 1461 nickel
1463
+ 1462 nineteen
1464
+ 1463 normal
1465
+ 1464 november
1466
+ 1465 nut
1467
+ 1466 octopus
1468
+ 1467 once
1469
+ 1468 organize
1470
+ 1469 over
1471
+ 1470 owl
1472
+ 1471 p
1473
+ 1472 parachute
1474
+ 1473 paragraph
1475
+ 1474 parallel
1476
+ 1475 pay attention
1477
+ 1476 peace
1478
+ 1477 peanut butter
1479
+ 1478 perfume
1480
+ 1479 personality
1481
+ 1480 pet
1482
+ 1481 philadelphia
1483
+ 1482 physician
1484
+ 1483 piece
1485
+ 1484 pity
1486
+ 1485 plant
1487
+ 1486 plenty
1488
+ 1487 pneumonia
1489
+ 1488 politics
1490
+ 1489 position
1491
+ 1490 pound
1492
+ 1491 pour
1493
+ 1492 praise
1494
+ 1493 preach
1495
+ 1494 preacher
1496
+ 1495 prefer
1497
+ 1496 pressure
1498
+ 1497 principal
1499
+ 1498 priority
1500
+ 1499 professional
1501
+ 1500 professor
1502
+ 1501 promote
1503
+ 1502 prostitute
1504
+ 1503 proud
1505
+ 1504 quote
1506
+ 1505 receive
1507
+ 1506 recent
1508
+ 1507 reduce
1509
+ 1508 refer
1510
+ 1509 referee
1511
+ 1510 relate
1512
+ 1511 relax
1513
+ 1512 remote control
1514
+ 1513 rent
1515
+ 1514 reputation
1516
+ 1515 resign
1517
+ 1516 responsible
1518
+ 1517 ridiculous
1519
+ 1518 road
1520
+ 1519 robot
1521
+ 1520 rubber
1522
+ 1521 safe
1523
+ 1522 sauce
1524
+ 1523 sausage
1525
+ 1524 scold
1526
+ 1525 senior
1527
+ 1526 september
1528
+ 1527 several
1529
+ 1528 shame
1530
+ 1529 shave
1531
+ 1530 shoot
1532
+ 1531 shoulder
1533
+ 1532 silver
1534
+ 1533 simple
1535
+ 1534 siren
1536
+ 1535 size
1537
+ 1536 skate
1538
+ 1537 skin
1539
+ 1538 skunk
1540
+ 1539 slice
1541
+ 1540 smooth
1542
+ 1541 snack
1543
+ 1542 snowman
1544
+ 1543 society
1545
+ 1544 someone
1546
+ 1545 song
1547
+ 1546 sore throat
1548
+ 1547 south america
1549
+ 1548 spanish
1550
+ 1549 speak
1551
+ 1550 speed
1552
+ 1551 spell
1553
+ 1552 spoon
1554
+ 1553 spring
1555
+ 1554 standard
1556
+ 1555 statistics
1557
+ 1556 sticky
1558
+ 1557 still
1559
+ 1558 stretch
1560
+ 1559 surgeon
1561
+ 1560 sweden
1562
+ 1561 swimsuit
1563
+ 1562 sympathy
1564
+ 1563 take turns
1565
+ 1564 take up
1566
+ 1565 technology
1567
+ 1566 temple
1568
+ 1567 tender
1569
+ 1568 thailand
1570
+ 1569 theater
1571
+ 1570 them
1572
+ 1571 theme
1573
+ 1572 thick
1574
+ 1573 this
1575
+ 1574 throat
1576
+ 1575 tissue
1577
+ 1576 tongue
1578
+ 1577 tonight
1579
+ 1578 tornado
1580
+ 1579 tower
1581
+ 1580 tranquil
1582
+ 1581 transform
1583
+ 1582 tutor
1584
+ 1583 twin
1585
+ 1584 united states
1586
+ 1585 value
1587
+ 1586 visitor
1588
+ 1587 waiter
1589
+ 1588 wake up
1590
+ 1589 wallet
1591
+ 1590 wander
1592
+ 1591 wash face
1593
+ 1592 weight
1594
+ 1593 whale
1595
+ 1594 whatever
1596
+ 1595 within
1597
+ 1596 wonder
1598
+ 1597 worker
1599
+ 1598 worthless
1600
+ 1599 wrap
1601
+ 1600 accent
1602
+ 1601 act
1603
+ 1602 adapt
1604
+ 1603 adjective
1605
+ 1604 adjust
1606
+ 1605 admire
1607
+ 1606 admit
1608
+ 1607 advanced
1609
+ 1608 adverb
1610
+ 1609 agreement
1611
+ 1610 aid
1612
+ 1611 alligator
1613
+ 1612 amputate
1614
+ 1613 anatomy
1615
+ 1614 annoy
1616
+ 1615 anyway
1617
+ 1616 approach
1618
+ 1617 arizona
1619
+ 1618 assist
1620
+ 1619 assume
1621
+ 1620 attorney
1622
+ 1621 audiologist
1623
+ 1622 audiology
1624
+ 1623 austria
1625
+ 1624 author
1626
+ 1625 available
1627
+ 1626 award
1628
+ 1627 aware
1629
+ 1628 bank
1630
+ 1629 baptize
1631
+ 1630 basic
1632
+ 1631 battery
1633
+ 1632 berry
1634
+ 1633 beside
1635
+ 1634 between
1636
+ 1635 binoculars
1637
+ 1636 blow
1638
+ 1637 boast
1639
+ 1638 boil
1640
+ 1639 bookshelf
1641
+ 1640 bookstore
1642
+ 1641 boxing
1643
+ 1642 braid
1644
+ 1643 brain
1645
+ 1644 breakdown
1646
+ 1645 breakfast
1647
+ 1646 breeze
1648
+ 1647 bribe
1649
+ 1648 brochure
1650
+ 1649 buffalo
1651
+ 1650 burp
1652
+ 1651 bush
1653
+ 1652 bye
1654
+ 1653 camel
1655
+ 1654 candidate
1656
+ 1655 cannot
1657
+ 1656 captain
1658
+ 1657 carnival
1659
+ 1658 caterpillar
1660
+ 1659 cheerleader
1661
+ 1660 chemical
1662
+ 1661 chicago
1663
+ 1662 choke
1664
+ 1663 christ
1665
+ 1664 click
1666
+ 1665 closet
1667
+ 1666 clueless
1668
+ 1667 clumsy
1669
+ 1668 collect
1670
+ 1669 comma
1671
+ 1670 comment
1672
+ 1671 commit
1673
+ 1672 committee
1674
+ 1673 common sense
1675
+ 1674 compete
1676
+ 1675 concentrate
1677
+ 1676 congratulations
1678
+ 1677 consider
1679
+ 1678 construct
1680
+ 1679 consume
1681
+ 1680 contest
1682
+ 1681 conversation
1683
+ 1682 convert
1684
+ 1683 cooperate
1685
+ 1684 counsel
1686
+ 1685 counselor
1687
+ 1686 crave
1688
+ 1687 create
1689
+ 1688 crocodile
1690
+ 1689 crown
1691
+ 1690 cuba
1692
+ 1691 curtain
1693
+ 1692 customer
1694
+ 1693 d
1695
+ 1694 damage
1696
+ 1695 dancer
1697
+ 1696 danger
1698
+ 1697 dangerous
1699
+ 1698 dawn
1700
+ 1699 death
1701
+ 1700 debate
1702
+ 1701 debt
1703
+ 1702 deliver
1704
+ 1703 democrat
1705
+ 1704 descend
1706
+ 1705 design
1707
+ 1706 detective
1708
+ 1707 devil
1709
+ 1708 dice
1710
+ 1709 difficult
1711
+ 1710 dining room
1712
+ 1711 discipline
1713
+ 1712 discount
1714
+ 1713 disgusted
1715
+ 1714 disturb
1716
+ 1715 division
1717
+ 1716 done
1718
+ 1717 drag
1719
+ 1718 dragon
1720
+ 1719 drama
1721
+ 1720 drug
1722
+ 1721 due
1723
+ 1722 dull
1724
+ 1723 dusk
1725
+ 1724 dvd
1726
+ 1725 dye
1727
+ 1726 e
1728
+ 1727 either
1729
+ 1728 electricity
1730
+ 1729 elementary
1731
+ 1730 else
1732
+ 1731 emergency
1733
+ 1732 engine
1734
+ 1733 enormous
1735
+ 1734 eraser
1736
+ 1735 every monday
1737
+ 1736 every tuesday
1738
+ 1737 everything
1739
+ 1738 except
1740
+ 1739 exhibit
1741
+ 1740 explode
1742
+ 1741 express
1743
+ 1742 f
1744
+ 1743 fairy
1745
+ 1744 familiar
1746
+ 1745 festival
1747
+ 1746 finance
1748
+ 1747 fingerspell
1749
+ 1748 fire
1750
+ 1749 firefighter
1751
+ 1750 five
1752
+ 1751 flatter
1753
+ 1752 fool
1754
+ 1753 forgive
1755
+ 1754 former
1756
+ 1755 freeway
1757
+ 1756 from now on
1758
+ 1757 g
1759
+ 1758 gang
1760
+ 1759 gay
1761
+ 1760 geometry
1762
+ 1761 get up
1763
+ 1762 gift
1764
+ 1763 grab
1765
+ 1764 graduation
1766
+ 1765 grateful
1767
+ 1766 grey
1768
+ 1767 gymnastics
1769
+ 1768 h
1770
+ 1769 hang up
1771
+ 1770 hanukkah
1772
+ 1771 heap
1773
+ 1772 heart attack
1774
+ 1773 hill
1775
+ 1774 holy
1776
+ 1775 hop
1777
+ 1776 host
1778
+ 1777 hot dog
1779
+ 1778 human
1780
+ 1779 i
1781
+ 1780 ill
1782
+ 1781 illegal
1783
+ 1782 impact
1784
+ 1783 individual
1785
+ 1784 inspect
1786
+ 1785 inspire
1787
+ 1786 intersection
1788
+ 1787 ireland
1789
+ 1788 iron
1790
+ 1789 j
1791
+ 1790 jewelry
1792
+ 1791 journey
1793
+ 1792 joy
1794
+ 1793 july
1795
+ 1794 k
1796
+ 1795 kid
1797
+ 1796 kindergarten
1798
+ 1797 lady
1799
+ 1798 lamp
1800
+ 1799 last week
1801
+ 1800 laundry
1802
+ 1801 leader
1803
+ 1802 league
1804
+ 1803 leak
1805
+ 1804 legal
1806
+ 1805 let
1807
+ 1806 liability
1808
+ 1807 librarian
1809
+ 1808 license
1810
+ 1809 little bit
1811
+ 1810 loan
1812
+ 1811 look at
1813
+ 1812 look for
1814
+ 1813 lord
1815
+ 1814 m
1816
+ 1815 meaning
1817
+ 1816 melody
1818
+ 1817 melt
1819
+ 1818 mention
1820
+ 1819 microscope
1821
+ 1820 midnight
1822
+ 1821 military
1823
+ 1822 mine
1824
+ 1823 mistake
1825
+ 1824 mock
1826
+ 1825 moose
1827
+ 1826 mosquito
1828
+ 1827 motivate
1829
+ 1828 murder
1830
+ 1829 n
1831
+ 1830 narrow
1832
+ 1831 necessary
1833
+ 1832 negotiate
1834
+ 1833 new york
1835
+ 1834 nine
1836
+ 1835 northwest
1837
+ 1836 not yet
1838
+ 1837 notice
1839
+ 1838 numerous
1840
+ 1839 nun
1841
+ 1840 o
1842
+ 1841 objective
1843
+ 1842 obsess
1844
+ 1843 obtain
1845
+ 1844 occur
1846
+ 1845 odor
1847
+ 1846 olympics
1848
+ 1847 or
1849
+ 1848 oral
1850
+ 1849 our
1851
+ 1850 overcome
1852
+ 1851 pack
1853
+ 1852 painter
1854
+ 1853 part
1855
+ 1854 pause
1856
+ 1855 peaceful
1857
+ 1856 pear
1858
+ 1857 peel
1859
+ 1858 penalty
1860
+ 1859 pennsylvania
1861
+ 1860 penny
1862
+ 1861 permit
1863
+ 1862 phrase
1864
+ 1863 pickle
1865
+ 1864 pilot
1866
+ 1865 player
1867
+ 1866 polar bear
1868
+ 1867 policeman
1869
+ 1868 poop
1870
+ 1869 post
1871
+ 1870 potential
1872
+ 1871 poverty
1873
+ 1872 precious
1874
+ 1873 precipitation
1875
+ 1874 precise
1876
+ 1875 preschool
1877
+ 1876 pride
1878
+ 1877 prince
1879
+ 1878 princess
1880
+ 1879 principle
1881
+ 1880 process
1882
+ 1881 profit
1883
+ 1882 progress
1884
+ 1883 propaganda
1885
+ 1884 proper
1886
+ 1885 psychologist
1887
+ 1886 public
1888
+ 1887 publish
1889
+ 1888 purchase
1890
+ 1889 pure
1891
+ 1890 pursue
1892
+ 1891 put off
1893
+ 1892 q
1894
+ 1893 quality
1895
+ 1894 quarrel
1896
+ 1895 quarter
1897
+ 1896 r
1898
+ 1897 rage
1899
+ 1898 rather
1900
+ 1899 real
1901
+ 1900 recliner
1902
+ 1901 recommend
1903
+ 1902 recover
1904
+ 1903 regular
1905
+ 1904 release
1906
+ 1905 relief
1907
+ 1906 rely
1908
+ 1907 reply
1909
+ 1908 rescue
1910
+ 1909 resist
1911
+ 1910 restroom
1912
+ 1911 retreat
1913
+ 1912 roar
1914
+ 1913 robber
1915
+ 1914 roommate
1916
+ 1915 rose
1917
+ 1916 ruin
1918
+ 1917 s
1919
+ 1918 salary
1920
+ 1919 saw
1921
+ 1920 scan
1922
+ 1921 scream
1923
+ 1922 sculpture
1924
+ 1923 sea
1925
+ 1924 seldom
1926
+ 1925 sequence
1927
+ 1926 settle
1928
+ 1927 shout
1929
+ 1928 shovel
1930
+ 1929 sin
1931
+ 1930 singer
1932
+ 1931 sixteen
1933
+ 1932 ski
1934
+ 1933 skip
1935
+ 1934 smoking
1936
+ 1935 sofa
1937
+ 1936 soldier
1938
+ 1937 solve
1939
+ 1938 someday
1940
+ 1939 something
1941
+ 1940 somewhere
1942
+ 1941 soul
1943
+ 1942 spill
1944
+ 1943 spit
1945
+ 1944 spread
1946
+ 1945 sprint
1947
+ 1946 squeeze
1948
+ 1947 stadium
1949
+ 1948 stepfather
1950
+ 1949 sting
1951
+ 1950 stir
1952
+ 1951 stitch
1953
+ 1952 stuck
1954
+ 1953 sue
1955
+ 1954 sunshine
1956
+ 1955 superman
1957
+ 1956 surrender
1958
+ 1957 suspend
1959
+ 1958 swimming
1960
+ 1959 t
1961
+ 1960 talent
1962
+ 1961 telescope
1963
+ 1962 tempt
1964
+ 1963 ten
1965
+ 1964 tend
1966
+ 1965 testify
1967
+ 1966 texas
1968
+ 1967 text
1969
+ 1968 than
1970
+ 1969 therefore
1971
+ 1970 thrill
1972
+ 1971 tobacco
1973
+ 1972 toilet paper
1974
+ 1973 tolerate
1975
+ 1974 torture
1976
+ 1975 towel
1977
+ 1976 trip
1978
+ 1977 truth
1979
+ 1978 turn
1980
+ 1979 tv
1981
+ 1980 u
1982
+ 1981 unique
1983
+ 1982 upstairs
1984
+ 1983 v
1985
+ 1984 vacant
1986
+ 1985 vague
1987
+ 1986 valley
1988
+ 1987 vampire
1989
+ 1988 very
1990
+ 1989 vice president
1991
+ 1990 viewpoint
1992
+ 1991 visualize
1993
+ 1992 vlog
1994
+ 1993 volleyball
1995
+ 1994 w
1996
+ 1995 washington
1997
+ 1996 waterfall
1998
+ 1997 weigh
1999
+ 1998 wheelchair
2000
+ 1999 whistle
assets/preprocess_300/nslt_100.json ADDED
The diff for this file is too large to render. See raw diff
 
assets/preprocess_300/nslt_300.json ADDED
The diff for this file is too large to render. See raw diff
 
assets/text_to_sign/filtered_one_video_per_gloss.json ADDED
The diff for this file is too large to render. See raw diff
 
download_assets.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gdown
3
+ import zipfile
4
+
5
+ DATASET_PATH = "assets/text_to_sign/wlasl2000_filtered_keypoints"
6
+
7
+ # Google Drive file ID
8
+ FILE_ID = "1l8pguR-nfbo5kLPua9wirHVPqiG1BE5h"
9
+
10
+ # Direct download URL
11
+ URL = f"https://drive.google.com/uc?id={FILE_ID}"
12
+
13
+ ZIP_PATH = "wlasl2000_filtered_keypoints.zip"
14
+
15
+ # Download only if dataset doesn't exist
16
+ if not os.path.exists(DATASET_PATH):
17
+ print("Downloading dataset...")
18
+
19
+ gdown.download(URL, ZIP_PATH, quiet=False)
20
+
21
+ print("Extracting dataset...")
22
+
23
+ with zipfile.ZipFile(ZIP_PATH, "r") as zip_ref:
24
+ zip_ref.extractall("assets/text_to_sign")
25
+
26
+ os.remove(ZIP_PATH)
27
+
28
+ print("Dataset ready ✅")
29
+
30
+ else:
31
+ print("Dataset already exists ✅")
32
+
nixpacks.toml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ [phases.setup]
2
+ nixPkgs = ["mesa", "glib"]
pipelines/sign_to_text/run.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import torch
3
+ import torch.nn.functional as F
4
+ import numpy as np
5
+ import os
6
+
7
+ from app.core.config import I3D_WEIGHTS, CLASS_LIST
8
+ from app.models.i3d_model import I3DModel
9
+ from app.utils.mediapipe_utils import hand_detector_instance
10
+
11
+ # ---------------- CONFIG ----------------
12
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
13
+
14
+ clip_length = 64
15
+ MIN_PAUSE_FRAMES = 5
16
+ MIN_SIGN_FRAMES = 10
17
+
18
+ # ---------------- MODEL ----------------
19
+ i3d_model = None
20
+
21
+
22
+ def get_i3d_model():
23
+ global i3d_model
24
+
25
+ if i3d_model is None:
26
+ print("Loading I3D model...")
27
+ i3d_model = I3DModel(I3D_WEIGHTS, device)
28
+
29
+ return i3d_model
30
+
31
+ with open(CLASS_LIST, "r") as f:
32
+ gloss_list = [line.strip().upper() for line in f.readlines()]
33
+
34
+
35
+ # ---------------- VIDEO LOADING ----------------
36
+ def load_video(video_path):
37
+ cap = cv2.VideoCapture(video_path)
38
+
39
+ if not cap.isOpened():
40
+ raise ValueError(f"Cannot open video: {video_path}")
41
+
42
+ frames = []
43
+ hand_flags = []
44
+
45
+ while True:
46
+ ret, frame = cap.read()
47
+ if not ret:
48
+ break
49
+
50
+ #print("FRAME SHAPE:", frame.shape)
51
+
52
+ resized = cv2.resize(frame, (224, 224))
53
+ norm = (resized.astype(np.float32) / 255.0) * 2 - 1
54
+
55
+ frames.append(norm)
56
+ hand_flags.append(hand_detector_instance.detect(frame))
57
+
58
+ cap.release()
59
+
60
+ frames = np.array(frames)
61
+ print("TOTAL FRAMES READ:", len(frames))
62
+
63
+ return frames, hand_flags
64
+
65
+
66
+ # ---------------- SEGMENTATION ----------------
67
+ def segment_frames(frames, hand_flags):
68
+ segments = []
69
+ start = 0
70
+ pause = 0
71
+
72
+ for i in range(len(frames)):
73
+ if hand_flags[i]:
74
+ pause += 1
75
+ else:
76
+ if pause >= MIN_PAUSE_FRAMES:
77
+ end = i - pause
78
+ if end - start >= MIN_SIGN_FRAMES:
79
+ segments.append((start, end))
80
+ start = i
81
+ pause = 0
82
+
83
+ if len(frames) - start >= MIN_SIGN_FRAMES:
84
+ segments.append((start, len(frames) - 1))
85
+
86
+ return segments
87
+
88
+
89
+ # ---------------- INFERENCE ----------------
90
+ def predict_segment(segment_frames, topk=5):
91
+
92
+ if len(segment_frames) < clip_length:
93
+ return []
94
+
95
+ results_accum = {}
96
+
97
+ # 🔥 FIX 1: proper sliding window (NOT naive chunking)
98
+ stride = clip_length // 2
99
+
100
+ for start in range(0, len(segment_frames) - clip_length, stride):
101
+
102
+ clip = segment_frames[start:start + clip_length]
103
+
104
+ # safety check
105
+ if clip.shape[0] != clip_length:
106
+ continue
107
+
108
+ clip = clip.transpose(3, 0, 1, 2)
109
+ clip_tensor = torch.from_numpy(clip).unsqueeze(0).to(device)
110
+
111
+ with torch.no_grad():
112
+ model = get_i3d_model()
113
+ logits = model(clip_tensor)
114
+ logits = torch.mean(logits, dim=2)
115
+ probs = F.softmax(logits, dim=1)
116
+
117
+ top_probs, top_idx = torch.topk(probs, k=topk, dim=1)
118
+
119
+ # 🔥 FIX 2: accumulate scores properly
120
+ for i, p in zip(top_idx[0], top_probs[0]):
121
+ idx = int(i)
122
+ if idx < len(gloss_list):
123
+ word = gloss_list[idx]
124
+
125
+ if word not in results_accum:
126
+ results_accum[word] = 0
127
+
128
+ results_accum[word] += float(p)
129
+
130
+ if not results_accum:
131
+ return []
132
+
133
+ # 🔥 FIX 3: sort final results
134
+ sorted_results = sorted(results_accum.items(), key=lambda x: x[1], reverse=True)
135
+
136
+ return sorted_results[:topk]
137
+
138
+
139
+ # ---------------- SELECTION ----------------
140
+ def select_best(results, context):
141
+ if not results:
142
+ return ""
143
+
144
+ return results[0][0]
145
+
146
+
147
+ # ---------------- PIPELINE ----------------
148
+ def run_pipeline(video_path):
149
+
150
+ print("\nSTARTING PIPELINE")
151
+
152
+ frames, hand_flags = load_video(video_path)
153
+
154
+ print("HAND FLAGS SAMPLE:", hand_flags[:20])
155
+ print("TRUE COUNT:", sum(hand_flags))
156
+
157
+ segments = segment_frames(frames, hand_flags)
158
+
159
+ print("SEGMENTS:", segments)
160
+
161
+ final_glosses = []
162
+
163
+ for s, e in segments:
164
+ segment = frames[s:e]
165
+
166
+ results = predict_segment(segment)
167
+
168
+ if not results:
169
+ continue
170
+
171
+ selected = select_best(results, final_glosses)
172
+ final_glosses.append(selected)
173
+
174
+ gloss_sentence = " ".join(final_glosses)
175
+
176
+ print("\nGLOSS:", gloss_sentence)
177
+
178
+ # IMPORTANT: no API test yet
179
+ english = "API NOT RUN (testing logic only)"
180
+
181
+ print("ENGLISH:", english)
182
+
183
+ return {
184
+ "gloss": gloss_sentence,
185
+ "english": english
186
+ }
pipelines/text_to_sign/assets_loader.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import numpy as np
3
+ from pathlib import Path
4
+ from .config import JSON_PATH, LANDMARKS_PATH
5
+
6
+ print("=" * 60)
7
+ print("Current working directory:", Path.cwd())
8
+ print("JSON_PATH:", JSON_PATH)
9
+ print("JSON exists:", Path(JSON_PATH).exists())
10
+ print("LANDMARKS_PATH:", LANDMARKS_PATH)
11
+ print("LANDMARKS exists:", Path(LANDMARKS_PATH).exists())
12
+ print("=" * 60)
13
+
14
+
15
+ def load_landmarks():
16
+ with open(JSON_PATH) as f:
17
+ data = json.load(f)
18
+
19
+ landmarks_data = np.load(LANDMARKS_PATH, allow_pickle=True)
20
+ landmarks_array = landmarks_data["landmarks"]
21
+
22
+ landmarks_dict = {}
23
+
24
+ for sample, lm in zip(data, landmarks_array):
25
+ gloss = sample["gloss"].lower().strip()
26
+ landmarks_dict[gloss] = lm
27
+
28
+ return landmarks_dict
pipelines/text_to_sign/config.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+
3
+ BASE_DIR = Path(__file__).resolve().parents[2]
4
+
5
+ JSON_PATH = BASE_DIR / "assets" / "text_to_sign" / "filtered_one_video_per_gloss.json"
6
+
7
+ LANDMARKS_PATH = (
8
+ BASE_DIR
9
+ / "assets"
10
+ / "text_to_sign"
11
+ / "wlasl2000_filtered_keypoints"
12
+ / "filtered_landmarks_V3"
13
+ / "filtered_landmarks_V3.npz"
14
+ )
15
+ FPS = 25
16
+ WIDTH = 1280
17
+ HEIGHT = 720
18
+
19
+ POSE_CONNECTIONS = [
20
+ (11,13),(13,15),(12,14),(14,16),(11,12),
21
+ (23,24),(11,23),(12,24),(23,25),(24,26),
22
+ (25,27),(26,28)
23
+ ]
24
+
25
+ FINGER_COLORS = {
26
+ "thumb": (0,128,255),
27
+ "index": (0,255,0),
28
+ "middle": (255,255,0),
29
+ "ring": (255,0,0),
30
+ "pinky": (255,0,255)
31
+ }
pipelines/text_to_sign/generator.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ import numpy as np
4
+ from .assets_loader import load_landmarks
5
+
6
+ landmarks_dict = None
7
+
8
+ def get_landmarks():
9
+ global landmarks_dict
10
+
11
+ if landmarks_dict is None:
12
+ print("Loading landmarks...")
13
+ landmarks_dict = load_landmarks()
14
+
15
+ return landmarks_dict
16
+
17
+ def generate_frames(mapped_sequence):
18
+
19
+ landmarks = get_landmarks()
20
+
21
+ all_frames = []
22
+
23
+ for item in mapped_sequence:
24
+
25
+ if item["type"] == "direct":
26
+ word = item["mapped"].lower()
27
+
28
+ if word in landmarks:
29
+ frames = [np.array(f, dtype=float) for f in landmarks[word]]
30
+ all_frames.extend(frames)
31
+
32
+ else:
33
+ word = re.sub(r"[^a-z]", "", item["original"].lower())
34
+
35
+ for letter in word:
36
+ if letter in landmarks:
37
+ frames = [np.array(f, dtype=float) for f in landmarks[letter]]
38
+ all_frames.extend(frames)
39
+
40
+ return all_frames
pipelines/text_to_sign/language.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import nltk
3
+ from nltk.stem import WordNetLemmatizer
4
+ from groq import Groq
5
+ import os
6
+ from dotenv import load_dotenv
7
+ load_dotenv()
8
+
9
+
10
+ try:
11
+ nltk.data.find("corpora/wordnet")
12
+ except LookupError:
13
+ nltk.download("wordnet")
14
+ nltk.download("omw-1.4")
15
+
16
+ lemmatizer = WordNetLemmatizer()
17
+ client = Groq(api_key=os.getenv("GROQ_API_KEY"))
18
+
19
+
20
+ # -----------------------------
21
+ # CLEAN LLM OUTPUT
22
+ # -----------------------------
23
+ def clean_llm_output(text):
24
+ text = text.strip()
25
+ text = text.split("\n")[0]
26
+ text = re.sub(r'[^A-Z0-9\s]', '', text.upper())
27
+ return text.strip()
28
+
29
+
30
+ # -----------------------------
31
+ # CLEAN GLOSS (SAFE)
32
+ # -----------------------------
33
+ def clean_gloss(gloss):
34
+ gloss = gloss.lower()
35
+ gloss = re.sub(r'[^a-z\s]', '', gloss)
36
+ gloss = re.sub(r'\s+', ' ', gloss)
37
+ return gloss.strip()
38
+
39
+ # -----------------------------
40
+ # MAP WORD TO VOCAB
41
+ # -----------------------------
42
+ def map_word(word, vocab_set):
43
+ word = word.lower()
44
+
45
+ if word in vocab_set:
46
+ return {"original": word, "mapped": word, "type": "direct"}
47
+
48
+ # fallback → fingerspelling
49
+ return {"original": word, "mapped": None, "type": "fingerspell"}
50
+
51
+
52
+ # -----------------------------
53
+ # LEMMATIZE
54
+ # -----------------------------
55
+ def lemmatize_words(words):
56
+ return [lemmatizer.lemmatize(w.lower()) for w in words]
57
+
58
+
59
+ # -----------------------------
60
+ # LLM → GLOSS (FIXED)
61
+ # -----------------------------
62
+ def llm_text_to_gloss(sentence):
63
+
64
+ prompt = f"""
65
+ You are an ASL gloss translator.
66
+
67
+ TASK:
68
+ Convert the sentence into ASL Gloss using ONLY valid vocabulary words.
69
+
70
+ STRICT RULES:
71
+ 1. Output ONLY space-separated words (NO hyphens, NO merging words)
72
+ 2. Do NOT create new words from vocabulary
73
+ 3. Use ONLY words from the vocabulary list
74
+ 4. Use uppercase only
75
+ 5. Remove: IS, AM, ARE, TO, THE, A, AN
76
+ 6. Keep natural ASL structure (time → topic → action when possible)
77
+ 7. Output ONE single line only
78
+
79
+ FINGERSPELLING RULE:
80
+ - If a word is NOT in the vocabulary list:
81
+ → spell it letter by letter separated by spaces
82
+ → example: "john" → J O H N
83
+
84
+ INPUT:
85
+ {sentence}
86
+
87
+
88
+ OUTPUT:
89
+ """
90
+
91
+ chat = client.chat.completions.create(
92
+ model="llama-3.1-8b-instant",
93
+ messages=[
94
+ {"role": "system", "content": "You output ONLY valid ASL gloss using given vocabulary."},
95
+ {"role": "user", "content": prompt}
96
+ ],
97
+ temperature=0.1
98
+ )
99
+
100
+ return clean_llm_output(chat.choices[0].message.content)
101
+
pipelines/text_to_sign/renderer.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ from .config import WIDTH, HEIGHT, FPS, POSE_CONNECTIONS
4
+
5
+ # =========================
6
+ # COLORS
7
+ # =========================
8
+ WHITE = (255, 255, 255)
9
+
10
+ # #190124 in HEX
11
+ # RGB = (25, 1, 36)
12
+ # OpenCV uses BGR
13
+ BACKGROUND = (36, 1, 25)
14
+
15
+ # =========================
16
+ # HAND
17
+ # =========================
18
+ def draw_hand_stylish(img, hand_points, thickness=5):
19
+ h, w = img.shape[:2]
20
+
21
+ pts = [(int(x * w), int(y * h)) for x, y, _ in hand_points]
22
+
23
+ fingers = {
24
+ "thumb": [0, 1, 2, 3, 4],
25
+ "index": [0, 5, 6, 7, 8],
26
+ "middle": [0, 9, 10, 11, 12],
27
+ "ring": [0, 13, 14, 15, 16],
28
+ "pinky": [0, 17, 18, 19, 20]
29
+ }
30
+
31
+ for idxs in fingers.values():
32
+ for i in range(len(idxs) - 1):
33
+ x1, y1 = pts[idxs[i]]
34
+ x2, y2 = pts[idxs[i + 1]]
35
+
36
+ cv2.line(
37
+ img,
38
+ (x1, y1),
39
+ (x2, y2),
40
+ WHITE,
41
+ thickness,
42
+ cv2.LINE_AA
43
+ )
44
+
45
+ # =========================
46
+ # POSE
47
+ # =========================
48
+ def draw_pose_lines(img, points, connections, thickness=4):
49
+ h, w = img.shape[:2]
50
+
51
+ pts = [(int(x * w), int(y * h)) for x, y, _ in points]
52
+
53
+ for i, j in connections:
54
+ if i < len(pts) and j < len(pts):
55
+ cv2.line(
56
+ img,
57
+ pts[i],
58
+ pts[j],
59
+ WHITE,
60
+ thickness,
61
+ cv2.LINE_AA
62
+ )
63
+
64
+ # =========================
65
+ # FACE LANDMARKS
66
+ # =========================
67
+ def draw_points_and_connections(img, points):
68
+ h, w = img.shape[:2]
69
+
70
+ pts = [(int(x * w), int(y * h)) for x, y, _ in points]
71
+
72
+ for (x, y) in pts:
73
+ if 0 <= x < w and 0 <= y < h:
74
+ cv2.circle(
75
+ img,
76
+ (x, y),
77
+ 2,
78
+ WHITE,
79
+ -1
80
+ )
81
+
82
+ # =========================
83
+ # MAIN DRAW
84
+ # =========================
85
+ def draw_skeleton(frame, img):
86
+ # Pose
87
+ draw_pose_lines(
88
+ img,
89
+ frame[42:75],
90
+ POSE_CONNECTIONS,
91
+ thickness=4
92
+ )
93
+
94
+ # Left hand
95
+ draw_hand_stylish(
96
+ img,
97
+ frame[0:21],
98
+ thickness=4
99
+ )
100
+
101
+ # Right hand
102
+ draw_hand_stylish(
103
+ img,
104
+ frame[21:42],
105
+ thickness=4
106
+ )
107
+
108
+ # Face
109
+ draw_points_and_connections(
110
+ img,
111
+ frame[75:553]
112
+ )
113
+
114
+ # =========================
115
+ # RENDER
116
+ # =========================
117
+ def render(frames, output="output.mp4"):
118
+ writer = cv2.VideoWriter(
119
+ output,
120
+ cv2.VideoWriter_fourcc(*"mp4v"),
121
+ FPS,
122
+ (WIDTH, HEIGHT)
123
+ )
124
+
125
+ for frame in frames:
126
+ # Create background using #190124
127
+ img = np.zeros((HEIGHT, WIDTH, 3), dtype=np.uint8)
128
+ img[:] = BACKGROUND
129
+
130
+ draw_skeleton(frame, img)
131
+ writer.write(img)
132
+
133
+ writer.release()
134
+ return output
pipelines/text_to_sign/run.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .language import (
2
+ clean_gloss,
3
+ map_word,
4
+ lemmatize_words,
5
+ llm_text_to_gloss,
6
+ )
7
+
8
+ from .generator import generate_frames, get_landmarks
9
+ from .renderer import render
10
+
11
+ # ==========================
12
+ # Lazy-loaded vocabulary
13
+ # ==========================
14
+
15
+ vocab_set = None
16
+
17
+
18
+ def get_vocab():
19
+ global vocab_set
20
+
21
+ if vocab_set is None:
22
+ print("Loading text-to-sign assets...")
23
+ vocab_set = set(get_landmarks().keys())
24
+
25
+ return vocab_set
26
+
27
+
28
+ # ==========================
29
+ # Main Pipeline
30
+ # ==========================
31
+
32
+ def run_pipeline(user_sentence):
33
+ print("INPUT:", user_sentence)
34
+
35
+ # STEP 1: Text → Gloss
36
+ raw_gloss = llm_text_to_gloss(user_sentence)
37
+ print("RAW GLOSS:", raw_gloss)
38
+
39
+ # STEP 2: Clean gloss
40
+ cleaned = clean_gloss(raw_gloss)
41
+ print("CLEANED:", cleaned)
42
+
43
+ # STEP 3: Tokenize + Lemmatize
44
+ tokens = cleaned.split()
45
+ tokens = lemmatize_words(tokens)
46
+ print("TOKENS:", tokens)
47
+
48
+ # STEP 4: Map words to vocabulary
49
+ vocab = get_vocab()
50
+ mapped_sequence = [map_word(token, vocab) for token in tokens]
51
+ print("MAPPED:", mapped_sequence)
52
+
53
+ # STEP 5: Generate landmark frames
54
+ frames = generate_frames(mapped_sequence)
55
+ print("FRAMES COUNT:", len(frames))
56
+
57
+ # STEP 6: Render video
58
+ video_path = render(frames)
59
+ print("VIDEO PATH:", video_path)
60
+
61
+ return video_path
requirements.txt ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ absl-py==2.4.0
2
+ annotated-doc==0.0.4
3
+ annotated-types==0.7.0
4
+ anyio==4.13.0
5
+ astunparse==1.6.3
6
+ attrs==26.1.0
7
+ beautifulsoup4==4.14.3
8
+ certifi==2026.4.22
9
+ cffi==2.0.0
10
+ charset-normalizer==3.4.7
11
+ click==8.3.3
12
+ colorama==0.4.6
13
+ contourpy==1.3.2
14
+ cryptography==47.0.0
15
+ cycler==0.12.1
16
+ distro==1.9.0
17
+ exceptiongroup==1.3.1
18
+ fastapi==0.136.1
19
+ filelock==3.29.0
20
+ flatbuffers==25.12.19
21
+ fonttools==4.62.1
22
+ fsspec==2026.4.0
23
+ gast==0.7.0
24
+ gdown==6.0.0
25
+ google-auth==2.50.0
26
+ google-auth-oauthlib==1.3.1
27
+ google-pasta==0.2.0
28
+ groq==1.2.0
29
+ grpcio==1.80.0
30
+ h11==0.16.0
31
+ h5py==3.16.0
32
+ httpcore==1.0.9
33
+ httpx==0.28.1
34
+ idna==3.13
35
+ jax==0.4.34
36
+ jaxlib==0.4.34
37
+ Jinja2==3.1.6
38
+ joblib==1.5.3
39
+ keras==2.15.0
40
+ kiwisolver==1.5.0
41
+ libclang==18.1.1
42
+ Markdown==3.10.2
43
+ MarkupSafe==3.0.3
44
+ matplotlib==3.10.9
45
+ mediapipe==0.10.21
46
+ ml-dtypes==0.2.0
47
+ mpmath==1.3.0
48
+ networkx==3.4.2
49
+ nltk==3.9.4
50
+ numpy==1.26.4
51
+ oauthlib==3.3.1
52
+ opencv-python-headless==4.9.0.80
53
+ opt_einsum==3.4.0
54
+ packaging==26.2
55
+ pillow==12.2.0
56
+ protobuf==4.25.9
57
+ pyasn1==0.6.3
58
+ pyasn1_modules==0.4.2
59
+ pycparser==3.0
60
+ pydantic==2.13.3
61
+ pydantic_core==2.46.3
62
+ pyparsing==3.3.2
63
+ PySocks==1.7.1
64
+ python-dateutil==2.9.0.post0
65
+ python-dotenv==1.2.2
66
+ python-multipart==0.0.27
67
+ regex==2026.4.4
68
+ requests==2.32.3
69
+ requests-oauthlib==2.0.0
70
+ scikit-learn==1.5.0
71
+ scipy==1.15.3
72
+ sentencepiece==0.2.1
73
+ six==1.17.0
74
+ sniffio==1.3.1
75
+ sounddevice==0.5.5
76
+ soupsieve==2.8.4
77
+ starlette==1.0.0
78
+ sympy==1.14.0
79
+ tensorboard==2.15.2
80
+ tensorboard-data-server==0.7.2
81
+ tensorflow==2.15.0
82
+ tensorflow-estimator==2.15.0
83
+ tensorflow-io-gcs-filesystem==0.31.0
84
+ termcolor==3.3.0
85
+ threadpoolctl==3.6.0
86
+ --extra-index-url https://download.pytorch.org/whl/cpu
87
+ torch==2.2.0+cpu
88
+ tqdm==4.67.3
89
+ typing-inspection==0.4.2
90
+ typing_extensions==4.15.0
91
+ urllib3==2.6.3
92
+ uvicorn==0.46.0
93
+ Werkzeug==3.1.8
94
+ wrapt==1.14.2
95
+ # Railway rebuild
start.sh ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ echo "Downloading assets..."
4
+ python download_assets.py
5
+
6
+ echo "Listing assets..."
7
+ find assets -maxdepth 4
8
+
9
+ echo "Starting FastAPI..."
10
+ uvicorn app.main:app --host 0.0.0.0 --port 7860