lilblueyes commited on
Commit
15ccbbc
·
1 Parent(s): 9479558

Update documentation

Browse files
README.md CHANGED
@@ -1,47 +1,114 @@
1
  ---
2
- title: ASL TTS Test
3
  emoji: 🗣️
4
- colorFrom: blue
5
- colorTo: purple
6
  sdk: gradio
7
  python_version: "3.12"
8
  app_file: app.py
9
  pinned: false
10
  ---
11
 
12
- # ASL TTS Test
 
13
 
14
- MVP Gradio pour tester la pipeline ASL video/camera -> intent JSON -> llama.cpp -> Qwen3-TTS.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  ## Pipeline
17
 
18
  ```text
19
- Video upload / camera capture
20
- -> ASL sequential frame sampling
21
- -> MediaPipe landmarks, if installed
22
- -> TFLite ASL classifier over sliding temporal windows, if data/models/asl/model.tflite is present
23
- -> gloss sequence aggregation
24
- -> DeepFace emotion aggregation, if installed
25
- -> llama.cpp subtitle + voice instruction
26
  -> Qwen3-TTS audio
27
  ```
28
 
29
- The app is organized so each brick can fail independently with diagnostics instead of blocking
30
  the whole interface at startup.
31
 
32
- The demo screen is intentionally step-by-step:
33
 
34
  ```text
35
- 1 Analyze ASL -> debug overlay + intent JSON
36
- 2 Generate subtitle -> llama.cpp output
37
- 3 Generate speech -> Qwen3-TTS audio
38
  ```
39
 
40
- When the ASL classifier file is missing, the app reports `model_missing` and does not invent
41
- ASL words. An empty-by-default manual gloss override exists only under advanced debug controls
42
- for testing downstream LLM/TTS behavior.
 
 
 
 
 
 
 
 
43
 
44
- ## Local checks
 
 
 
 
45
 
46
  Run unit tests:
47
 
@@ -58,16 +125,17 @@ python3 scripts/test_tts_brick.py
58
  python3 scripts/test_full_pipeline.py
59
  ```
60
 
61
- `scripts/test_asl_brick.py` creates a tiny temporary demo clip when no video path is supplied.
62
- It also writes a debug overlay video path. To test the transparent fallback:
 
63
 
64
  ```bash
65
  python3 scripts/test_asl_brick.py --gloss-override "I LOVE YOU"
66
  ```
67
 
68
- ## ASL model files
69
 
70
- The ASL classifier assets are stored under:
71
 
72
  ```text
73
  data/models/asl/model.tflite
@@ -75,21 +143,21 @@ data/models/asl/train.csv
75
  data/models/asl/sign_to_prediction_index_map.json
76
  ```
77
 
78
- They come from:
 
79
 
80
- ```text
81
- https://github.com/jamesjbustos/sign-language-recognition
82
- ```
 
 
83
 
84
- Without these files, the ASL brick still samples frames and emits `model_missing` diagnostics.
85
- This model recognizes the isolated signs listed in `sign_to_prediction_index_map.json`; it is not
86
- a full sentence or fingerspelling recognizer. Predictions below `ASL_CONFIDENCE_THRESHOLD`
87
- defaulting to `0.70` are reported as `low_confidence` and are not forwarded as detected glosses.
88
 
89
- Uploaded videos use a phrase-prototype mode: frames are read in temporal order, landmarks are
90
- extracted once, then the ASL model runs over sliding windows. Accepted window predictions are
91
- collapsed into an ordered gloss sequence before llama.cpp rewrites them as natural speech. Tune it
92
- with:
93
 
94
  ```text
95
  ASL_UPLOAD_TARGET_FPS=12
@@ -99,15 +167,17 @@ ASL_SEQUENCE_STRIDE=15
99
  ASL_CONFIDENCE_THRESHOLD=0.70
100
  ```
101
 
102
- This is still not full continuous ASL translation, but it lets recorded phrase clips become
103
- `hello where water`-style gloss sequences instead of one global class.
 
104
 
105
- An experimental WLASL2000 I3D backend is also available for broader vocabulary coverage. It uses
106
- `raghuhasan/asl2000-i3d` from Hugging Face, downloads the I3D architecture helper if needed, and
107
- falls back to the TFLite detector when `ASL_DETECTOR_BACKEND=auto` cannot initialize it.
 
108
 
109
  ```text
110
- ASL_DETECTOR_BACKEND=auto # default: try WLASL2000, then fallback to TFLite
111
  ASL_DETECTOR_BACKEND=tflite # lightweight TFLite-only backend
112
  ASL_DETECTOR_BACKEND=wlasl_i3d # WLASL2000 I3D only
113
  WLASL_I3D_CONFIDENCE_THRESHOLD=0.20
@@ -116,15 +186,16 @@ WLASL_I3D_SEQUENCE_STRIDE=32
116
  WLASL_I3D_FRAME_SIZE=224
117
  ```
118
 
119
- The WLASL backend is heavier and more experimental. The model card reports 2,000 classes with
120
- 32.48% top-1, 57.31% top-5, and 66.31% top-10 accuracy, so the UI exposes top candidates and
121
- segment diagnostics instead of hiding uncertainty.
 
122
 
123
- Live camera debug prioritizes speed over long temporal batching. It starts predicting after
124
- `LIVE_ASL_MIN_FRAMES=4`, keeps a rolling buffer of `LIVE_ASL_MAX_FRAMES=12`, and runs ASL
125
- prediction every `LIVE_ASL_PREDICT_EVERY=1` frame. DeepFace emotion is heavier, so it runs every
126
- `LIVE_EMOTION_EVERY=45` frames by default. The overlay and status panel still show the current top
127
- candidates even when the accepted gloss is empty because the confidence is below threshold.
128
 
129
  Good first signs to test because they are in the model vocabulary:
130
 
@@ -132,7 +203,7 @@ Good first signs to test because they are in the model vocabulary:
132
  hello, where, who, why, yes, no, thankyou, please, water, happy, sad
133
  ```
134
 
135
- Reference clips/GIFs from the upstream demo list:
136
 
137
  ```text
138
  hello https://media.giphy.com/media/3o7TKNKOfKlIhbD3gY/giphy.gif
@@ -146,20 +217,21 @@ please https://lifeprint.com/asl101/gifs-animated/pleasecloseup.gif
146
  water https://lifeprint.com/asl101/gifs/w/water-2.gif
147
  ```
148
 
149
- ## GPU dependencies
150
 
151
- `flash-attn` is only useful on a CUDA GPU Space with compatible PyTorch/CUDA versions.
152
- Keep the default `requirements.txt` for CPU Spaces. If the Space is moved to a compatible
153
- GPU runtime, install the GPU dependency set instead:
154
 
155
  ```bash
156
  pip install -r requirements-gpu.txt --no-build-isolation
157
  ```
158
 
159
- ## Full ASL dependencies
160
 
161
- The default requirements keep the Space build lighter. For a full local ASL/emotion runtime with
162
- MediaPipe landmarks and DeepFace emotion detection, install:
 
163
 
164
  ```bash
165
  pip install -r requirements-asl-full.txt
 
1
  ---
2
+ title: Sign2Voice
3
  emoji: 🗣️
4
+ colorFrom: green
5
+ colorTo: yellow
6
  sdk: gradio
7
  python_version: "3.12"
8
  app_file: app.py
9
  pinned: false
10
  ---
11
 
12
+ A local-first AI stack that translates sign language, intent, and expression
13
+ into natural speech.
14
 
15
+ Sign2Voice is a Gradio demo built for the Hugging Face Build Small Hackathon.
16
+ It takes an uploaded or webcam ASL clip, extracts sign candidates and facial
17
+ expression signals, converts the result into a compact intent JSON, then speaks
18
+ the sentence with a local small-model voice stack.
19
+
20
+ ## Scope Note
21
+
22
+ We would have loved to push Sign2Voice further toward open-ended ASL
23
+ conversation during the hackathon. The main constraint was not the app shell or
24
+ the voice stack; it was data. Public ASL resources are still uneven for this
25
+ task: many usable datasets focus on isolated signs, dictionary retrieval, or
26
+ fingerspelling, while fluent ASL needs signer diversity, real-world lighting and
27
+ camera angles, temporal boundaries, facial grammar, body movement, and careful
28
+ expert annotation.
29
+
30
+ That is why this submission is intentionally evidence-first. It exposes top sign
31
+ candidates, confidence thresholds, segment diagnostics, and emotion metadata
32
+ instead of pretending that a small hackathon model can solve full ASL
33
+ translation end to end. Microsoft Research describes sign-language modeling as
34
+ being far behind spoken-language modeling largely because of a lack of
35
+ appropriate training data, and recent SLR survey work calls data acquisition and
36
+ annotation the main bottleneck for systems that work on fluent signing.
37
+
38
+ References:
39
+
40
+ - [ASL Citizen, Microsoft Research](https://www.microsoft.com/en-us/research/project/asl-citizen/)
41
+ - [Trends and Challenges for Sign Language Recognition with Machine Learning, ESANN 2023](https://www.esann.org/sites/default/files/proceedings/2023/ES2023-7.pdf)
42
+
43
+ ## Hackathon Fit
44
+
45
+ The project is aimed at the Backyard AI track: a practical communication tool
46
+ for people who need fast sign-to-speech support without sending every clip to a
47
+ cloud API.
48
+
49
+ Build Small constraints covered by this repo:
50
+
51
+ - Gradio app, ready for a Hugging Face Space.
52
+ - Small-model stack under the 32B parameter limit.
53
+ - Local-first runtime: ASL detection, llama.cpp text generation, and Qwen3-TTS
54
+ speech generation run in the app process.
55
+ - Step-by-step demo flow that shows intermediate evidence instead of hiding
56
+ uncertainty.
57
+
58
+ Badges that fit this build:
59
+
60
+ - Off the Grid / Local-first: no cloud inference API is required at runtime.
61
+ - Llama Champion: the intent-to-speech text step uses `llama.cpp`.
62
+ - Off-Brand / Custom UI: the Space uses custom Gradio styling.
63
+ - Field Notes: claim this only after publishing the build write-up.
64
+
65
+ Badges not claimed:
66
+
67
+ - Well-Tuned: this repo uses published models; it does not publish a new
68
+ fine-tuned model.
69
+ - Sharing is Caring: no public agent trace is included yet.
70
 
71
  ## Pipeline
72
 
73
  ```text
74
+ Video upload or camera capture
75
+ -> Sequential ASL frame sampling
76
+ -> MediaPipe landmarks, when installed
77
+ -> WLASL2000 I3D or TFLite ASL classifier
78
+ -> Ordered gloss sequence with confidence diagnostics
79
+ -> DeepFace emotion aggregation, when installed
80
+ -> llama.cpp subtitle and voice instruction
81
  -> Qwen3-TTS audio
82
  ```
83
 
84
+ Each brick can fail independently and return diagnostics instead of blocking
85
  the whole interface at startup.
86
 
87
+ The demo screen is intentionally staged:
88
 
89
  ```text
90
+ 1. Analyze ASL -> debug overlay + intent JSON
91
+ 2. Generate subtitle -> llama.cpp output
92
+ 3. Generate speech -> Qwen3-TTS audio
93
  ```
94
 
95
+ When no ASL classifier is available, Sign2Voice reports `model_missing` and
96
+ does not invent ASL words. The manual gloss override is empty by default and
97
+ lives under advanced debug controls for downstream LLM/TTS testing only.
98
+
99
+ ## Run Locally
100
+
101
+ Install the default CPU-friendly dependency set:
102
+
103
+ ```bash
104
+ pip install -r requirements.txt
105
+ ```
106
 
107
+ Start the Gradio app:
108
+
109
+ ```bash
110
+ python3 app.py
111
+ ```
112
 
113
  Run unit tests:
114
 
 
125
  python3 scripts/test_full_pipeline.py
126
  ```
127
 
128
+ `scripts/test_asl_brick.py` creates a tiny temporary clip when no video path is
129
+ supplied. It also writes a debug overlay video path. To test the transparent
130
+ fallback:
131
 
132
  ```bash
133
  python3 scripts/test_asl_brick.py --gloss-override "I LOVE YOU"
134
  ```
135
 
136
+ ## ASL Model Files
137
 
138
+ The ASL classifier assets live under:
139
 
140
  ```text
141
  data/models/asl/model.tflite
 
143
  data/models/asl/sign_to_prediction_index_map.json
144
  ```
145
 
146
+ They come from
147
+ [jamesjbustos/sign-language-recognition](https://github.com/jamesjbustos/sign-language-recognition).
148
 
149
+ Without these files, the ASL brick still samples frames and emits
150
+ `model_missing` diagnostics. The TFLite model recognizes the isolated signs in
151
+ `sign_to_prediction_index_map.json`; it is not a full sentence or fingerspelling
152
+ recognizer. Predictions below `ASL_CONFIDENCE_THRESHOLD`, defaulting to `0.70`,
153
+ are reported as `low_confidence` and are not forwarded as detected glosses.
154
 
155
+ Uploaded videos use a phrase-prototype mode: frames are read in temporal order,
156
+ landmarks are extracted once, and the ASL model runs over sliding windows.
157
+ Accepted window predictions are collapsed into an ordered gloss sequence before
158
+ `llama.cpp` rewrites them as natural speech.
159
 
160
+ Tune it with:
 
 
 
161
 
162
  ```text
163
  ASL_UPLOAD_TARGET_FPS=12
 
167
  ASL_CONFIDENCE_THRESHOLD=0.70
168
  ```
169
 
170
+ This is still not full continuous ASL translation, but it lets recorded phrase
171
+ clips become `hello where water`-style gloss sequences instead of one global
172
+ class.
173
 
174
+ An experimental WLASL2000 I3D backend is also available for broader vocabulary
175
+ coverage. It uses `raghuhasan/asl2000-i3d` from Hugging Face, downloads the I3D
176
+ architecture helper if needed, and falls back to the TFLite detector when
177
+ `ASL_DETECTOR_BACKEND=auto` cannot initialize it.
178
 
179
  ```text
180
+ ASL_DETECTOR_BACKEND=auto # default: WLASL2000, then TFLite
181
  ASL_DETECTOR_BACKEND=tflite # lightweight TFLite-only backend
182
  ASL_DETECTOR_BACKEND=wlasl_i3d # WLASL2000 I3D only
183
  WLASL_I3D_CONFIDENCE_THRESHOLD=0.20
 
186
  WLASL_I3D_FRAME_SIZE=224
187
  ```
188
 
189
+ The WLASL backend is heavier and more experimental. Its model card reports
190
+ 2,000 classes with 32.48% top-1, 57.31% top-5, and 66.31% top-10 accuracy, so
191
+ the UI exposes top candidates and segment diagnostics instead of hiding
192
+ uncertainty.
193
 
194
+ Live camera debug prioritizes speed over long temporal batching. It starts
195
+ predicting after `LIVE_ASL_MIN_FRAMES=4`, keeps a rolling buffer of
196
+ `LIVE_ASL_MAX_FRAMES=12`, and runs ASL prediction every
197
+ `LIVE_ASL_PREDICT_EVERY=1` frame. DeepFace emotion is heavier, so it runs every
198
+ `LIVE_EMOTION_EVERY=45` frames by default.
199
 
200
  Good first signs to test because they are in the model vocabulary:
201
 
 
203
  hello, where, who, why, yes, no, thankyou, please, water, happy, sad
204
  ```
205
 
206
+ Reference clips and GIFs from the upstream demo list:
207
 
208
  ```text
209
  hello https://media.giphy.com/media/3o7TKNKOfKlIhbD3gY/giphy.gif
 
217
  water https://lifeprint.com/asl101/gifs/w/water-2.gif
218
  ```
219
 
220
+ ## GPU Dependencies
221
 
222
+ `flash-attn` is only useful on a CUDA GPU Space with compatible PyTorch/CUDA
223
+ versions. Keep the default `requirements.txt` for CPU Spaces. If the Space is
224
+ moved to a compatible GPU runtime, install the GPU dependency set instead:
225
 
226
  ```bash
227
  pip install -r requirements-gpu.txt --no-build-isolation
228
  ```
229
 
230
+ ## Full ASL Dependencies
231
 
232
+ The default requirements keep the Space build lighter. For a full local
233
+ ASL/emotion runtime with MediaPipe landmarks and DeepFace emotion detection,
234
+ install:
235
 
236
  ```bash
237
  pip install -r requirements-asl-full.txt
app.py CHANGED
@@ -71,34 +71,34 @@ def build_video_input(label: str) -> gr.Video:
71
  )
72
 
73
 
74
- with gr.Blocks(title="SignSpeak Local") as demo:
75
  gr.HTML(
76
  """
77
  <main id="hero">
78
  <div class="brand-lockup">
79
  <span class="brand-mark" aria-hidden="true"></span>
80
  <div>
81
- <p class="eyebrow">Local ASL speech console</p>
82
- <h1>SignSpeak Local</h1>
83
  </div>
84
  </div>
85
  <div class="hero-grid">
86
  <div>
87
  <p class="hero-copy">
88
- Translate camera or uploaded ASL clips into expressive speech with a
89
- controllable ASL, llama.cpp, and Qwen3-TTS pipeline.
90
  </p>
91
  <div class="pipeline-rail" aria-label="Pipeline stages">
92
  <span>Capture</span>
93
- <span>ASL</span>
94
  <span>Intent</span>
95
  <span>Voice</span>
96
  </div>
97
  </div>
98
  <div class="system-strip" aria-label="System capabilities">
99
  <span>Camera ready</span>
100
- <span>Offline LLM</span>
101
- <span>Expressive TTS</span>
102
  </div>
103
  </div>
104
  </main>
@@ -223,7 +223,7 @@ with gr.Blocks(title="SignSpeak Local") as demo:
223
  gr.HTML(
224
  """
225
  <p class="footer-note">
226
- Hackathon target badges: Llama Champion, Off-Brand, Off the Grid.
227
  </p>
228
  """
229
  )
 
71
  )
72
 
73
 
74
+ with gr.Blocks(title="Sign2Voice") as demo:
75
  gr.HTML(
76
  """
77
  <main id="hero">
78
  <div class="brand-lockup">
79
  <span class="brand-mark" aria-hidden="true"></span>
80
  <div>
81
+ <p class="eyebrow">Local-first sign-to-speech console</p>
82
+ <h1>Sign2Voice</h1>
83
  </div>
84
  </div>
85
  <div class="hero-grid">
86
  <div>
87
  <p class="hero-copy">
88
+ Translate camera or uploaded signing clips into natural speech with
89
+ visible intent, expression, and confidence diagnostics.
90
  </p>
91
  <div class="pipeline-rail" aria-label="Pipeline stages">
92
  <span>Capture</span>
93
+ <span>Signs</span>
94
  <span>Intent</span>
95
  <span>Voice</span>
96
  </div>
97
  </div>
98
  <div class="system-strip" aria-label="System capabilities">
99
  <span>Camera ready</span>
100
+ <span>llama.cpp local</span>
101
+ <span>Expressive voice</span>
102
  </div>
103
  </div>
104
  </main>
 
223
  gr.HTML(
224
  """
225
  <p class="footer-note">
226
+ Build Small badges targeted: Off the Grid, Llama Champion, Off-Brand.
227
  </p>
228
  """
229
  )
scripts/test_llm_brick.py CHANGED
@@ -11,7 +11,7 @@ from signspeak.pipeline import DEFAULT_INTENT, json_text
11
 
12
  def main() -> None:
13
  parser = argparse.ArgumentParser(description="Run only the llama.cpp intent-to-text brick.")
14
- parser.add_argument("--intent", help="Path to an intent JSON file. Uses default mock intent if omitted.")
15
  args = parser.parse_args()
16
 
17
  intent_json = Path(args.intent).read_text(encoding="utf-8") if args.intent else json_text(DEFAULT_INTENT)
 
11
 
12
  def main() -> None:
13
  parser = argparse.ArgumentParser(description="Run only the llama.cpp intent-to-text brick.")
14
+ parser.add_argument("--intent", help="Path to an intent JSON file. Uses the sample intent if omitted.")
15
  args = parser.parse_args()
16
 
17
  intent_json = Path(args.intent).read_text(encoding="utf-8") if args.intent else json_text(DEFAULT_INTENT)
signspeak/__init__.py CHANGED
@@ -1,2 +1 @@
1
- """SignSpeak local ASL-to-speech pipeline package."""
2
-
 
1
+ """Sign2Voice local sign-to-speech pipeline package."""
 
signspeak/pipeline.py CHANGED
@@ -19,7 +19,7 @@ DEFAULT_INTENT = {
19
  "confidence": 0.83,
20
  },
21
  "communication_intent": "friendly_greeting",
22
- "pipeline_stage": "mock_asl_intent_for_llama_cpp_test",
23
  }
24
 
25
  DEFAULT_VIDEO_PATH = Path(__file__).resolve().parents[1] / "data" / "examples" / "videoplayback.mp4"
@@ -52,7 +52,7 @@ def apply_gloss_override(result: dict[str, Any], gloss_override: str | None) ->
52
  asl["top_prediction"] = " ".join(glosses)
53
  asl["status"] = f"{asl.get('status', 'unknown')}_with_manual_override"
54
  intent["detected_glosses"] = glosses
55
- intent["communication_intent"] = "manual_gloss_override_for_demo"
56
  intent.setdefault("diagnostics", {})["manual_gloss_override"] = True
57
  intent["diagnostics"]["override_reason"] = "ASL classifier is missing or uncertain; user supplied visible glosses."
58
  return result
@@ -88,7 +88,7 @@ def create_synthetic_demo_video() -> Path:
88
  except Exception as exc:
89
  raise RuntimeError("OpenCV is required to create the fallback demo video.") from exc
90
 
91
- output_path = Path(tempfile.gettempdir()) / "signspeak_demo_input.mp4"
92
  if output_path.exists():
93
  return output_path
94
 
@@ -111,7 +111,7 @@ def create_synthetic_demo_video() -> Path:
111
  cv2.circle(frame, (width - center_x, 144), 18, (129, 140, 248), -1)
112
  cv2.putText(
113
  frame,
114
- "SignSpeak demo",
115
  (36, 214),
116
  cv2.FONT_HERSHEY_SIMPLEX,
117
  0.62,
 
19
  "confidence": 0.83,
20
  },
21
  "communication_intent": "friendly_greeting",
22
+ "pipeline_stage": "sample_asl_intent_for_llama_cpp_check",
23
  }
24
 
25
  DEFAULT_VIDEO_PATH = Path(__file__).resolve().parents[1] / "data" / "examples" / "videoplayback.mp4"
 
52
  asl["top_prediction"] = " ".join(glosses)
53
  asl["status"] = f"{asl.get('status', 'unknown')}_with_manual_override"
54
  intent["detected_glosses"] = glosses
55
+ intent["communication_intent"] = "manual_gloss_override_for_pipeline_check"
56
  intent.setdefault("diagnostics", {})["manual_gloss_override"] = True
57
  intent["diagnostics"]["override_reason"] = "ASL classifier is missing or uncertain; user supplied visible glosses."
58
  return result
 
88
  except Exception as exc:
89
  raise RuntimeError("OpenCV is required to create the fallback demo video.") from exc
90
 
91
+ output_path = Path(tempfile.gettempdir()) / "sign2voice_demo_input.mp4"
92
  if output_path.exists():
93
  return output_path
94
 
 
111
  cv2.circle(frame, (width - center_x, 144), 18, (129, 140, 248), -1)
112
  cv2.putText(
113
  frame,
114
+ "Sign2Voice demo",
115
  (36, 214),
116
  cv2.FONT_HERSHEY_SIMPLEX,
117
  0.62,