Verwendung von KI zur Übersetzung von Filmuntertiteln

In diesem Artikel wird erläutert, wie Sie mit Python ffmpeg und Gemini zum Übersetzen von Filmuntertiteln aufrufen. Der Effekt ist im Abschnitt „Effektanzeige“ zu finden.

Hintergrund

Ich habe mein letztes Unternehmen vor nicht allzu langer Zeit verlassen und mich wieder selbstständig gemacht. Ich habe fast nahtlos zwischen den vorherigen Jobs gewechselt, ohne viel nachzudenken. Diesmal habe ich beschlossen, sorgfältig darüber nachzudenken und mich zu entspannen und an etwas zu arbeiten, das mir Spaß macht. Ich kaufte ein NAS und stellte fest, dass meine IT-Kenntnisse bei der Arbeit endlich in meinem Leben zum Einsatz kamen, wobei es zunächst um chinesische Untertitel für Filme ging.

Der erste Schritt zu einem NAS besteht darin, hektisch mit dem Herunterladen von 4K-Filmen zu beginnen. Diese Filme sind alle mit Untertiteln ausgestattet, einige haben jedoch keine chinesischen Untertitel oder sind nicht gut übersetzt. Außerdem ist die von mir gekaufte NAS-Software nicht voll funktionsfähig und das Herunterladen chinesischer Untertitel ist mühsam, daher hoffe ich auf eine automatisierte Lösung. Nach der Evaluierung denke ich, dass wir die aktuelle KI wie ChatGPT und Gemini verwenden können, um englische Untertitel zu übersetzen, was zu guten Ergebnissen führen dürfte.

Verwenden Sie Poetry, um Projekte zu verwalten

Ich habe in den letzten Jahren nicht viele Python-Projekte durchgeführt, aber ich habe einige Projekte gesehen, in denen Poesie zum Einsatz kam , also habe ich beschlossen, es in diesem Projekt zu verwenden. Die Testerfahrung ist sehr gut, weitaus besser als die Pipenv, die ich zuvor verwendet habe.

Der Inhalt meiner pyproject.toml-Datei ist wie folgt:

[tool.poetry]
name = "upbox"
version = "0.1.0"
description = ""
authors = ["rocksun <[email protected]>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
ffmpeg-python = "^0.2.0"
llama-index = "^0.10.25"
llama-index-llms-gemini = "^0.1.6"
pysubs2 = "^1.6.1"
# yt-dlp = "^2024.4.9"
# typer = "^0.12.3"
# faster-whisper = "^1.0.1"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

Ich werde hier nicht näher auf die Verwendung von Poesie eingehen, Sie können es selbst lernen. Die Verpackungsbibliothek von ffmpeg wird hier zitiert (der Befehl ffmpeg ist im Pfad erforderlich). In diesem Artikel gibt es tatsächlich keinen großen Unterschied zwischen der Verwendung von llama-index und der entsprechenden Gemini-Bibliothek Verwenden Sie nicht zu viele Funktionen von llama-index. Es handelt sich schließlich um die Untertitelverarbeitungsbibliothek pysubs2. Ich habe einmal darüber nachgedacht, Untertitel direkt zu analysieren, aber später habe ich festgestellt, dass die Verwendung von pysubs2 immer noch viel Zeit sparen kann.

Extraktion englischer Untertitel

Es ist einfach, in ein Video eingebettete Untertitel über ffmpeg zu extrahieren.

ffmpeg -i my_file.mkv outfile.vtt

Tatsächlich enthält ein Video jedoch mehrere Untertitel, was nicht korrekt ist und daher noch bestätigt werden muss. Ich denke immer noch über die Verwendung einer ffmpeg-Bibliothek nach, nämlich ffmpeg-python . Der Code für die Verwendung dieser Bibliothek zum Extrahieren englischer Untertitel lautet wie folgt:

def _guess_eng_subtitle_index(video_path):
    probe = ffmpeg.probe(video_path)
    streams = probe['streams']
    for index, stream in enumerate(streams):
        if stream.get('codec_type') == 'subtitle' and stream.get('tags', {}).get('language') == 'eng':
            return index
    for index, stream in enumerate(streams):
        if stream['codec_type'] == 'subtitle' and stream.get('tags', {}).get('title', "").lower().find("english")!=-1 :
            return index
    return -1

def _extract_subtitle_by_index(video_path, output_path, index):
    return ffmpeg.input(video_path).output(output_path, map='0:'+str(index)).run()

def extract_subtitle(video_path, en_subtitle_path):
    # get the streams from video with ffprobe
    index = _guess_eng_subtitle_index(video_path)
    if index == -1:
        return -1
    
    return _extract_subtitle_by_index(video_path, en_subtitle_path, index)

Es wurde eine Methode hinzugefügt, _guess_eng_subtitle_indexum den Index der englischen Untertitel zu bestimmen. Dies liegt daran, dass die Untertitel der meisten Videos zwar relativ standardisiert sind, es aber tatsächlich einige Videos gibt, deren Untertitel überhaupt keine Tags haben, sodass ich das nur vermuten kann Es gibt noch einige in der Praxis. Andere Situationen können nur auf der Grundlage der tatsächlichen Situation behandelt werden.

Verarbeitung englischer Untertitel

Zuerst dachte ich, es würde ausreichen, einfach die Untertitel an Gemini zu übergeben und die Ergebnisse zu speichern, aber das hat nicht wirklich funktioniert. Es gab mehrere Probleme:

  1. In vielen englischen Untertiteln gibt es viele Tags, die sich auf die Wirkung beim Übersetzen auswirken
  2. Ist ein Untertitel zu groß, kommt Gemini damit nicht zurecht und ist der Kontext zu lang, kann es zu Problemen kommen.
  3. Der Zeitstempel in den Untertiteln ist zu lang, wodurch die Aufforderung zu lang wird.

Aus diesem Grund musste ich eine Untertitelklasse UpSubs hinzufügen, um die oben genannten Probleme zu lösen:

class UpSubs:
    def __init__(self, subs_path):
        self.subs = pysubs2.load(subs_path)

    def get_subtitle_text(self):
        text = ""
        for sub in self.subs:
            text += sub.text + "\n\n"
        return text

    def get_subtitle_text_with_index(self):
        text = ""
        for i, sub in enumerate(self.subs):
            text += "chunk-"+str(i) + ":\n" + sub.text.replace("\\N", " ") + "\n\n"
        return text
    
    def save(self, output_path):
        self.subs.save(output_path)

    def clean(self):
        indexes = []
        for i, sub in enumerate(self.subs):
            # remove xml tag and line change in sub text
            sub.text = re.sub(r"<[^>]+>", "", sub.text)
            sub.text = sub.text.replace("\\N", " ")

    def fill(self, text):
        text = text.strip()
        pattern = r"\n\s*\n"
        paragraphs = re.split(pattern, text)
        for para in paragraphs:
            try:
                firtline = para.split("\n")[0]
                countstr = firtline[6:len(firtline)-1]
                # print(countstr)
                index = int(countstr)
                p = "\n".join(para.split("\n")[1:])
                self.subs[index].text = p
            except Exception as e:
                print(f"Error merge paragraph : \n {para} \n with exception: \n {e}")
                raise(e)
    
    def merge_dual(self, subspath):
        second_subs = pysubs2.load(subspath)
        merged_subs = SSAFile()
        if len(self.subs.events) == len(second_subs.events):            
            for i, first_event in enumerate(self.subs.events):
                second_event = second_subs[i]
                if first_event.text == second_event.text:
                    merged_event = SSAEvent(first_event.start, first_event.end, first_event.text)
                else:
                    merged_event = SSAEvent(first_event.start, first_event.end, first_event.text + '\n' + second_event.text)
                merged_subs.append(merged_event)
            return merged_subs
        
        return None

cleanMit der Methode können Untertitel einfach bereinigt werden; mit der Speichermethode merge_dualkönnen zweisprachige Untertitel zusammengeführt werden. Diese sind relativ einfach und wir werden uns später auf die Verarbeitung des Untertiteltextes konzentrieren.

Das ursprüngliche SRT-Dateiformat ist wie folgt:

12
00:02:30,776 --> 00:02:34,780
Not even the great Dragon Warrior.

13
00:02:43,830 --> 00:02:45,749
Oh, where is Po?

14
00:02:45,749 --> 00:02:48,502
He was supposed to be here hours ago.

Die Methode wird get_subtitle_text_with_index:

chunk-12
Not even the great Dragon Warrior.

chunk-13
Oh, where is Po?

chunk-14
He was supposed to be here hours ago.

Dies geschieht, um die Anzahl der Wörter und Blöcke zu reduzieren. Darüber hinaus kann die Nummer jedes Untertitels weiterhin verfolgt werden. Durch filldie Methode können wir die Untertitel aus dem übersetzten Text wiederherstellen.

Rufen Sie Zwillinge an

Es gibt mehrere Probleme beim Anrufen von Zwillingen:

  • Zugangsschlüssel erforderlich
  • Für Inlandsbesuche ist ein geeigneter Agent erforderlich
  • Muss über eine gewisse Fehlertoleranz verfügen
  • Es besteht auch die Notwendigkeit, den Sicherheitsmechanismus von Gemini zu umgehen.

Daher completewurde eine spezielle Methode geschrieben, um diese Probleme anzugehen:

def complete(prompt, max_tokens=32760):
    prompt = prompt.strip()
    if not prompt:
        return ""
    
    safety_settings = [
        {
            "category": "HARM_CATEGORY_HARASSMENT",
            "threshold": "BLOCK_NONE"
        },
        {
            "category": "HARM_CATEGORY_HATE_SPEECH",
            "threshold": "BLOCK_NONE"
        },
        {
            "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
            "threshold": "BLOCK_NONE"
        },
        {
            "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
            "threshold": "BLOCK_NONE"
        },
    ]

    retries = 3
    for _ in range(retries):
        try:
            return Gemini(max_tokens=max_tokens, safety_settings=safety_settings, temperature = 0.01).complete(prompt).text
        except Exception as e:
            print(f"Error completing prompt: {prompt} \n with error: \n ")
            traceback.print_exc()
    return ""

safety_settingsEs ist sehr wichtig, dass in Filmuntertiteln häufig besonders sensible Ausdrücke vorkommen, und Zwillinge müssen darüber informiert werden, dass sie diese so weit wie möglich tolerieren. Obwohl dies laut Dokumentation nur mit kostenpflichtigen Konten möglich ist BLOCK_NONE, scheint es, dass ich bei der Übersetzung von Filmen nicht viele Probleme mit der oben genannten Konfiguration hatte. Gelegentlich traten jedoch einige auf, aber sie verschwanden, wenn ich es erneut versuchte.

Dann werden 3 Wiederholungsversuche hinzugefügt, die gelegentlich fehlschlagen.

Schließlich kann der API-Schlüssel über Google AI Studio bezogen werden. Fügen Sie dann eine .env-Datei zum Projekt hinzu:

http_proxy=http://192.168.0.107:7890
https_proxy=http://192.168.0.107:7890
GOOGLE_API_KEY=[your-api-key]

Das Programm kann den API-Schlüssel und die Proxy-Einstellungen lesen.

Aufrufvorgang

Schauen wir uns zunächst die äußerste tran_subtitlesMethode an

def tran_subtitles(fixed_subtitle, zh_subtitle=None, cncf = False, chunk_size=3000):
    subtitle_base = os.path.splitext(fixed_subtitle)[0]
    video_base = os.path.splitext(subtitle_base)[0]
    if zh_subtitle is None:
        zh_subtitle = video_base + ".zh-fixed.vtt"
    if os.path.exists(zh_subtitle):
        print(f"zh subtitle {zh_subtitle} already translated, skip to translate.")
        return 1

    prompt_tpl = MOVIE_TRAN_PROMPT_TPL
    opts = { }

    srtp = UpSubs(fixed_subtitle)
    text = srtp.get_subtitle_text_with_index()

    process_text(srtp, text, prompt_tpl, opts, chunk_size = chunk_size)
    srtp.save(zh_subtitle)

    return zh_subtitle

Die Logik ist relativ einfach. Lesen Sie die englischen Untertitel, get_subtitle_text_with_indexkonvertieren Sie sie mit der Methode in zu übersetzenden Text und führen Sie dann die Methode „process_text“ aus, um die Übersetzung abzuschließen. Die Eingabeaufforderungswortvorlage prompt_tpl verweist direkt auf MOVIE_TRAN_PROMPT_TPL, das Folgendes enthält:

MOVIE_TRAN_PROMPT_TPL = """你是个专业电影字幕翻译,你需要将一份英文字幕翻译成中文。
[需要翻译的英文字幕]:

{content}

# [中文字幕]:"""

Sie sehen, dass diese Eingabeaufforderung recht einfach ist.

Dann können Sie auf folgende Methoden achten process_text:

def process_text(subs, text, prompt_tpl, opts, chunk_size=2500):
    # ret = ""
    chunks = _split_subtitles(text, chunk_size)
    for(i, chunk) in enumerate(chunks):
        print("process chunk ({}/{})".format(i+1,len(chunks)))
        # if i==4:
        #     break
        # format string with all the field in a dict 
        opts["content"] = chunk
        prompt = prompt_tpl.format(**opts)

        print(prompt)
        out = complete(prompt, max_tokens=32760)
        subs.fill(out)
        print(out)

Teilen Sie den Untertiteltext mithilfe _split_subtitlesder Methode in mehrere Abschnitte auf und übergeben Sie sie dann an die oben genannte Methode complete.

Zeige Ergebnisse

Anfangs hatte ich keine großen Erwartungen an die Übersetzung der Untertitel, aber der Endeffekt war überraschend gut. Am Beispiel von Kung Fu Panda 4 ist dies ein Vergleich einiger Übersetzungen:

Englische Untertitel:

10
00:02:22,184 --> 00:02:27,606
Let it be known from the highest mountain
to the lowest valley that Tai Lung lives,

11
00:02:27,606 --> 00:02:30,776
and no one will stand in his way.

12
00:02:30,776 --> 00:02:34,780
Not even the great Dragon Warrior.

13
00:02:43,830 --> 00:02:45,749
Oh, where is Po?

Chinesischer Untertitel:

10
00:02:22,184 --> 00:02:27,606
让最高的山峰和最低的山谷都知道,泰隆还活着,

11
00:02:27,606 --> 00:02:30,776
没人能阻挡他。

12
00:02:30,776 --> 00:02:34,780
即使是伟大的神龙大侠也不行。

13
00:02:43,830 --> 00:02:45,749
哦,阿宝在哪儿?

Die Ergebnisse waren überraschend gut. Mein Prmopt lieferte keinen weiteren Kontext, aber Gemini lieferte eine authentische Übersetzung.

Zusammenfassen

Bei Filmen läuft der obige Code relativ stabil. Aber wenn man mit einigen Untertiteln konfrontiert wird, die an sich nicht sehr gut sind, sind auch die Übersetzungsergebnisse nicht sehr gut und es gibt viele Anomalien, sodass viele Verbesserungen vorgenommen werden müssen. Kürzlich hat mein Videokonto (Yunyunzhongshengs) einige technische Videos geteilt, die mit verbessertem Code implementiert wurden, und ich werde sie später mit Ihnen teilen.

Ich finde es großartig, Technologie nutzen zu können, um Leben zu verändern. Ich hoffe, dass es mehr Möglichkeiten für Verbesserungen gibt. Jeder ist willkommen, mehr Aufmerksamkeit zu schenken und zu kommunizieren.

Dieser Artikel wurde zuerst auf Yunyunzhongsheng ( https://yylives.cc/ ) veröffentlicht, jeder ist herzlich willkommen.

Ich habe beschlossen, Open-Source-Hongmeng aufzugeben . Wang Chenglu, der Vater von Open-Source-Hongmeng: Open-Source-Hongmeng ist die einzige Architekturinnovations- Industriesoftwareveranstaltung im Bereich Basissoftware in China – OGG 1.0 wird veröffentlicht, Huawei steuert den gesamten Quellcode bei Google Reader wird vom „Code-Scheißberg“ getötet Ubuntu 24.04 LTS wird offiziell veröffentlicht Vor der offiziellen Veröffentlichung von Fedora Linux 40 Microsoft-Entwickler: Die Leistung von Windows 11 ist „lächerlich schlecht“, Ma Huateng und Zhou Hongyi geben sich die Hand, „beseitigen den Groll“ Namhafte Spielefirmen haben neue Vorschriften erlassen: Hochzeitsgeschenke an Mitarbeiter dürfen 100.000 Yuan nicht überschreiten. Pinduoduo wurde wegen unlauteren Wettbewerbs zu einer Entschädigung von 5 Millionen Yuan verurteilt
{{o.name}}
{{m.name}}

Ich denke du magst

Origin my.oschina.net/u/6919515/blog/11054239
Empfohlen
Rangfolge