import numpy as np
import os
import csv
import fitz # PyMuPDF
from tkinter import Tk, Canvas, Label, Button, filedialog, PhotoImage, messagebox
from PIL import Image
from PyPDF2 import PdfReader, PdfWriter
from objc import loadBundleFunctions, loadBundle
import Quartz
import CoreText
import Vision
import re
class PDFSplitterGUI:
def __init__(self, root):
self.root = root
self.root.title("PDF-Seiten teilen und Patienteninformationen auslesen")
self.pdf_path = None
self.output_dir = os.path.dirname(os.path.abspath(__file__)) # Projektordner
self.images = []
self.page_selection = []
self.patients = [] # Liste zum Speichern der Patientendaten
# Benutzeroberfläche
self.label = Label(root, text="PDF-Teiler mit OCR und Patientenabgleich")
self.label.pack(pady=10)
self.select_pdf_button = Button(root, text="1. PDF auswählen", command=self.select_pdf)
self.select_pdf_button.pack(pady=5)
self.load_button = Button(root, text="2. PDF laden und anzeigen", command=self.load_pdf, state="disabled")
self.load_button.pack(pady=5)
self.split_button = Button(root, text="3. PDF exportieren", command=self.export_pdf, state="disabled")
self.split_button.pack(pady=10)
self.ocr_button = Button(root, text="4. OCR auf ausgewählten Seiten", command=self.ocr_selected_pages, state="disabled")
self.ocr_button.pack(pady=10)
self.canvas = Canvas(root, width=800, height=600, bg="white")
self.canvas.pack()
# Patientendaten laden
self.load_patient_data()
def select_pdf(self):
self.pdf_path = filedialog.askopenfilename(filetypes=[("PDF-Dateien", "*.pdf")])
if self.pdf_path:
messagebox.showinfo("PDF ausgewählt", f"PDF-Datei ausgewählt:\n{self.pdf_path}")
self.load_button.config(state="normal")
def load_pdf(self):
if not self.pdf_path:
messagebox.showwarning("Keine PDF-Datei", "Bitte wählen Sie zuerst eine PDF-Datei aus.")
return
self.images = self.extract_thumbnails(self.pdf_path)
self.display_thumbnails()
def extract_thumbnails(self, pdf_path):
images = []
doc = fitz.open(pdf_path)
# Zielgröße der Thumbnails
thumbnail_width = 150
thumbnail_height = 200
for page_num in range(len(doc)):
page = doc[page_num]
mat = fitz.Matrix(thumbnail_width / page.rect.width, thumbnail_height / page.rect.height)
pix = page.get_pixmap(matrix=mat)
img = PhotoImage(data=pix.tobytes("ppm"))
images.append((page_num + 1, img))
return images
def display_thumbnails(self):
self.canvas.delete("all")
self.page_selection = [False] * len(self.images)
for i, (page_num, img) in enumerate(self.images):
x = (i % 4) * 200 + 10
y = (i // 4) * 250 + 10
image_id = self.canvas.create_image(x, y, anchor="nw", image=img)
text_id = self.canvas.create_text(x + 75, y + 220, text=f"Seite {page_num}", fill="black")
rect_id = self.canvas.create_rectangle(x, y, x + 150, y + 200, outline="blue", width=2)
self.canvas.tag_bind(image_id, "<Button-1>", lambda event, idx=i, rect=rect_id: self.toggle_selection(idx, rect))
self.canvas.tag_bind(text_id, "<Button-1>", lambda event, idx=i, rect=rect_id: self.toggle_selection(idx, rect))
self.canvas.tag_bind(rect_id, "<Button-1>", lambda event, idx=i, rect=rect_id: self.toggle_selection(idx, rect))
self.split_button.config(state="normal")
self.ocr_button.config(state="normal")
def toggle_selection(self, idx, rect_id):
self.page_selection[idx] = not self.page_selection[idx]
color = "green" if self.page_selection[idx] else "blue"
self.canvas.itemconfig(rect_id, outline=color)
def export_pdf(self):
selected_pages = [i for i, selected in enumerate(self.page_selection) if selected]
if not selected_pages:
messagebox.showwarning("Keine Seiten ausgewählt", "Bitte wählen Sie mindestens eine Seite aus.")
return
reader = PdfReader(self.pdf_path)
writer = PdfWriter()
for page_num in selected_pages:
writer.add_page(reader.pages[page_num])
output_file = os.path.join(self.output_dir, "Exportierte_Seiten.pdf")
with open(output_file, "wb") as f:
writer.write(f)
messagebox.showinfo("Export abgeschlossen", f"Das PDF wurde gespeichert unter:\n{output_file}")
def load_patient_data(self):
file_path = os.path.join(self.output_dir, "patients.csv")
try:
with open(file_path, "r", encoding="utf-8") as file:
reader = csv.DictReader(file, delimiter=";")
required_columns = {"patientid", "vorname", "nachname", "geburtsdatum"}
if not required_columns.issubset(reader.fieldnames):
raise ValueError("Die Datei 'patients.csv' enthält nicht alle erforderlichen Spalten.")
self.patients = [row for row in reader]
except FileNotFoundError:
messagebox.showwarning("Patientendaten fehlen", f"Die Datei '{file_path}' wurde nicht gefunden.")
self.patients = []
except ValueError as e:
messagebox.showerror("Ungültige CSV-Datei", str(e))
self.patients = []
def anonymize_patient_data(self, text):
"""
Ersetzt Patientendaten im Text durch Dummy-Werte.
- Nachname -> "Name"
- Vorname -> "Vorname"
- Geburtsdatum -> "01.01." + Geburtsjahr
- Wohnort und Straße -> Entfernen
"""
for patient in self.patients:
if patient["nachname"] in text:
text = text.replace(patient["nachname"], "Name")
if patient["vorname"] in text:
text = text.replace(patient["vorname"], "Vorname")
if patient["geburtsdatum"] in text:
geburtsjahr = patient["geburtsdatum"].split(".")[-1]
dummy_datum = f"01.01.{geburtsjahr}"
text = text.replace(patient["geburtsdatum"], dummy_datum)
wohnort = patient.get("wohnort", "")
if wohnort and wohnort in text:
text = text.replace(wohnort, "")
strasse = patient.get("strasse", "")
if strasse and strasse in text:
text = text.replace(strasse, "")
text = re.sub(r"\s{2,}", " ", text).strip()
return text
def find_patient_pid(self, text):
for patient in self.patients:
if (patient["nachname"] in text and
patient["vorname"] in text and
(patient["geburtsdatum"] in text or patient["geburtsdatum"].replace(".", ".20") in text)):
return patient["patientid"]
return None
def ocr_selected_pages(self):
selected_pages = [i for i, selected in enumerate(self.page_selection) if selected]
if not selected_pages:
messagebox.showwarning("Keine Seiten ausgewählt", "Bitte wählen Sie mindestens eine Seite aus.")
return
doc = fitz.open(self.pdf_path)
results = []
success_messages = []
for page_num in selected_pages:
pix = doc[page_num].get_pixmap(dpi=300)
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
# OCR mit Vision
ocr_result = self.perform_mac_ocr(img)
text = "\n".join(ocr_result)
# Anonymisiere Patientendaten
anonymized_text = self.anonymize_patient_data(text)
pid = self.find_patient_pid(text)
if pid:
for patient in self.patients:
if patient["patientid"] == pid:
success_messages.append(
f"Patient {patient['vorname']} {patient['nachname']}, geb. {patient['geburtsdatum']}, ID {pid}"
)
break
page_results = f"Seite {page_num + 1}:\n"
page_results += f"Patient-ID: {pid if pid else 'Nicht gefunden'}\n"
page_results += "OCR-Text:\n"
page_results += f"{anonymized_text}\n"
results.append(page_results)
self.save_ocr_results("\n".join(results))
success_text = "\n".join(success_messages) if success_messages else "Keine Patienten gefunden."
messagebox.showinfo("OCR abgeschlossen", f"Die Ergebnisse wurden gespeichert.\n\n{success_text}")
def perform_mac_ocr(self, image):
import Vision
from io import BytesIO
print(f"Bildgröße: {image.size}, Modus: {image.mode}")
image = image.convert("RGBA")
buffer = BytesIO()
image.save(buffer, format="PNG")
png_data = buffer.getvalue()
request = Vision.VNRecognizeTextRequest.alloc().initWithCompletionHandler_(
lambda req, err: None
)
request.setRecognitionLanguages_(["de", "en"])
handler = Vision.VNImageRequestHandler.alloc().initWithData_options_(
png_data, None
)
success, error = handler.performRequests_error_([request], None)
if not success:
raise ValueError(f"OCR konnte nicht ausgeführt werden: {error}")
results = request.results()
if not results:
return []
recognized_texts = []
for res in results:
if isinstance(res, Vision.VNRecognizedTextObservation):
top_candidate = res.topCandidates_(1)
if top_candidate:
recognized_texts.append(top_candidate[0].string())
return recognized_texts
def save_ocr_results(self, results):
output_file = os.path.join(self.output_dir, "ocr_results_with_pid.txt")
with open(output_file, "w", encoding="utf-8") as f:
f.write(results)
def run_pdf_splitter():
root = Tk()
app = PDFSplitterGUI(root)
root.mainloop()
if __name__ == "__main__":
run_pdf_splitter()