Spaces:
Sleeping
Sleeping
Initial Hugging Face deployment
Browse files- .gitattributes +2 -33
- .gitignore +16 -0
- Dockerfile +22 -0
- app/api/sign_to_text.py +25 -0
- app/api/text_to_sign.py +21 -0
- app/core/config.py +14 -0
- app/core/logger.py +6 -0
- app/main.py +12 -0
- app/models/i3d_model.py +15 -0
- app/services/sign_to_text_service.py +461 -0
- app/services/text_to_sign_service.py +8 -0
- app/utils/mediapipe_utils.py +30 -0
- app/utils/preprocessing.py +5 -0
- app/utils/video_utils.py +19 -0
- assets/I3D_300Weights/FINAL_nslt_100_iters=896_top1=65.89_top5=84.11_top10=89.92 (1).pt +3 -0
- assets/I3D_300Weights/FINAL_nslt_300_iters2997_top156.14_top579.94_top1086.98.pt +3 -0
- assets/I3D_modelfiles/language.py +308 -0
- assets/I3D_modelfiles/pytorch_i3d.py +354 -0
- assets/I3D_modelfiles/run.py +246 -0
- assets/I3D_modelfiles/wlasl_class_list.txt +2000 -0
- assets/preprocess_300/nslt_100.json +0 -0
- assets/preprocess_300/nslt_300.json +0 -0
- assets/text_to_sign/filtered_one_video_per_gloss.json +0 -0
- download_assets.py +32 -0
- nixpacks.toml +2 -0
- pipelines/sign_to_text/run.py +186 -0
- pipelines/text_to_sign/assets_loader.py +28 -0
- pipelines/text_to_sign/config.py +31 -0
- pipelines/text_to_sign/generator.py +40 -0
- pipelines/text_to_sign/language.py +101 -0
- pipelines/text_to_sign/renderer.py +134 -0
- pipelines/text_to_sign/run.py +61 -0
- requirements.txt +95 -0
- start.sh +10 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,4 @@
|
|
| 1 |
-
*.
|
| 2 |
-
|
| 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
|