Hanschrifterkennung in PDF-Dateien und entsprechendes Umbenennen

Du hast eine Idee für ein Projekt?
Antworten
Fisatec
User
Beiträge: 2
Registriert: Mittwoch 9. August 2023, 11:35

Hallo zusammen,
für ein Skript zur Erkennung von Handschrift und automatischem Ubenennen einer PDF-Datei habe ich mir mit ChatGPT ein bisschen was zusammengebastelt.
Im Grunde funktioniert das Skript, aber ich bin leider nicht in der Lage, weitere Anpassungen vorzunehmen. Meine Programmierkenntnisse streben nämlich leider gegen Null.

Meine Idee ist, dass sich ein GUI mit Drag&Drop Unterstütung (Tkinter mit TkinterDnD) öffnet, ich mehrere PDF-Dateien droppen kann, welche dann nacheinander das Skript durchlaufen.
Im Skript sollen dann folgende Schritte abgearbeitet werden:

1. Eine einseitige PDF-Datei soll geladen und in eine PNG-Datei konvertiert werden, die den gleichen Namen und den gleichen Pfad besitzt, wie die geladene PDF-Datei. Zum Konvertieren soll pdf2image genutzt werden und die konvertierte PNG-Datei soll schwarz-weiß sein.

2. Die erstellte PNG-Datei soll mit Hilfe der Google Vision API auf Text und Handschrift untersucht werden (die JSON-Datei zur Authentifizierung gebe ich direkt im Skript an).

3. Der erkannte Text soll mit bestimmten Keywords aus einer separaten keywords.txt verglichen werden und gefundene Übereinstimmungen ausgegeben werden. Die keywords.txt-Datei befindet sich im gleichen Ordner wie das Skript. Außerdem sollen Daten extrahiert werden in verschiedenen Formaten (z.B. 10.08.2023, 10.8.2023, 10.08.23 etc.).

4. Wenn es Übereinstimmungen gibt, sollen diese mit dem Inhalt einer Datenbank im CSV-Format (daten.csv) abgeglichen werden. Die Datenbank beinhaltet die Keywords, Vorname, Nachname und Personalnummer

5. Die PNG-Datei soll umbenannt werden und den Dateinamen im Format „Nachname, Vorname.PNG“ erhalten. Nachname und Vorname werden aus der daten.csv entnommen

6. Anschließend soll die Datei in eine PDF-Datei konvertiert werden. Der Name soll von der PNG-Datei übernommen werden

7. Die erstellte PNG-Datei soll nach der Konvertierung wieder gelöscht werden. Ebenso die ursprüngliche PDF-Datei, so dass nur die neue PDF-Datei mir dem Namen "Nachname, Vorname.pdf" existiert.

Den Schritt von PDF-->PNG-->PDF gehe ich, da Google Vision nur Schrift in Bildern erkennt.

Ich hänge hier gerne mal ein Skript an, welches soweit funktioniert, aber nicht alle meine Bedingungen erfüllt (z.B. kein Drag&Drop und keine automatische Verarbeitung mehrer Dateien nacheinander):

Code: Alles auswählen

import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk
from google.cloud import vision
from pdf2image import convert_from_path
import os
import re
import csv

# Setzen Sie hier den Pfad zu Ihrer JSON-Anmeldeinformation ein
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "C:/Users/***/Desktop/HTR/GoogleCloudVision.json"

# Pfad zur Textdatei mit den Schlagwörtern
schlagwoerter_datei = "C:/Users/***/Desktop/HTR/keywords.txt"

# Pfad zur CSV-Datei mit den Datensätzen
datensatz_datei = "C:/Users/***/Desktop/HTR/daten.csv"

# Definieren Sie hier die Koordinaten für den begrenzten Bereich
x1, y1, x2, y2 = 0, 1000, 1654, 1500

# Erstellen Sie hier eine globale Variable für image_label
image_label = None

# Erstellen Sie hier eine globale Variable für result_text
result_text = None

def load_keywords():
    with open(schlagwoerter_datei, "r") as f:
        keywords = f.read().splitlines()
    return keywords

def extract_dates(text):
    date_pattern = r"\b(?:\d{1,2}\.\d{1,2}\.\d{2,4}|d{1,2}\.d{1,2}\.\d{2,4}|\d{1,2}\.\d{1,2}\.\d{2,4}|\d{1,2}\.\d{1,2}\.\d{2,4})\b"
    dates = re.findall(date_pattern, text)
    return dates

def load_dataset():
    dataset = {}
    with open(datensatz_datei, "r", newline="") as csvfile:
        reader = csv.DictReader(csvfile)
        for row in reader:
            keywords = row["Keyword"].split(",")
            dataset_keywords = [keyword.strip() for keyword in keywords]
            dataset[tuple(dataset_keywords)] = {
                "vorname": row["Vorname"],
                "nachname": row["Nachname"],
                "personalnummer": row["Personalnummer"]
            }
    return dataset

def rename_image(image_path, dataset):
    extracted_text = detect_handwritten_text(image_path)
    keywords = load_keywords()
    found_keywords = [keyword for keyword in keywords if keyword.lower() in extracted_text.lower()]
    
    extracted_dates = extract_dates(extracted_text)
    
    for keywords_tuple, data in dataset.items():
        if all(keyword.lower() in extracted_text.lower() for keyword in keywords_tuple):
            new_basename = f"{data['nachname']}, {data['vorname']}.png"
            dirname, _ = os.path.split(image_path)
            new_path = os.path.join(dirname, new_basename)
            os.rename(image_path, new_path)
            return new_path
    return None

def detect_handwritten_text(image_path):
    client = vision.ImageAnnotatorClient()

    with open(image_path, 'rb') as image_file:
        content = image_file.read()

    image = vision.Image(content=content)
    response = client.document_text_detection(image=image)

    texts = []
    for page in response.full_text_annotation.pages:
        for block in page.blocks:
            for paragraph in block.paragraphs:
                for word in paragraph.words:
                    word_text = ''.join(symbol.text for symbol in word.symbols)
                    texts.append(word_text)

    return ' '.join(texts)

def open_image():
    global image_label
    global result_text

    file_path = filedialog.askopenfilename()
    if file_path:
        # Check if the file is a PDF and convert it if necessary
        if file_path.lower().endswith('.pdf'):
            pdf_images = convert_from_path(file_path)
            if pdf_images:
                pdf_image = pdf_images[0]
                pdf_image_path = "C:/Users/RGoebel/Desktop/Scan/temporary_image.png"
                pdf_image.save(pdf_image_path, 'PNG')
                original_file_path = file_path
                file_path = pdf_image_path

        img = Image.open(file_path)
        img_cropped = img.crop((x1, y1, x2, y2))
        img_cropped.thumbnail((300, 300))
        img_tk = ImageTk.PhotoImage(img_cropped)
        image_label.config(image=img_tk)
        image_label.image = img_tk

        extracted_text = detect_handwritten_text(file_path)
        keywords = load_keywords()
        found_keywords = [keyword for keyword in keywords if keyword.lower() in extracted_text.lower()]

        extracted_dates = extract_dates(extracted_text)

        dataset = load_dataset()
        new_image_path = rename_image(file_path, dataset)

        result_text.delete("1.0", tk.END)
        result_text.insert(tk.END, "Found Keywords:\n" + ", ".join(found_keywords))
        result_text.insert(tk.END, "\n\nExtracted Dates:\n" + ", ".join(extracted_dates))
        if new_image_path:
            result_text.insert(tk.END, f"\n\nImage Renamed to:\n{new_image_path}")
        else:
            result_text.insert(tk.END, "\n\nMatching entry not found in dataset.")
        #result_text.insert(tk.END, "\n\nExtracted Text:\n" + "".join(extracted_text))  
        
        # Convert the newly created PNG image to PDF
        if new_image_path and new_image_path.lower().endswith('.png'):
            pdf_path = new_image_path.replace('.png', '.pdf')
            img = Image.open(new_image_path)
            img.save(pdf_path, 'PDF', resolution=100.0, save_all=True)
            result_text.insert(tk.END, f"\n\nPNG Image Converted to PDF:\n{pdf_path}")
            
        # Remove the PNG file
        os.remove(new_image_path)
        result_text.insert(tk.END, f"\n\nPNG Image Deleted:\n{new_image_path}")   

        os.remove(original_file_path)
        result_text.insert(tk.END, f"\n\nOriginal Input File Deleted:\n{original_file_path}")    

def main():
    global image_label  # Zugriff auf die globale Variable image_label
    global result_text  # Zugriff auf die globale Variable result_text
    
    root = tk.Tk()
    root.title("HTR to rename")
    
    image_label = tk.Label(root)
    image_label.pack(pady=10)
    
    load_button = tk.Button(root, text="Load File", command=open_image)
    load_button.pack(pady=5)
    
    result_text = tk.Text(root, height=15, width=50)
    result_text.pack(padx=10, pady=10)
    
    root.mainloop()

if __name__ == "__main__":
    main()
Die Anzeige des Vorschaubilder soll ebenfalls verschwinden, sodass nur ein GUI mit Drag&Drop-Fläche und Textausgabe existiert. In der Textausgabe sollen dann die gefundenen Keywords und der neue Name der .pdf-Datei inkl. Pfad erscheinen.
Der begrenzte Bereich wurde ursprünglich angegeben, um die Handschrifterkennung auf eben diesen Bereich zu beschränken. Aber Google Cloud Vision bearbeitet anscheinend immer das gesamte Bild.

Vielleicht ist ja jemand bereit mal über den Code zu schauen und mir ein paar detaillierte Tipps zu geben :)

Liebe Grüße
Fisatec
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn Deine Programmierkenntnisse gegen 0 gehen, dann kannst Du das entweder ändern, oder jemanden suchen, der Dir gegen Geld ein Programm nach Deinen Wünschen schreibt. Das Forum ist für Hilfe zur Selbsthilfe da, wenn Du also konkrete Fragen zu einem Problem hast, und sagen kannst, wo Du bei Deinen Versuchen nicht weiter kommst, können wir gerne weiterhelfen.
Es scheint wohl ein Projekt für drag%drop zu geben: https://github.com/Eliav2/tkinterdnd2

Globale Variablen darf es nicht geben, vergiss gleich wieder dass es das Schlüsselwort `global` überhaupt gibt. Alles was Funktionen brauchen, müssen sie auch über ihre Argumente bekommen.
Konstanten werden KOMPLETT_GROSS geschrieben.
In load_keywords fehlt bei open die Angabe eines Encodings. In extract_dates wird viermal das selbe Pattern wiederholt. Warum?
In load_dataset fehlt wieder die Angabe des Encodings. Warum wird dataset_keywords als Liste erzeugt, um es gleich danach in ein Tuple unzuwandeln?
In rename_image wird found_keywords definiert, aber gar nicht benutzt. Der `in`-Vergleich für Keywords in einem Text dürfte eh falsch sein, weil das ja auch Schlüssel innerhalb von Wörtern erkennt, die gar nichts mit dem Keyword zu tun haben. `extracted_dates` wird definiert, aber gar nicht benutzt. Es ist unsinnig, etliche mal den selben Text in Kleinbuchstaben zu verwandeln, das macht man nur einmal!
os.path ist veraltet, in neuem Code benutzt man pathlib.
In `open_image` wird `global` verwendet, ganz schlechte Idee. Warum wird `file_path` in `original_file_path´ umbenannt, was ja auf eine PDF-Datei verweist und dann `file_path` mit `pdf_image_path` überschrieben, was ja auf eine png-Datei verweist? Werden die ganzen if-Abfragen nicht erfüllt, dann zeigt file_path immer noch auf ein PDF und das Laden des Bildes schlägt fehl, oder original_file_path ist gar nicht definiert und das Löschen schlägt später fehl.
`load_keywords` rufst Du auch an mehreren Stellen mehrmals auf, statt es nur einmal zu tun und die keywords dann entsprechend weiterzureichen.
Warum speicherst Du ein Bild in eine Datei, um es gleich darauf wieder zu laden? Das Speichern und Laden kannst du Dir sparen. Und Du lädst es ja nicht nur einmal, sondern gleich dreimal!
Zusätzlich benennst Du das Bild um, um es gleich darauf unter dem neuen Namen zu löschen.
Wenn `new_image_path` None ist, wird trotzdem versucht, die Datei zu löschen.
Das ganze Programm besteht also aus lauter völlig unnötigen Aktionen, die den Code aufblähen und man sich ständig fragt, warum so kompliziert, wenn es auch einfach ginge?
Fisatec
User
Beiträge: 2
Registriert: Mittwoch 9. August 2023, 11:35

Hey Sirius3,
vielen Dank für deine schnelle Rückmeldung.
Vielleicht ist ja jemand bereit mal über den Code zu schauen und mir ein paar detaillierte Tipps zu geben :)
Ich erwarte natürlich nicht, dass mir jemand den gesamten Code vorkaut.
Da ich aber komplett planlos bin, brauche ich einige Ansätze um den Code insgesamt anzupassen und zu verbessern.
Du hast ja schon einige Sachen angesprochen, die ich mir Mal genau anschauen werde.

Beste Grüße
Fisatec
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Zum Betreff dieses Themas: Ist Han-Schrift eingentlich was chinesisches? Eine Schrift/Schreibweise aus der Han-Dynastie oder so? 😛
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten