binaryMao commited on
Commit
7b18ccd
·
verified ·
1 Parent(s): c446e17

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +33 -54
app.py CHANGED
@@ -8,16 +8,15 @@ import gradio as gr
8
  from huggingface_hub import snapshot_download
9
  from nemo.collections import asr as nemo_asr
10
 
11
- # Configuration du matériel
12
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
13
  SEGMENT_DURATION = 10.0
14
 
15
- # Dictionnaire des modèles
16
  MODELS = {
17
  "Soloni V3 (Recommandé CPU)": "RobotsMali/soloni-114m-tdt-ctc-v3"
18
  }
19
 
20
- # Cache pour éviter de recharger le modèle à chaque clic
21
  _cache = {}
22
 
23
  # ==========================
@@ -28,12 +27,12 @@ def get_model(model_name):
28
  return _cache[model_name]
29
 
30
  repo_id = MODELS[model_name]
31
- print(f"⏳ Téléchargement du modèle depuis {repo_id}...")
32
 
33
- # Téléchargement depuis Hugging Face
34
  folder = snapshot_download(repo_id, local_dir_use_symlinks=False)
35
 
36
- # Recherche automatique du fichier .nemo dans le dossier téléchargé
37
  try:
38
  nemo_file = next(
39
  os.path.join(folder, f)
@@ -41,11 +40,9 @@ def get_model(model_name):
41
  if f.endswith(".nemo")
42
  )
43
  except StopIteration:
44
- raise FileNotFoundError("Aucun fichier .nemo trouvé dans le dépôt.")
45
 
46
- print(f"📦 Restauration du modèle NeMo sur {DEVICE}...")
47
-
48
- # Utilisation de ASRModel pour l'auto-détection (CTC/RNNT)
49
  model = nemo_asr.models.ASRModel.restore_from(
50
  nemo_file,
51
  map_location=torch.device(DEVICE)
@@ -56,100 +53,82 @@ def get_model(model_name):
56
  return model
57
 
58
  # ==========================
59
- # PIPELINE DE TRANSCRIPTION
60
  # ==========================
61
  def pipeline(audio_path, model_name):
62
  if not audio_path:
63
- yield "❌ Aucun fichier audio fourni", ""
64
  return
65
 
66
- # Création d'un dossier temporaire unique pour les segments
67
  tmp_dir = tempfile.mkdtemp()
68
 
69
  try:
70
- yield "⏳ Préparation de l'audio (FFmpeg)...", ""
71
 
72
- # Normalisation et segmentation de l'audio
73
- # -ac 1 (mono), -ar 16000 (16kHz requis par NeMo)
74
- command = (
75
  f"ffmpeg -y -i '{audio_path}' -f segment -segment_time {SEGMENT_DURATION} "
76
  f"-ac 1 -ar 16000 {tmp_dir}/seg_%03d.wav > /dev/null 2>&1"
77
  )
78
- os.system(command)
79
 
80
- # Liste et tri des segments générés
81
  segments = sorted(glob.glob(os.path.join(tmp_dir, "seg_*.wav")))
82
-
83
- # Filtrer les fichiers trop petits (silences ou erreurs FFmpeg)
84
  segments = [s for s in segments if os.path.getsize(s) > 1000]
85
 
86
  if not segments:
87
- yield "❌ Erreur lors de la segmentation audio", ""
88
  return
89
 
90
- yield f"🎙️ Transcription de {len(segments)} segments en cours...", ""
91
 
92
- # Récupération du modèle
93
  model = get_model(model_name)
94
 
95
- # Inférence
96
  with torch.inference_mode():
97
- results = model.transcribe(
98
- segments,
99
- batch_size=4,
100
- num_workers=0 # Important sur CPU pour éviter les fuites de mémoire
101
- )
102
 
103
- # Reconstruction du texte
104
- # Note: selon le modèle, results peut être une liste de chaînes ou un objet complexe
105
  if isinstance(results, tuple):
106
- text_results = results[0]
107
  else:
108
- text_results = results
109
 
110
- final_text = " ".join(text_results)
111
-
112
- yield "✅ Transcription terminée", final_text
113
 
114
  except Exception as e:
115
- yield f"❌ Erreur système : {str(e)}", ""
116
 
117
  finally:
118
- # Nettoyage automatique du dossier temporaire
119
  if os.path.exists(tmp_dir):
120
  shutil.rmtree(tmp_dir)
121
 
122
  # ==========================
123
- # INTERFACE GRADIO (UI)
124
  # ==========================
125
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
126
- gr.Markdown("""
127
- # 🤖 RobotsMali ASR Edition CPU
128
- Déposez un fichier audio pour obtenir une transcription automatique.
129
- """)
130
 
131
  with gr.Row():
132
  with gr.Column():
133
- audio_input = gr.Audio(type="filepath", label="Fichier Audio (WAV, MP3, etc.)")
134
  model_input = gr.Dropdown(
135
  choices=list(MODELS.keys()),
136
  value="Soloni V3 (Recommandé CPU)",
137
- label="Sélection du Modèle"
138
  )
139
- btn = gr.Button("🚀 Lancer la Transcription", variant="primary")
140
 
141
  with gr.Column():
142
- status = gr.Markdown("### Statut : Prêt")
143
- output = gr.Textbox(label="Résultat de la transcription", lines=12, show_copy_button=True)
144
 
145
- # Interaction
146
  btn.click(
147
  fn=pipeline,
148
  inputs=[audio_input, model_input],
149
- outputs=[status, output]
150
  )
151
 
152
- gr.Markdown("--- \n *Optimisé pour les environnements à ressources limitées.*")
153
-
154
  if __name__ == "__main__":
155
  demo.launch()
 
8
  from huggingface_hub import snapshot_download
9
  from nemo.collections import asr as nemo_asr
10
 
11
+ # Configuration
12
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
13
  SEGMENT_DURATION = 10.0
14
 
15
+ # Modèle unique pour CPU (CTC est plus léger)
16
  MODELS = {
17
  "Soloni V3 (Recommandé CPU)": "RobotsMali/soloni-114m-tdt-ctc-v3"
18
  }
19
 
 
20
  _cache = {}
21
 
22
  # ==========================
 
27
  return _cache[model_name]
28
 
29
  repo_id = MODELS[model_name]
30
+ print(f"⏳ Chargement du modèle : {repo_id}")
31
 
32
+ # Téléchargement local
33
  folder = snapshot_download(repo_id, local_dir_use_symlinks=False)
34
 
35
+ # Trouve le fichier .nemo
36
  try:
37
  nemo_file = next(
38
  os.path.join(folder, f)
 
40
  if f.endswith(".nemo")
41
  )
42
  except StopIteration:
43
+ raise FileNotFoundError("Fichier .nemo introuvable.")
44
 
45
+ # Restauration générique (Auto-détecte CTC/RNNT)
 
 
46
  model = nemo_asr.models.ASRModel.restore_from(
47
  nemo_file,
48
  map_location=torch.device(DEVICE)
 
53
  return model
54
 
55
  # ==========================
56
+ # LOGIQUE DE TRANSCRIPTION
57
  # ==========================
58
  def pipeline(audio_path, model_name):
59
  if not audio_path:
60
+ yield "❌ Erreur", "Veuillez fournir un fichier audio."
61
  return
62
 
 
63
  tmp_dir = tempfile.mkdtemp()
64
 
65
  try:
66
+ yield "⏳ Traitement audio...", ""
67
 
68
+ # Segmentation FFmpeg (16kHz Mono requis)
69
+ # On utilise une syntaxe simple pour éviter les erreurs de shell
70
+ os.system(
71
  f"ffmpeg -y -i '{audio_path}' -f segment -segment_time {SEGMENT_DURATION} "
72
  f"-ac 1 -ar 16000 {tmp_dir}/seg_%03d.wav > /dev/null 2>&1"
73
  )
 
74
 
 
75
  segments = sorted(glob.glob(os.path.join(tmp_dir, "seg_*.wav")))
 
 
76
  segments = [s for s in segments if os.path.getsize(s) > 1000]
77
 
78
  if not segments:
79
+ yield "❌ Erreur", "Impossible de segmenter l'audio."
80
  return
81
 
82
+ yield f"🎙️ Transcription ({len(segments)} segments)...", ""
83
 
 
84
  model = get_model(model_name)
85
 
 
86
  with torch.inference_mode():
87
+ # Transcription par batch pour le CPU
88
+ results = model.transcribe(segments, batch_size=2, num_workers=0)
 
 
 
89
 
90
+ # Gestion des différents formats de sortie de NeMo
 
91
  if isinstance(results, tuple):
92
+ text_list = results[0]
93
  else:
94
+ text_list = results
95
 
96
+ final_text = " ".join(text_list)
97
+ yield "✅ Terminé", final_text
 
98
 
99
  except Exception as e:
100
+ yield "❌ Erreur Système", str(e)
101
 
102
  finally:
 
103
  if os.path.exists(tmp_dir):
104
  shutil.rmtree(tmp_dir)
105
 
106
  # ==========================
107
+ # INTERFACE UTILISATEUR
108
  # ==========================
109
+ with gr.Blocks() as demo:
110
+ gr.Markdown("# 🤖 RobotsMali ASR")
111
+ gr.Markdown("Outil de transcription automatique optimisé pour le CPU.")
 
 
112
 
113
  with gr.Row():
114
  with gr.Column():
115
+ audio_input = gr.Audio(type="filepath", label="Audio")
116
  model_input = gr.Dropdown(
117
  choices=list(MODELS.keys()),
118
  value="Soloni V3 (Recommandé CPU)",
119
+ label="Modèle"
120
  )
121
+ btn = gr.Button("🚀 Transcrire", variant="primary")
122
 
123
  with gr.Column():
124
+ status_output = gr.Textbox(label="Statut", interactive=False)
125
+ text_output = gr.Textbox(label="Texte Transcrit", lines=10)
126
 
 
127
  btn.click(
128
  fn=pipeline,
129
  inputs=[audio_input, model_input],
130
+ outputs=[status_output, text_output]
131
  )
132
 
 
 
133
  if __name__ == "__main__":
134
  demo.launch()