Prototyp, Analyse von mutmaßlichen Fakes (Papierform, Basis: Videos)

 import sys

import os

import cv2

import numpy as np

import tensorflow as tf

import pytesseract

from PyQt5.QtCore import QObject

from skimage.feature import graycomatrix, graycoprops

from tensorflow.keras.models import Sequential, load_model

from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, GlobalAveragePooling2D

from tensorflow.keras.preprocessing.image import ImageDataGenerator

from tensorflow.keras.applications import EfficientNetB0

from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QFileDialog, QLabel, QProgressBar, QMessageBox, QTextEdit

from PyQt5.QtCore import Qt, QThread, pyqtSignal

import logging


# Logging einrichten für Debugging und Fehlerverfolgung

logging.basicConfig(level=logging.INFO)


# Schritt 1: Extraktion von Frames aus einem Video

def extract_frames(video_path, frame_size=(256, 256)):

    """

    Extrahiert alle Frames aus einem Video und skaliert sie auf eine vorgegebene Größe.

    

    :param video_path: Pfad zur Videodatei

    :param frame_size: Die Zielgröße der extrahierten Frames

    :return: Liste von Frames (NumPy-Arrays)

    """

    video = cv2.VideoCapture(video_path)

    frames = []

    

    while True:

        ret, frame = video.read()

        if not ret:

            break

        frame_resized = cv2.resize(frame, frame_size)  # Bildgröße anpassen

        frames.append(frame_resized)

        

    video.release()

    return frames


# Schritt 2: Extraktion von Texturmerkmalen (Papierstruktur)

def extract_texture_features(image):

    """

    Extrahiert Texturmerkmale aus einem Bild, wie Kontrast, Homogenität, Energie und Korrelation,

    um die Papierstruktur zu analysieren.

    

    :param image: Eingabebild (NumPy-Array)

    :return: Ein Tupel mit den berechneten Texturmerkmalen

    """

    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # Bild in Graustufen umwandeln

    glcm = graycomatrix(gray_image, [1], [0, np.pi/4, np.pi/2, 3*np.pi/4], symmetric=True, normed=True)

    contrast = greycoprops(glcm, 'contrast')  # Berechnet den Kontrast

    homogeneity = greycoprops(glcm, 'homogeneity')  # Berechnet die Homogenität

    energy = greycoprops(glcm, 'energy')  # Berechnet die Energie

    correlation = greycoprops(glcm, 'correlation')  # Berechnet die Korrelation

    return contrast.mean(), homogeneity.mean(), energy.mean(), correlation.mean()


# Schritt 3: Erstellung des Deep Learning Modells

def create_model(input_shape=(256, 256, 3)):

    """

    Erstellt ein Convolutional Neural Network (CNN) Modell unter Verwendung von EfficientNetB0

    als Feature-Extractor und einer einfachen dichten Schicht zur Klassifikation.

    

    :param input_shape: Die Eingabebildgröße

    :return: Das erstellte Modell

    """

    base_model = EfficientNetB0(input_shape=input_shape, include_top=False, weights='imagenet')  # Vortrainiertes Modell verwenden

    base_model.trainable = False  # Das vortrainierte Modell einfrieren


    model = Sequential()

    model.add(base_model)  # Fügt das vortrainierte Modell hinzu

    model.add(GlobalAveragePooling2D())  # Globales Pooling, um Features zu aggregieren

    model.add(Dense(128, activation='relu'))  # Dichte Schicht

    model.add(Dropout(0.5))  # Dropout für Regularisierung

    model.add(Dense(1, activation='sigmoid'))  # Ausgabeschicht (binär: Echt oder Gefälscht)

    model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])  # Modell kompilieren

    return model


# Schritt 4: Modelltraining mit erweiterten Datenaugmentation

def train_model(model, train_images, train_labels, validation_images, validation_labels, epochs=20):

    """

    Trainiert das Modell mit erweiterten Datenaugmentation, um die Robustheit zu verbessern.

    

    :param model: Das zu trainierende Modell

    :param train_images: Trainingsbilder

    :param train_labels: Labels der Trainingsbilder

    :param validation_images: Validierungsbilder

    :param validation_labels: Labels der Validierungsbilder

    :param epochs: Anzahl der Epochen für das Training

    :return: Das Trainingsergebnis

    """

    datagen = ImageDataGenerator(

        rotation_range=40,  # Drehung der Bilder

        width_shift_range=0.3,  # Horizontale Verschiebung

        height_shift_range=0.3,  # Vertikale Verschiebung

        shear_range=0.3,  # Scherung

        zoom_range=0.3,  # Zoom

        horizontal_flip=True,  # Zufälliges horizontales Spiegeln

        fill_mode='nearest',  # Auffüllen der Bildränder

        brightness_range=[0.5, 1.5],  # Helligkeit variiert

        channel_shift_range=50.0,  # Farbraumverschiebung

    )

    

    datagen.fit(train_images)  # Trainierende Bilder vorbereiten


    # Modelltraining mit Validierung

    history = model.fit(

        datagen.flow(train_images, train_labels, batch_size=32),

        epochs=epochs,

        validation_data=(validation_images, validation_labels)

    )

    

    return history


# Schritt 5: Analyse des Videos mit Vorhersage

def analyze_video(video_path, model, frame_size=(256, 256)):

    """

    Führt eine Analyse auf jedem Frame eines Videos durch, um zu bestimmen, ob der Wahlzettel echt oder gefälscht ist.

    

    :param video_path: Pfad zum Video

    :param model: Das vortrainierte Deep Learning Modell

    :param frame_size: Die Größe der Frames

    :return: Eine Liste von Analyseergebnissen

    """

    frames = extract_frames(video_path, frame_size)

    

    results = []

    for idx, frame in enumerate(frames):

        frame_input = np.expand_dims(frame, axis=0) / 255.0  # Normalisieren

        prediction = model.predict(frame_input)

        result = {

            'frame': idx + 1,

            'prediction': 'Echt' if prediction[0] > 0.5 else 'Gefälscht',  # Entscheidung basierend auf dem Vorhersagewert

            'confidence': prediction[0] if prediction[0] > 0.5 else 1 - prediction[0]

        }

        results.append(result)

    

    return results


# GUI Klasse zur Interaktion mit dem Benutzer

class WahlzettelApp(QWidget):

    def __init__(self):

        super().__init__()


        self.setWindowTitle("Wahlzettel Erkennung")  # Fenster-Titel

        self.setGeometry(100, 100, 600, 500)  # Fenstergröße und Position

        

        self.model = None  # Initialisiert das Modell als None

        self.train_images = []  # Initialisiert die Liste der Trainingsbilder

        self.train_labels = []  # Initialisiert die Liste der Labels

        self.analysis_results = []  # Initialisiert die Liste der Analyseergebnisse


        self.init_ui()  # GUI initialisieren


    def init_ui(self):

        """Erstellt das GUI-Layout mit verschiedenen Buttons und Anzeigen"""

        layout = QVBoxLayout()


        self.label = QLabel("Wählen Sie Trainingsbilder oder ein Video", self)

        self.label.setAlignment(Qt.AlignCenter)

        layout.addWidget(self.label)


        # Button für das Hinzufügen von Trainingsbildern

        self.train_button = QPushButton("Trainingsbilder hinzufügen", self)

        self.train_button.clicked.connect(self.load_train_images)

        layout.addWidget(self.train_button)


        # Button für das Starten des Trainings

        self.train_model_button = QPushButton("Modell trainieren", self)

        self.train_model_button.clicked.connect(self.train_model)

        layout.addWidget(self.train_model_button)


        # Button für das Speichern des Modells

        self.save_model_button = QPushButton("Modell speichern", self)

        self.save_model_button.clicked.connect(self.save_model)

        layout.addWidget(self.save_model_button)


        # Button für das Laden des Modells

        self.load_model_button = QPushButton("Modell laden", self)

        self.load_model_button.clicked.connect(self.load_model)

        layout.addWidget(self.load_model_button)


        # Button für die Videoanalyse

        self.analyze_video_button = QPushButton("Video analysieren", self)

        self.analyze_video_button.clicked.connect(self.analyze_video)

        layout.addWidget(self.analyze_video_button)


        # Textfeld für die Anzeige der Analyseergebnisse

        self.results_text = QTextEdit(self)

        self.results_text.setReadOnly(True)

        layout.addWidget(self.results_text)


        # Fortschrittsanzeige

        self.progress_bar = QProgressBar(self)

        layout.addWidget(self.progress_bar)


        self.setLayout(layout)


    def load_train_images(self):

        """Lädt Trainingsbilder und deren Labels"""

        file_paths, _ = QFileDialog.getOpenFileNames(self, "Wählen Sie Trainingsbilder", "", "Bilder (*.png *.jpg *.jpeg)")

        

        if file_paths:

            for file_path in file_paths:

                img = cv2.imread(file_path)

                img_resized = cv2.resize(img, (256, 256))  # Bildgröße anpassen

                self.train_images.append(img_resized)

                label = 1 if 'echt' in file_path else 0  # Label basierend auf Dateiname (z. B. "echt" oder "gefaelscht")

                self.train_labels.append(label)


            self.train_images = np.array(self.train_images) / 255.0  # Normalisierung

            self.train_labels = np.array(self.train_labels)


            self.label.setText(f"{len(self.train_images)} Trainingsbilder geladen")


    def train_model(self):

        """Startet das Modelltraining im Hintergrund"""

        if not self.train_images:

            QMessageBox.warning(self, "Fehler", "Keine Trainingsbilder geladen!")

            return

        

        self.progress_bar.setValue(0)

        self.label.setText("Training startet...")


        # Modell erstellen

        self.model = create_model()

        

        # Startet das Training in einem Hintergrund-Thread

        self.thread = QThread()

        self.worker = TrainWorker(self.model, self.train_images, self.train_labels)

        self.worker.moveToThread(self.thread)

        self.worker.finished.connect(self.on_training_finished)

        self.worker.progress.connect(self.update_progress)

        self.thread.started.connect(self.worker.run)

        self.thread.start()


    def update_progress(self, value):

        """Aktualisiert den Fortschritt der Anzeige"""

        self.progress_bar.setValue(value)


    def on_training_finished(self):

        """Benachrichtigt, wenn das Training abgeschlossen ist"""

        self.label.setText("Training abgeschlossen!")

        self.progress_bar.setValue(100)


    def save_model(self):

        """Speichert das Modell auf der Festplatte"""

        if self.model:

            file_path, _ = QFileDialog.getSaveFileName(self, "Speichern Sie das Modell", "", "Modelle (*.h5)")

            if file_path:

                self.model.save(file_path)

                QMessageBox.information(self, "Erfolg", "Modell erfolgreich gespeichert!")

        else:

            QMessageBox.warning(self, "Fehler", "Kein Modell zum Speichern vorhanden!")


    def load_model(self):

        """Lädt ein Modell von der Festplatte"""

        file_path, _ = QFileDialog.getOpenFileName(self, "Wählen Sie das Modell", "", "Modelle (*.h5)")

        if file_path:

            self.model = load_model(file_path)

            QMessageBox.information(self, "Erfolg", "Modell erfolgreich geladen!")


    def analyze_video(self):

        """Startet die Videoanalyse"""

        if self.model is None:

            QMessageBox.warning(self, "Fehler", "Das Modell muss zuerst trainiert oder geladen werden!")

            return


        video_path, _ = QFileDialog.getOpenFileName(self, "Wählen Sie ein Video", "", "Videos (*.mp4 *.avi)")

        

        if video_path:

            self.progress_bar.setValue(0)

            self.label.setText("Videoanalyse startet...")


            # Startet die Analyse im Hintergrund

            self.thread = QThread()

            self.worker = AnalyzeVideoWorker(self.model, video_path)

            self.worker.moveToThread(self.thread)

            self.worker.finished.connect(self.on_video_analysis_finished)

            self.worker.progress.connect(self.update_progress)

            self.thread.started.connect(self.worker.run)

            self.thread.start()


    def on_video_analysis_finished(self):

        """Benachrichtigt, wenn die Videoanalyse abgeschlossen ist"""

        self.label.setText("Videoanalyse abgeschlossen!")

        self.progress_bar.setValue(100)

        self.display_video_analysis_results()


    def display_video_analysis_results(self):

        """Zeigt die Ergebnisse der Videoanalyse im Textfeld an"""

        if self.analysis_results:

            results_text = ""

            for result in self.analysis_results:

                results_text += f"Frame {result['frame']}: {result['prediction']} (Confidence: {result['confidence']:.2f})\n"

            self.results_text.setText(results_text)

        else:

            self.results_text.setText("Keine Analyseergebnisse vorhanden.")


class TrainWorker(QObject):

    finished = pyqtSignal()

    progress = pyqtSignal(int)


    def __init__(self, model, train_images, train_labels):

        super().__init__()

        self.model = model

        self.train_images = train_images

        self.train_labels = train_labels


    def run(self):

        """Trainiert das Modell"""

        history = train_model(self.model, self.train_images, self.train_labels, self.train_images, self.train_labels)

        self.finished.emit()


class AnalyzeVideoWorker(QObject):

    finished = pyqtSignal()

    progress = pyqtSignal(int)


    def __init__(self, model, video_path):

        super().__init__()

        self.model = model

        self.video_path = video_path


    def run(self):

        """Analysiert das Video"""

        self.analysis_results = analyze_video(self.video_path, self.model)

        self.finished.emit()


if __name__ == "__main__":

    app = QApplication(sys.argv)

    window = WahlzettelApp()

    window.show()

    sys.exit(app.exec_())


Kommentare