• Englisch
  • Docs
  • Wissensdatenbank
  • F.A.Q
  • Forum
  • Medien
  • Kurse
  • Marktplatz
  • Login
  • Try for free
  • Englisch
  • Docs
  • Wissensdatenbank
  • F.A.Q
  • Forum
  • Medien
  • Kurse
  • Marktplatz
  • Login
  • Try for free
home/Wissensdatenbank/Anleitungen/Feinabstimmung und Ausführung benutzerdefinierter AI-Modelle in Simplifier

Feinabstimmung und Ausführung benutzerdefinierter AI-Modelle in Simplifier

Written by Jennifer Häfner
April 9, 2025

Wie kann ich meine lokalen AI-Modelle in Simplifier ausführen?

Die Nutzung von Cloud-basierten KI-Diensten über einen API-Aufruf hat sicherlich ihre Vorteile, aber einige Anwendungsfälle erfordern benutzerdefinierte Modelle, die auf dem Client ausgeführt werden, um eine bessere Leistung, mehr Datenschutz und Sicherheit sowie eine bessere Kostenkontrolle zu gewährleisten.

In diesem Artikel erfahren Sie, wie Sie

  • Feinabstimmung eines vorab trainierten Modells anhand Ihrer eigenen Daten,
  • exportieren Sie das feinabgestimmte Modell,
  • importieren Sie das feinabgestimmte Modell in Simplifier,
  • Führen Sie das fein abgestimmte Modell mit Ihren benutzerdefinierten Daten aus.

Der Prozess besteht aus drei Schritten: 1) Modelltraining (oder besser gesagt Feinabstimmung), 2) Modellexport und 3) Modellinferenz.

Wir trainieren/optimieren das Modell in Python, da dies die beliebteste Programmiersprache für maschinelles Lernen ist.
Wir führen das Modell in JavaScript unter Verwendung von Transformers.js aus, da dies die Programmiersprache ist, die für die Implementierung der Simplifier-App-Logik verwendet wird.

Um sicherzustellen, dass unser benutzerdefiniertes Modell in beiden Programmierumgebungen funktionieren kann, exportieren wir es im ONNX-Format. Modelle in diesem Format können in vielen verschiedenen Frameworks eingesetzt werden und profitieren auch vom Zugang zu Hardware-Optimierungen (dies wird jedoch in diesem Artikel nicht behandelt).

Anwendungsfall

In diesem Tutorial wollen wir eine App mit Bilderkennungsfunktionalität implementieren. Unser Ziel ist es, bestimmte Objekte im Videostream der Webcam in Echtzeit zu erkennen und ihre Position so genau wie möglich zu bestimmen, indem wir einen Begrenzungsrahmen um sie herum zeichnen.

Es gibt bereits vorgefertigte Modelle, die eine breite Palette von Alltagsgegenständen erkennen können. Das von Ultralytics vortrainierte Modell YOLO kann beispielsweise 80 verschiedene Klassen erkennen.

In vielen Fällen ist die Verwendung eines vorab trainierten Bilderkennungsmodells eine ausreichende Wahl. Wenn Sie dem Modell jedoch beibringen möchten, auch benutzerdefinierte Objekte zu erkennen (z. B. bestimmte Komponenten aus Ihrem Fertigungsprozess), ist eine Feinabstimmung des Modells auf Ihren benutzerdefinierten Datensatz erforderlich.

Voraussetzungen

Für die Feinabstimmung des Modells:

  1. Zugang zu einer Python-Entwicklungsumgebung
    Sie können einfach einen Jupyter-Notebook-Dienst nutzen, z. B. Google Colab
  2. Erzeugen Sie einen beschrifteten Datensatz für Ihren Anwendungsfall
    Sammeln Sie Bilder und beschriften Sie die Objekte, die Sie erkennen möchten, mit Bounding Boxes. Weitere Informationen über den Annotationsprozess und gängige Annotationswerkzeuge finden Sie hier: Data Collection and Annotation
    Um diesem Tutorial folgen zu können, sollten Ihre Daten im YOLO-Format gespeichert werden, d.h. alle Bilder werden in einem Ordner namens images gespeichert, und alle Annotationen werden als separate .txt-Datei in einem Ordner namens labels gespeichert, mit demselben Dateinamen wie das entsprechende Bild.
    Sie müssen noch keine .yaml-Datei bereitstellen oder die Daten in train/validation/test set aufteilen, da wir diese Schritte in diesem Tutorial behandeln werden.

Für die Modellinferenz:

  1. Erstellen Sie eine Simplifier-Anwendung, um Ihr benutzerdefiniertes Modell auszuführen
  2. Erstellen eines clientseitigen Business-Objekts (CBO)
  3. Laden Sie die Bibliothek Transformers.js in Ihre Simplifier-Instanz hoch
    Fügen Sie dazu den folgenden Code in den Abschnitt JS Code to include ein (siehe Screenshot):

    import ("https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.2.4").then((tf) => {
        window.transformers = tf;
    });

    Laden Sie dann eine leere, gezippte .js-Datei hoch (da Simplifier für jede benutzerdefinierte Bibliothek eine ZIP-Datei mit einer enthaltenen .js-Datei erwartet)

  4. Fügen Sie die Transformers.js-Bibliothek als Abhängigkeit zu Ihrem zuvor erstellten CBO hinzu

SCHRITT 1: Modelltraining/Feinabstimmung

Sprache: Python
Umgebung: Jupyter Notebook (ich verwende Google Colab in diesem Tutorial)

Wenn Sie bereits über ein benutzerdefiniertes Modell im ONNX-Format verfügen, können Sie das Training/die Feinabstimmung und den Exportschritt überspringen und direkt zu SCHRITT 3: Modellinferenz übergehen.

Wenn Sie ein vorab trainiertes Modell ohne Feinabstimmung verwenden möchten, können Sie den Schritt Training/Feinabstimmung überspringen und direkt zu SCHRITT 2: Modellexport gehen.

Der Schritt Training/Feinabstimmung erfordert die Verbindung mit einer GPU-Laufzeit in Ihrem Python-Notebook. Für den Export nach ONNX ist eine CPU-Laufzeit ausreichend.

In diesem Lehrgang wird ein von Ultralytics trainiertes YOLO-Modell mit Hilfe des Python-Pakets von Ultralytics feinabgestimmt. Dieses Paket bietet Funktionen zum Trainieren, Bewerten und Exportieren von YOLO-Modellen.

Vorverarbeitung von Daten

Zunächst laden wir unsere Daten (Bilder und Beschriftungen) und teilen sie in Trainings-, Validierungs- und Testsatz auf. Dann generieren wir eine yaml-Datei, die die Konfiguration für das Modelltraining enthält.

Installieren Sie das Paket ultralytics:

!pip install -q -U ultralytics

Laden Sie Ihren benutzerdefinierten Datensatz von Google Drive (oder Ihrer jeweiligen Datenquelle).
Die Daten werden im Google Drive-Ordner yolo gespeichert. Der Ordner enthält die Bilder und die Beschriftungen für jedes Bild.

from google.colab import drive

drive.mount('/content/drive')

Teilen Sie die Daten in Train/Eval/Test-Sets auf. Dies ist nur erforderlich, wenn die Daten noch nicht aufgeteilt sind:

import os
import numpy as np
from sklearn.model_selection import train_test_split

images = os.listdir('/content/drive/MyDrive/yolo/images')
labels = os.listdir('/content/drive/MyDrive/yolo/labels')

images.sort()
labels.sort()
images_with_labels = list(zip(images, labels))

print(f'Loaded {len(images_with_labels)} annotated images.')

# split all into train and test
data_train, data_test = train_test_split(images_with_labels, test_size=0.2, random_state=42)

# split test into eval and test
data_eval, data_test = train_test_split(data_test, test_size=0.5, random_state=42)

print(f'Train data: {len(data_train)} samples.')
print(f'Eval data: {len(data_eval)} samples.')
print(f'Test data: {len(data_test)} samples.')

Speichern Sie die Splits in separaten Ordnern:

import shutil
from tqdm.auto import tqdm

train_folder_path = '/content/drive/MyDrive/yolo/train/'
val_folder_path = '/content/drive/MyDrive/yolo/val'
test_folder_path = '/content/drive/MyDrive/yolo/test'

# create folders if they do not exist
if not os.path.isdir(train_folder_path):
    os.mkdir(train_folder_path)
if not os.path.isdir(val_folder_path):
    os.mkdir(val_folder_path)
if not os.path.isdir(test_folder_path):
    os.mkdir(test_folder_path)

print('Storing train data...')
for image_with_label in tqdm(data_train):
    shutil.copy(f'/content/drive/MyDrive/yolo/images/{image_with_label[0]}', f'{train_folder_path}')
    shutil.copy(f'/content/drive/MyDrive/yolo/labels/{image_with_label[1]}', f'{train_folder_path}')

print('Storing eval data...')
for image_with_label in tqdm(data_eval):
    shutil.copy(f'/content/drive/MyDrive/yolo/images/{image_with_label[0]}', f'{val_folder_path}')
    shutil.copy(f'/content/drive/MyDrive/yolo/labels/{image_with_label[1]}', f'{val_folder_path}')

print('Storing test data...')
for image_with_label in tqdm(data_test):
    shutil.copy(f'/content/drive/MyDrive/yolo/images/{image_with_label[0]}', f'{test_folder_path}')
    shutil.copy(f'/content/drive/MyDrive/yolo/labels/{image_with_label[1]}', f'{test_folder_path}')

print('Data splitting done.')

Erzeugen Sie eine yaml-Datei für die Zugkonfiguration und speichern Sie sie im Ordner yolo. Passen Sie die Klassen an, die Sie in der yaml-Datei erkennen möchten:

import yaml

yaml_content = {
    'path': '/content/drive/MyDrive/yolo',
    'train': 'train',
    'val': 'val',
    'nc': 1, # number of classes
    'names':
        {0: 'roll'} # adapt your classes here
    }
yaml_path = '/content/drive/MyDrive/yolo/train_model.yaml'

with open(yaml_path, 'w') as yaml_file:
    yaml.dump(yaml_content, yaml_file, default_flow_style=False)

Lastmodell und Feinabstimmung

Laden Sie ein vortrainiertes YOLO-Modell:

from ultralytics import YOLO

model = YOLO("yolo11s.pt")

Feinabstimmung des Modells mit der Konfiguration in der generierten yaml-Datei:

model.train(data="/content/drive/MyDrive/yolo/train_model.yaml", epochs=50, imgsz=640, plots=True) # 50 epochs should be enough

Um die Leistung des Modells zu überprüfen, können Sie die generierten Ergebnisdateien/Diagramme einsehen und das Modell auch mit einem Zufallsbild testen:

import os, random

test_folder_path = '/content/drive/MyDrive/yolo/test'
test_images = list(filter(lambda x: '.txt' not in x, os.listdir(test_folder_path)))

Erhalten Sie ein zufälliges Beispielbild:

random_index = random.randint(0, len(test_images))
print(f'random index: {random_index}')

sample_image = test_images[random_index]
sample_image_path = os.path.join(test_folder_path, sample_image)
print(f'sample image: {sample_image_path}')

Testen Sie die Objekterkennung:

result = model(sample_image_path)

# display the result
result[0].show()  # display to screen

SCHRITT 2: Modell-Export nach ONNX

Sprache: Python
Umgebung: Jupyter Notebook (ich verwende Google Colab in diesem Tutorial)

ONNX-Ausfuhr

Wir haben jetzt ein fein abgestimmtes YOLO-Modell, das unsere benutzerdefinierte Klasse erkennen kann. Um es in Simplifier auszuführen, müssen wir das Modell im ONNX-Format exportieren, damit es auch in einer JavaScript-Umgebung verwendet werden kann.

Das Python-Paket von Ultralytics bietet eine Funktion, die dies ermöglicht:

model.export(format="onnx", dynamic=True, simplify=True, nms=True, opset=19) # nms=True works for YOLO >= v11

Speichern Sie dann das exportierte ONNX-Modell auf Ihrem lokalen System.

Erstellung von Konfig-Dateien

Um das ONNX-Modell später mit Transformers.js zu laden, müssen Sie zwei zusätzliche Dateien mit Informationen über das Modell und die erkannten Klassen erstellen:

config.json

Erstellen Sie diese Datei von Hand. Sie enthält z.B. die Klassen des fein abgestimmten YOLO-Modells:

{
    "id2label": {
        "0": "roll"
    },
    "label2id": {
        "roll": 0
    },
    "model_type": "yolov11"
}
preprocessor_config.json

Diese Datei wurde von Hand erstellt. Sie enthält die Anweisungen, wie das Bild für das Modell bearbeitet werden muss, zum Beispiel:

{
    "do_normalize": false,
    "do_pad": false,
    "do_rescale": true,
    "do_resize": true,
    "feature_extractor_type": "ImageFeatureExtractor",
    "resample": 2,
    "rescale_factor": 0.00392156862745098,
    "size": {
        "width": 640,
        "height": 640
    }
}


		

SCHRITT 3: Modellinferenz (Ausführen des Modells)

Sprache: JavaScript
Umgebung: Vereinfacher (App und Client-seitiges Geschäftsobjekt)

Nachdem wir nun das trainierte Modell feinabgestimmt und im ONNX-Format exportiert haben, können wir es in Simplifier importieren und dort verwenden. Folgen Sie einfach diesen Schritten:

Importieren Sie das ONNX-Modell und die Konfigurationsdateien

Öffnen Sie die Simplifier-App, in der Sie Ihr Modell ausführen möchten. Laden Sie die ONNX-Modelldatei, die Datei config.json und die Datei preprocessor_config.json in den Abschnitt assets Ihrer App hoch.

Laden und Ausführen des Modells

Wechseln Sie nun zu dem CBO, das Sie als Voraussetzung erstellt haben, und erstellen Sie eine neue Funktion. Verwenden Sie den folgenden Code, um das Modell und den Datenpräprozessor mit den Dateien zu laden, die Sie im Asset-Bereich der App hochgeladen haben.

const sModelId = 'data'; // this is the assets folder where both .config and .onnx are stored
let oModel, oProcessor;

// init transformers.js environment variables
transformers.env.allowRemoteModels = false;
transformers.env.allowLocalModels = true;
transformers.env.localModelPath = '.';

// Load the fine-tuned model
oModel = await transformers.AutoModel.from_pretrained(sModelId, {
     model_file_name: 'YOLO_with_NMS', // adapt this to match your model name
     subfolder: '',
     dtype: 'fp32', // new transformers.js version (after 3.x) full-precision model: 'fp32', quantized model: 'int8'
});
sModelInputName = oModel.sessions.model.inputNames[0];
sModelOutputName = oModel.sessions.model.outputNames[0];
		
// Load the image processor
oProcessor = await transformers.AutoProcessor.from_pretrained(sModelId);

Erstellen Sie dann einen Schnappschuss des Webcam-Videostreams, verarbeiten Sie das Bild vor und lassen Sie das Modell auf den Bilddaten laufen.

// Read the current frame from the video
const oPixelData = oContext.getImageData(0, 0, width, height).data;
const oRawImage = new transformers.RawImage(oPixelData, width, height, 4);
	
// Process the image
const {
    pixel_values,
    reshaped_input_sizes
} = await oProcessor(oRawImage);

// Run the model
const aModelOutputs = await oModel({
    [sModelInputName]: pixel_values
});

Das Modell überprüft das Bild auf die Objektklassen, auf die es in Schritt 1 abgestimmt wurde, und gibt die Koordinaten der Begrenzungsrahmen um die erkannten Objekte zurück.

Vollständiges Code-Beispiel

Hier ist eine vollständige CBO-Funktion, die die Webcam startet, das benutzerdefinierte KI-Modell lädt und es auf Schnappschüssen des Webcam-Videostreams ausführt. Sie können diese CBO-Funktion in Ihrer App verwenden, z. B. ausgelöst durch das Ereignis onAfterRendering.

Fügen Sie auf dem Bildschirm Ihrer Anwendung das Widget ui_core_HTML hinzu. Fügen Sie für den Inhalt der Widget-Eigenschaft den folgenden HTML-Code hinzu:

<div id=”container”>
<video id=”video” autoplay=”autoplay” muted=”” width=”300″ height=”150″></video> <canvas id=”canvas” width=”360″ height=”240″></canvas>
<div id=”overlay”></div>
</div>

Hier ist der Code für die CBO-Funktion detectObject:

// Reference the elements that we will need
const oContainer = document.getElementById('container');
const oOverlay = document.getElementById('overlay');
const oCanvas = document.getElementById('canvas');
const oVideo = document.getElementById('video');

// if we detect only 1 class, we technically only need 1 colour
const COLOURS = [
    "#EF4444", "#4299E1", "#059669",
    "#FBBF24", "#4B52B1", "#7B3AC2",
    "#ED507A", "#1DD1A1", "#F3873A",
    "#4B5563", "#DC2626", "#1852B4",
    "#18A35D", "#F59E0B", "#4059BE",
    "#6027A5", "#D63D60", "#00AC9B",
    "#E64A19", "#272A34"
];
let bIsProcessing = false;
const oContext = oCanvas.getContext('2d', {
    willReadFrequently: true
});
const sModelId = 'data'; // this is the assets folder where both .config and .onnx are stored
let oModel, oProcessor;
let iThreshold = 0.25;

(async () => {
    try {
        // init transformers.js environment variables
        transformers.env.allowRemoteModels = false;
        transformers.env.allowLocalModels = true;
        transformers.env.localModelPath = '.';

        // Load the fine-tuned model
        oModel = await transformers.AutoModel.from_pretrained(sModelId, {
            model_file_name: 'YOLO_with_NMS', // adapt this to match your model name
            subfolder: '',
            dtype: 'fp32', // new transformers.js version (after 3.x) full-precision model: 'fp32', quantized model: 'int8'
        });
        sModelInputName = oModel.sessions.model.inputNames[0];
        sModelOutputName = oModel.sessions.model.outputNames[0];

        // Load the image processor
        oProcessor = await transformers.AutoProcessor.from_pretrained(sModelId);

        // Start the video stream
        navigator.mediaDevices.getUserMedia({
                video: {
                    facingMode: 'environment'
                }
            }, // Ask for video
        ).then((stream) => {
            // Set up the video and canvas elements.
            oVideo.srcObject = oStream;
            oVideo.play();

            const oVideoTrack = oStream.getVideoTracks()[0];
            const {
                width,
                height
            } = oVideoTrack.getSettings();

            oCanvas.width = width;
            oCanvas.height = height;

            // Set container width and height depending on the image aspect ratio
            const fAspectRatio = width / height;
            const [iContainerWidth, iContainerHeight] = (fAspectRatio > 720 / 405) ? [720, 720 / fAspectRatio] : [405 * fAspectRatio, 405];
            oContainer.style.width = `${iContainerWidth}px`;
            oContainer.style.height = `${iContainerHeight}px`;

            // Start the animation loop
            window.requestAnimationFrame(updateCanvas);

            fnSuccess({});
        });
    } catch (e) {
        fnError(e.message);
    }
})();

// Run processor and model on pixel data every frame
function updateCanvas() {
    const {
        width,
        height
    } = oCanvas;
    oContext.drawImage(oVideo, 0, 0, width, height);

    if (!bIsProcessing) {
        bIsProcessing = true;
        (async function() {
            try {
                // Read the current frame from the video
                const oPixelData = oContext.getImageData(0, 0, width, height).data;
                const oRawImage = new transformers.RawImage(oPixelData, width, height, 4);

                // Process the image
                const {
                    pixel_values,
                    reshaped_input_sizes
                } = await oProcessor(oRawImage);

                // Run the model
                const aModelOutputs = await oModel({
                    [sModelInputName]: pixel_values
                });

                // Update UI
                oOverlay.innerHTML = '';

                // Draw boxes on video
                const aSizes = reshaped_input_sizes[0].reverse();

                // Iterate over all detected boxes and check the box data
                aModelOutputs[sModelOutputName].tolist()[0].forEach(aBox => renderBox(aBox, aSizes));

                bIsProcessing = false;
            } catch (e) {
                fnError(e.message);
            }
        })();
    }
    window.requestAnimationFrame(updateCanvas);
}

// Render a bounding box and label on the image
function renderBox([iXmin, iYmin, iXmax, iYmax, fScore, iClassId], [iImageWidth, iImageHeight]) {
    try {
        if (fScore < fConfidenceThreshold) return; // Skip boxes with low confidence

        // Generate a random color for the box
        const sColour = COLOURS[iClassId % COLOURS.length];

        // Draw the box
        const oBoxElement = document.createElement('div');
        oBoxElement.className = 'bounding-box';
        Object.assign(oBoxElement.style, {
            borderColor: sColour,
            left: 100 * iXmin / iImageWidth + '%',
            top: 100 * iYmin / iImageHeight + '%',
            width: 100 * (iXmax - iXmin) / iImageWidth + '%',
            height: 100 * (iYmax - iYmin) / iImageHeight + '%',
        })

        // Draw label
        const oLabelElement = document.createElement('span');
        oLabelElement.textContent = `${oModel.config.id2label[iClassId]} (${(100 * fScore).toFixed(2)}%)`;
        oLabelElement.className = 'bounding-box-label';
        oLabelElement.style.backgroundColor = sColour;

        oBoxElement.appendChild(oLabelElement);
        oOverlay.appendChild(oBoxElement);
    } catch (e) {
        fnError(e.message);
    }
}

Das war’s!

Jetzt haben Sie gelernt, wie Sie AI-Modelle feinabstimmen, im ONNX-Format exportieren und in einer Simplifier-App mit Transformers.js ausführen können. Natürlich können Sie auch andere KI-bezogene JavaScript-Bibliotheken, wie ONNX Runtime JavaScript API, verwenden, um Ihre ONNX-Modelle auszuführen. Laden Sie einfach die ONNX-Modelle in den Asset-Bereich Ihrer Simplifier-App hoch und folgen Sie dann der Dokumentation der Bibliotheken, um den JavaScript-Code in Ihrer CBO-Funktion anzupassen.

Tags:aiModellimageErkennungZugfinetune

Was this article helpful?

Yes  No
Related Articles
  • Verwendung der Simplifier Teams App
  • Wie man wertvolle Hilfen und Vorschläge in Benutzereingaben integriert
  • So integrieren Sie Simplifier in CI/CD-Pipelines
  • So verwenden Sie Google reCAPTCHA v3 Essentials
  • So integrieren Sie SAP ERP (über SAP RFC)
  • Erstellen eines ChatBot-Clients für ChatGPT
Leave A Comment Antworten abbrechen

Du musst angemeldet sein, um einen Kommentar abzugeben.

Anleitungen
  • Feinabstimmung und Ausführung benutzerdefinierter AI-Modelle in Simplifier
  • So integrieren und verwenden Sie externe Bibliotheken in Simplifier Apps
  • Verwenden von Abfrageoptionen im ODataV2-Connector
  • So verwenden Sie das TimePicker-Widget
  • Dynamische where-Klausel und wiederholbare Anweisung
  • Versions- und Release-Management in Simplifier
  • Protokolle und Überwachung in Simplifier
  • Verwenden Sie die Adobe PDF-Einbettungs-API in Ihrer Simplifier-Anwendung
  • PDF-Vorlagen: Gestalten Sie den Inhalt der Kopf-/Fußzeile dynamisch
  • Konvertieren von Text in Sprache mit dem Google TTS Connector
  • So verwenden Sie das Simplifier Content Repository Plugin
  • Erstellen von Testfällen mit dem UI5 Test Recorder
  • Arbeiten mit Drag & Drop
  • Erstellen Sie eine PDF-Vorlage über das Plugin
  • Generieren eines PDFs mit Simplifier per Plugin
  • So setzen Sie Simplifier-Umgebungsvariablen in Open Shift
  • So senden und empfangen Sie Push-Benachrichtigungen
  • Volltextsuche nach PDF-Dokumenten
  • Volltextsuche nach Excel-Dateien
  • So implementieren Sie eine Genehmigungs-App in Simplifier
  • Authentifizierung über ein anderes Fenster
  • Erstellen eines ChatBot-Clients für ChatGPT
  • So verwenden Sie Google reCAPTCHA v3 Essentials
  • So integrieren Sie Simplifier in CI/CD-Pipelines
  • Wie man wertvolle Hilfen und Vorschläge in Benutzereingaben integriert
  • Verwendung der Simplifier Teams App
Knowledgebase Categories
  • Erste Schritte 4
  • Mobiler Client 2
  • Layout & Design 4
  • Widgets 8
  • Integration 10
  • Datenbank Handling 1
  • Anleitungen 26
  • Best Practices 3
  • Cloud Services 6
  • Plugins 6

Verwendung der Simplifier Teams App  

GetApp Bewertung

Benutzerbewertungen

Capterra

Benutzerbewertungen

Für AWS-Kunden

Für SAP-Kunden

  • Contact | Imprint | Privacy | © 2025 Simplifier AG. All Rights Reserved.

  • Englisch
  • Deutsch

Link einfügen/ändern

Gib die Ziel-URL ein

Oder verlinke auf bestehende Inhalte

    Es wurde kein Suchbegriff angegeben. Es werden die aktuellen Inhalte angezeigt. Verwende zur Auswahl eines Elements die Suche oder die Hoch-/Runter-Pfeiltasten.