pokung commited on
Commit
1ccb8ec
·
verified ·
1 Parent(s): fb99a15

Upload 27 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,12 @@ saved_model/**/* 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
 
 
 
 
 
 
 
 
 
 
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
36
+ assets/logo.png filter=lfs diff=lfs merge=lfs -text
37
+ examples/0x0006de19.wav filter=lfs diff=lfs merge=lfs -text
38
+ examples/0x0008d8e5.wav filter=lfs diff=lfs merge=lfs -text
39
+ examples/0x000f7780.wav filter=lfs diff=lfs merge=lfs -text
40
+ examples/0x000faf94.wav filter=lfs diff=lfs merge=lfs -text
41
+ examples/0x0011ce99.wav filter=lfs diff=lfs merge=lfs -text
42
+ examples/0x0011cedc.wav filter=lfs diff=lfs merge=lfs -text
43
+ examples/dialogue_1.wav filter=lfs diff=lfs merge=lfs -text
44
+ examples/dialogue_9.wav filter=lfs diff=lfs merge=lfs -text
assets/logo.png ADDED

Git LFS Details

  • SHA256: 9512c8c28e0faeea4f339dc4f66287727e007a33b1d8d48979d7e8cdd08cedeb
  • Pointer size: 131 Bytes
  • Size of remote file: 256 kB
assets/schema.png ADDED
examples/0x0006de19.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:22799d5aaf1f1c10bd98b5117585dbf862268336fb0ed43b6213db5014cfd762
3
+ size 200464
examples/0x0008d8e5.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bf4cbd5261c35939b2bcd26fbdcc5ee7f4d861986f67feb4b7e1698a8deb3880
3
+ size 214576
examples/0x000f7780.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:daff7d700b3b7da0d4c1d4b1d26ef6a0978ed9bd563d802563fd7535738bfc7f
3
+ size 317124
examples/0x000faf94.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:890b88c846f65fe12ca9f7c25cdb0d6b81c3dba6297ae2f21b9e812a82ef74e1
3
+ size 525982
examples/0x0011ce99.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:381520ad242a2f2b5292117b264706d5f9a8f02d86125dc953742dd725cae514
3
+ size 419672
examples/0x0011cedc.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2da3034a48fe6b51a766900804aa8a88c0c3859c22383cd8948ede4d8fe1d79a
3
+ size 796462
examples/dialogue_1.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:45d53cc727fe1d3ee851a8cb339e41b201b067094cdbe13b2c98cf2bf54d96de
3
+ size 1763754
examples/dialogue_9.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c6d82ae45a082cdee06a54aed78666506b4570885b8674d83707e2823ba1f975
3
+ size 882044
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ coqui-tts==0.26.2
2
+ deepl==1.22.0
3
+ dotenv==0.9.9
4
+ gradio==5.34.2
5
+ librosa==0.11.0
6
+ torch==2.7.1
7
+ transformers==4.51.3
src/__pycache__/asr.cpython-310.pyc ADDED
Binary file (1.54 kB). View file
 
src/__pycache__/asr.cpython-311.pyc ADDED
Binary file (2.5 kB). View file
 
src/__pycache__/translation.cpython-310.pyc ADDED
Binary file (954 Bytes). View file
 
src/__pycache__/translation.cpython-311.pyc ADDED
Binary file (1.44 kB). View file
 
src/__pycache__/tts.cpython-310.pyc ADDED
Binary file (1.2 kB). View file
 
src/__pycache__/tts.cpython-311.pyc ADDED
Binary file (1.75 kB). View file
 
src/content/about_tab.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ def about_tab():
4
+ with gr.TabItem("ℹ️ **O projekcie**"):
5
+ gr.Markdown("## O DubAI")
6
+ gr.Markdown("""
7
+ DubAI to aplikacja typu end-to-end umożliwiająca:
8
+
9
+ - transkrypcję mowy (ASR),
10
+ - tłumaczenie na inny język,
11
+ - oraz syntezę mowy z zachowaniem brzmienia głosu.
12
+
13
+ **Zastosowania:** dubbing gier, AI-asystenci, tworzenie voice-overów.
14
+
15
+ **Technologie:** Whisper, DeepL, XTTS, Gradio, Python.
16
+ """)
src/content/contact_tab.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ def contact_tab():
4
+ with gr.TabItem("📩 Kontakt"):
5
+ gr.Markdown("## Formularz kontaktowy")
6
+ gr.Textbox(label="📧 Twój email")
7
+ gr.Textbox(label="💬 Wiadomość", lines=4)
8
+ gr.Button("✉️ Wyślij (do zaimplementowania)")
src/content/front_image.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from src.utils import get_base64_image
3
+
4
+ def image_tab():
5
+ gr.HTML("""
6
+ <style>
7
+ .full-width-img-container {
8
+ width: 100%;
9
+ overflow: hidden;
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+ .full-width-img {
14
+ width: 100%;
15
+ height: auto;
16
+ display: block;
17
+ }
18
+ </style>
19
+ """)
20
+
21
+ img_data = get_base64_image("assets/logo.png")
22
+ logo_html = f"""
23
+ <div class="full-width-img-container">
24
+ <img src="{img_data}"
25
+ class="full-width-img"
26
+ alt="Logo" />
27
+ </div>
28
+ """
29
+ gr.HTML(logo_html)
src/content/method_tab.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from src.utils import get_base64_image
3
+
4
+ def method_tab():
5
+ with gr.Tab("⚙️ Metoda"):
6
+ with gr.Column(elem_classes="method-tab"):
7
+ gr.HTML('<div style="font-size: 40px; font-family: Myriad, sans-serif; font-weight: bold; text-align: left;">⚙️ Metoda</div>')
8
+
9
+ gr.HTML("""
10
+ <div style="font-size: 18px; font-family: Myriad, sans-serif;">
11
+ Proces rozpoczęto od oczyszczenia nagrań z zakłóceń tła przy użyciu modelu Demucs, który umożliwił skuteczne wydzielenie sygnału mowy. Następnie zastosowano Mossformer2 do separacji mówców, co pozwoliło rozdzielić wypowiedzi różnych osób w nagraniu. Transkrypcja i segmentacja audio zostały przeprowadzone za pomocą modelu Whisper, znanego z wysokiej jakości rozpoznawania mowy. Otrzymane wypowiedzi pogrupowano przy użyciu algorytmu DBSCAN, co ułatwiło identyfikację spójnych segmentów konwersacyjnych.
12
+ <br><br>
13
+ Do tłumaczenia wykorzystano polski wielojęzyczny model językowy Bielik, który dobrze radzi sobie z przekładem tekstów. Jakość tłumaczeń oceniano przy użyciu metryk COMET, BERTScore i BLEU, zapewniających obiektywną ocenę pod względem zgodności semantycznej i płynności.
14
+ <br><br>
15
+ Syntezę mowy zrealizowano przy użyciu modelu XTTS-v2, obsługującego 17 języków i umożliwiającego generowanie naturalnego głosu na podstawie jedynie 6-sekundowego nagrania referencyjnego. Został on wybrany ze względu na dobrą jakość dźwięku, szybkość działania oraz lepszą kompatybilność z językiem polskim niż inne dostępne modele. Na końcu przeprowadzono subiektywną ocenę końcowego dźwięku w formie testów odsłuchowych.
16
+ <br><br>
17
+ Na poniższym schemacie przedstawiono szczegółowy przebieg procesu.
18
+ </div>
19
+ """)
20
+
21
+ img_data = get_base64_image("assets/schema.png")
22
+ schema_html = f"""
23
+ <div style="display: flex; justify-content: center; padding: 20px;">
24
+ <img src="{img_data}"
25
+ style="
26
+ width: 100%;
27
+ max-width: 1000px;
28
+ border: none;
29
+ object-fit: contain;
30
+ "
31
+ alt="Schemat procesu"
32
+ />
33
+ </div>
34
+ """
35
+ gr.HTML(schema_html)
36
+ gr.HTML('<div style="text-align: center; font-size: 18px; font-family: Myriad, sans-serif;"><strong>Schemat 1.</strong> Przebieg procesu przetwarzania nagrania.</div>')
37
+
38
+ gr.HTML('<div style="font-size: 40px; font-family: Myriad, sans-serif; font-weight: bold; text-align: left;">📊 Wyniki</div>')
39
+
40
+ gr.HTML('<div style="font-size: 40px; font-family: Myriad, sans-serif; font-weight: bold; text-align: left;">🎵 Nagrania przykładowe</div>')
41
+ with gr.Row():
42
+ with gr.Column():
43
+ gr.Audio("examples/0x000f7780.wav", label="Nagranie 1", type="filepath")
44
+ with gr.Column():
45
+ gr.Audio("examples/0x000faf94.wav", label="Nagranie 2", type="filepath")
46
+ with gr.Row():
47
+ with gr.Column():
48
+ gr.Audio("examples/0x0006de19.wav", label="Nagranie 3", type="filepath")
49
+ with gr.Column():
50
+ gr.Audio("examples/0x0008d8e5.wav", label="Nagranie 4", type="filepath")
51
+ with gr.Row():
52
+ with gr.Column():
53
+ gr.Audio("examples/0x0011ce99.wav", label="Nagranie 5", type="filepath")
54
+ with gr.Column():
55
+ gr.Audio("examples/0x0011cedc.wav", label="Nagranie 6", type="filepath")
56
+ with gr.Row():
57
+ with gr.Column():
58
+ gr.Audio("examples/dialogue_1.wav", label="Dialog 1", type="filepath")
59
+ with gr.Column():
60
+ gr.Audio("examples/dialogue_9.wav", label="Dialog 2", type="filepath")
src/content/transcription_tab.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ def transcription_tab(full_pipeline):
4
+ with gr.Tab("🎙️ Aplikacja"):
5
+ with gr.Column(elem_classes="method-tab"):
6
+ gr.HTML('<div style="font-size: 40px; font-family: Myriad, sans-serif; font-weight: bold; text-align: left;">🎙️ Aplikacja</div>')
7
+ gr.HTML("""
8
+ <div style="font-size: 18px; font-family: Myriad, sans-serif;">
9
+ Jesteś ciekawy, jak Twój głos brzmi w innym języku? Możesz to sprawdzić! W ramach projektu udostępniamy wersję demonstracyjną naszej aplikacji. Wystarczy nagrać swoją wypowiedź w języku polskim, a aplikacja automatycznie przetłumaczy ją na język angielski i wygeneruje syntezę głosu. Wystarczy kliknąć przycisk "Uruchom", aby rozpocząć proces.
10
+ <br><br>
11
+ A może masz już gotowy plik audio z wypowiedzią w języku polskim? Możesz go przesłać, przeciągając go do pola poniżej lub klikając na nie, aby otworzyć okno wyboru pliku. Aplikacja obsługuje pliki w formacie WAV.
12
+ <br><br>
13
+ Architektura owej wersji demonstracyjnej jest okrojoną wersją naszego pełnego rozwiązania, jednak zawiera najważniejsze funkcjonalności pozwalające na relatywnie szybkie działanie. Składa się ona z modelu transkrypcji mowy Whisper Large v3 Turbo, modułu tłumaczenia wykorzystującego API od DeepL, oraz modelu syntezy mowy XTTS.
14
+ """)
15
+
16
+ audio_input = gr.Audio(type="filepath", label="🎙️ Nagranie mowy (PL)")
17
+ tts_output = gr.Audio(label="🗣️ Synteza głosu (EN)", type="filepath")
18
+
19
+ run_btn = gr.Button("▶️ Uruchom")
20
+
21
+ run_btn.click(
22
+ fn=full_pipeline,
23
+ inputs=audio_input,
24
+ outputs=[tts_output]
25
+ )
src/models/asr.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import librosa
2
+ from transformers import WhisperProcessor, WhisperForConditionalGeneration
3
+ from typing import Optional
4
+ import torch
5
+
6
+
7
+ class ASRService:
8
+ def __init__(self):
9
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
10
+ self.processor, self.model = self._load_model()
11
+
12
+ def _load_model(self):
13
+ """Ładowanie modelu Whisper"""
14
+ model_name = "openai/whisper-large-v3-turbo"
15
+ processor = WhisperProcessor.from_pretrained(model_name)
16
+ model = WhisperForConditionalGeneration.from_pretrained(model_name).to(self.device)
17
+ model.config.forced_decoder_ids = processor.get_decoder_prompt_ids(language="pl", task="transcribe")
18
+ return processor, model
19
+
20
+ def transcribe(self, audio_path: str) -> str:
21
+ """Transkrypcja audio na tekst"""
22
+ sample, sr = librosa.load(audio_path, sr=16000)
23
+ input_features = self.processor(
24
+ sample,
25
+ sampling_rate=sr,
26
+ return_tensors="pt"
27
+ ).input_features.to(self.device)
28
+
29
+ predicted_ids = self.model.generate(input_features)
30
+ return self.processor.batch_decode(predicted_ids, skip_special_tokens=True)[0]
31
+
32
+ # Singleton pattern
33
+ ASR = ASRService()
src/models/pipeline.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from src.models.tts.xtts import XTTS
2
+ from src.models.asr import ASR
3
+ from src.models.translation import TRANSLATOR
4
+ import os
5
+ OUTPUT_WAV_PATH = "output.wav"
6
+
7
+
8
+ def PIPELINE(audio_file: str) -> str:
9
+ # TRANSCRIPTION
10
+ transcript = ASR.transcribe(audio_file)
11
+ # TRANSLATION
12
+ translation = TRANSLATOR.translate(transcript, source_lang="pl", target_lang="EN")
13
+ # SYNTHESIS
14
+ if os.path.exists(OUTPUT_WAV_PATH):
15
+ os.remove(OUTPUT_WAV_PATH)
16
+ XTTS.synthesize(
17
+ text=translation,
18
+ reference_wav=audio_file,
19
+ output_path=OUTPUT_WAV_PATH,
20
+ language="en"
21
+ )
22
+
23
+ return OUTPUT_WAV_PATH
src/models/translation.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import deepl
2
+ import os
3
+ from typing import Optional
4
+ from dotenv import load_dotenv
5
+
6
+
7
+ load_dotenv()
8
+
9
+
10
+ class TranslationService:
11
+ def __init__(self):
12
+ self.api_key = os.getenv("DEEPL_API_KEY")
13
+ if not self.api_key:
14
+ raise ValueError("Zmienna środowiskowa DEEPL_API_KEY nie jest ustawiona!")
15
+ self.translator = deepl.Translator(self.api_key)
16
+
17
+ def translate(
18
+ self,
19
+ text: str,
20
+ source_lang: str = "PL",
21
+ target_lang: str = "EN"
22
+ ) -> str:
23
+ """Tłumaczenie tekstu"""
24
+ result = self.translator.translate_text(
25
+ text,
26
+ source_lang=source_lang,
27
+ target_lang=target_lang
28
+ )
29
+ return result.text
30
+
31
+ # Singleton pattern
32
+ TRANSLATOR = TranslationService()
src/models/tts/xtts.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from TTS.api import TTS
2
+ import torch
3
+ from typing import Optional
4
+
5
+ class TTSService:
6
+ def __init__(self):
7
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
8
+ self.model = self._load_model()
9
+
10
+ def _load_model(self):
11
+ """Ładowanie modelu XTTS v2"""
12
+ return TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(self.device)
13
+
14
+ def synthesize(
15
+ self,
16
+ text: str,
17
+ reference_wav: str,
18
+ output_path: str,
19
+ language: str = "en"
20
+ ) -> None:
21
+ """Generacja pliku audio z tekstu"""
22
+ self.model.tts_to_file(
23
+ text=text,
24
+ speaker_wav=reference_wav,
25
+ language=language,
26
+ file_path=output_path
27
+ )
28
+
29
+ # Singleton pattern
30
+ XTTS = TTSService()
src/utils.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import base64
2
+
3
+ def get_base64_image(img_path):
4
+ with open(img_path, "rb") as f:
5
+ encoded = base64.b64encode(f.read()).decode("utf-8")
6
+ return f"data:image/png;base64,{encoded}"