Projektaufbau – Entrauschungs-KI: Unterschied zwischen den Versionen

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen
Adrian.klinspon@stud.hshl.de (Diskussion | Beiträge)
Keine Bearbeitungszusammenfassung
Adrian.klinspon@stud.hshl.de (Diskussion | Beiträge)
Keine Bearbeitungszusammenfassung
Zeile 9: Zeile 9:


= Training (<code>DnCnnTrain.m</code>)=
= Training (<code>DnCnnTrain.m</code>)=
Trainiert das neuronale Netz mit Bildern aus dem Ordner <code>./BSDS500</code>.
Trainiert das neuronale Netz mit Bildern aus dem Ordner <code>./ImagesForTraining</code>. Dieser Ordner enthält die Trainingsdaten, zusammengestellt aus den Datensätzen BSDS500 und DIV2K.


== Funktionsweise ==
== Funktionsweise ==
Im Gegensatz zu einfachen Ansätzen lädt dieses Skript nicht ganze Bilder, sondern zerlegt diese in kleine Ausschnitte (Patches), um den GPU-Speicher effizient zu nutzen und die Trainingsmenge zu erhöhen.
Das Skript prüft zunächst, ob bereits generierte Bild-Patches existieren und fragt den Nutzer via Dialog, ob diese neu erstellt werden sollen oder ob ein bereits existierendes KI-Modell weiter trainiert werden soll.
 
* '''Daten-Verarbeitung'''
* '''Patch-Extraktion:''' Bilder werden in 50x50 Pixel große Quadrate zerlegt (Stride: 30px).
** '''Quellen:''' Bilder werden aus <code>./ImagesForTraining</code> geladen.
* '''Speicherung:''' Patches werden physisch im Ordner <code>./PatchesCreatedByThisScriptInOrderToProperlyTrainTheAI</code> abgelegt.
** '''Filterung:''' Nur RGB-Bilder, die größer als die Patches sind, werden verarbeitet.
* '''Architektur:''' Ein 15-Layer tiefes CNN mit Residual Learning (Regression Layer am Ende).
** '''Patch-Extraktion:''' Bilder werden in 50x50 Pixel große Quadrate zerlegt (Stride: 30px).
** '''Speicherung:''' Patches werden physisch im Ordner <code>./PatchesCreatedByThisScriptInOrderToProperlyTrainTheAI</code> abgelegt.
* '''Datensatz-Split:''' Die erstellten Patches werden im Verhältnis 90:10 in Trainings- und Validierungsdaten unterteilt (<code>trainSplit = 0.9</code>)
* '''Noise-Augmentation:''' Während des Trainings wird dynamisch Rauschen mittels einer Helper-Funktion (<code>addNoise</code>) hinzugefügt.
* '''Architektur:''' Ein 15-Layer tiefes CNN mit Residual Learning.
* '''Optimiser:''' Verwendet ADAM mit dynamischer Anpassung der Lernrate (Piecewise Drop).
* '''Optimiser:''' Verwendet ADAM mit dynamischer Anpassung der Lernrate (Piecewise Drop).
* '''Training-Modi:''' Über einen Dialog kann gewählt werden zwischen:
# '''Train new AI:''' Startet mit einer Lernrate von <code>1e-3</code>.
# '''Train old AI:''' Lädt trainierte AI und setzt das Training mit einer feineren Lernrate von <code>1e-4</code> fort.


== Code-Auszug ==
== Code-Auszug ==
<syntaxhighlight lang="matlab">% Konfiguration
<syntaxhighlight lang="matlab">% --- Pfade & Grundkonfiguration ---
patchSize = 50;
pathToImages = './ImagesForTraining';
networkDepth = 15;
patchDirectory = './PatchesCreatedByThisScriptInOrderToProperlyTrainTheAI';
numFilters = 64;
pathToSavedAI  = './KIs/AI.mat';      


% GPU-Kompatibilität erzwingen
patchSize = 50;     
stride = 30;         
trainSplit = 0.9;    % 90% Training, 10% Validierung
maxPatchesPerImage = 1500;
 
% GPU aktivieren
parallel.gpu.enableCUDAForwardCompatibility(true);
parallel.gpu.enableCUDAForwardCompatibility(true);


% Netzwerk-Architektur (Auszug)
% --- Benutzer-Dialog 1: Patches erstellen? ---
layers = [
areNewPatchesNeeded = false;
    imageInputLayer([patchSize patchSize channels], "Normalization", "none")
    convolution2dLayer(3, numFilters, 'Padding', 'same')
    batchNormalizationLayer
    reluLayer
];


% Dynamischer Aufbau der mittleren Layer
if ~isempty(dir(fullfile(patchDirectory, '*.png')))
for i = 2:(networkDepth-1)
     recreatePatches = questdlg('Patches already Exist. Do you want do Delete them and Create new ones?', ...
     layers = [
         'Create new Patches?', 'Yes', 'No', 'Yes');
         layers
 
        convolution2dLayer(3, numFilters, 'Padding', 'same')
    if strcmp(recreatePatches, 'Yes')
         batchNormalizationLayer
         areNewPatchesNeeded = true;
         reluLayer
         delete(fullfile(patchDirectory, '*.png')); % Löscht alte Patches
     ];
     end
else
    areNewPatchesNeeded = true;
end
end


% Abschluss (Residual Learning)
% (Hier folgt im Skript der Code zur Erstellung der Patches, falls areNewPatchesNeeded == true)
layers = [
 
    layers
% --- Datastore & Split Logik ---
    convolution2dLayer(3, channels, "Padding","same")
% Lädt alle Patches
    batchNormalizationLayer
allPatches = imageDatastore(patchDirectory, 'ReadFcn', @(f)im2double(imread(f)));
    regressionLayer
 
];
% Zufällige Aufteilung in Training (90%) und Validierung (10%)
numFiles = numel(allPatches.Files);
idx = randperm(numFiles); % Erzeugt zufällige Indizes
trainFiles = allPatches.Files(idx(1:round(trainSplit * numFiles)));
valFiles = allPatches.Files(idx(round(trainSplit * numFiles) + 1:end));
 
% Datastores mit Rauschen (addNoise Funktion)
dsTrain = transform(imageDatastore(trainFiles), @(x)addNoise(x, useResidual));
dsVal  = transform(imageDatastore(valFiles),  @(x)addNoise(x, useResidual));


% Training starten
% --- Benutzer-Dialog 2: Training Modus ---
trainNewOrOldAI = questdlg('Would you like to...', 'Training Options', 'Train new AI', 'Train old AI', 'Train new AI');
initialLRate = 1e-3; % Standard Lernrate
 
if strcmp(trainNewOrOldAI, 'Train old AI')
    if exist(pathToSavedAI, 'file')
        loadNet = load(pathToSavedAI);
        lGraph = layerGraph(loadNet.net);
        initialLRate = 1e-4; % Feinere Lernrate für Nachtraining
    else
        trainNewOrOldAI = 'Train new AI'; % Fallback
    end
end
 
% --- Training Starten ---
options = trainingOptions("adam", ...
options = trainingOptions("adam", ...
'MaxEpochs', 70, ...
    'MaxEpochs', 70, ...
'ExecutionEnvironment', 'gpu', ...
    'MiniBatchSize', 128, ...
);
    'InitialLearnRate', initialLRate, ...
net = trainNetwork(dsTrain, lgraph, options);</syntaxhighlight>
    'LearnRateSchedule', 'piecewise', ...
    'ValidationData', dsVal, ...
    'CheckpointPath', './Checkpoints/LAB29-06', ...
    'OutputNetwork', 'best-validation-loss');
 
net = trainNetwork(dsTrain, lgraph, options);
save(pathToSavedAI, 'net');</syntaxhighlight>


= Evaluierung (<code>DnCnnEval.m</code>) =
= Evaluierung (<code>DnCnnEval.m</code>) =

Version vom 15. Dezember 2025, 16:14 Uhr

Aufbau

Dieses Projekt implementiert ein Denoising Convolutional Neural Network (DnCNN) in MATLAB unter Verwendung von Residual Learning. Es dient dazu, Bildrauschen aus digitalen Bildern zu entfernen. Im Gegensatz zu klassischen Ansätzen lernt das Netz hierbei das Rauschen selbst (Residual) und subtrahiert es vom Eingangsbild.

Technische Voraussetzungen

MATLAB mit folgenden Toolboxen:

  • Deep Learning Toolbox
  • Image Processing Toolbox
  • Parallel Computing Toolbox

Training (DnCnnTrain.m)

Trainiert das neuronale Netz mit Bildern aus dem Ordner ./ImagesForTraining. Dieser Ordner enthält die Trainingsdaten, zusammengestellt aus den Datensätzen BSDS500 und DIV2K.

Funktionsweise

Das Skript prüft zunächst, ob bereits generierte Bild-Patches existieren und fragt den Nutzer via Dialog, ob diese neu erstellt werden sollen oder ob ein bereits existierendes KI-Modell weiter trainiert werden soll.

  • Daten-Verarbeitung
    • Quellen: Bilder werden aus ./ImagesForTraining geladen.
    • Filterung: Nur RGB-Bilder, die größer als die Patches sind, werden verarbeitet.
    • Patch-Extraktion: Bilder werden in 50x50 Pixel große Quadrate zerlegt (Stride: 30px).
    • Speicherung: Patches werden physisch im Ordner ./PatchesCreatedByThisScriptInOrderToProperlyTrainTheAI abgelegt.
  • Datensatz-Split: Die erstellten Patches werden im Verhältnis 90:10 in Trainings- und Validierungsdaten unterteilt (trainSplit = 0.9)
  • Noise-Augmentation: Während des Trainings wird dynamisch Rauschen mittels einer Helper-Funktion (addNoise) hinzugefügt.
  • Architektur: Ein 15-Layer tiefes CNN mit Residual Learning.
  • Optimiser: Verwendet ADAM mit dynamischer Anpassung der Lernrate (Piecewise Drop).
  • Training-Modi: Über einen Dialog kann gewählt werden zwischen:
  1. Train new AI: Startet mit einer Lernrate von 1e-3.
  2. Train old AI: Lädt trainierte AI und setzt das Training mit einer feineren Lernrate von 1e-4 fort.

Code-Auszug

% --- Pfade & Grundkonfiguration ---
pathToImages = './ImagesForTraining';  
patchDirectory = './PatchesCreatedByThisScriptInOrderToProperlyTrainTheAI';
pathToSavedAI  = './KIs/AI.mat';       

patchSize = 50;       
stride = 30;          
trainSplit = 0.9;     % 90% Training, 10% Validierung
maxPatchesPerImage = 1500; 

% GPU aktivieren
parallel.gpu.enableCUDAForwardCompatibility(true);

% --- Benutzer-Dialog 1: Patches erstellen? ---
areNewPatchesNeeded = false;

if ~isempty(dir(fullfile(patchDirectory, '*.png')))
    recreatePatches = questdlg('Patches already Exist. Do you want do Delete them and Create new ones?', ...
        'Create new Patches?', 'Yes', 'No', 'Yes');

    if strcmp(recreatePatches, 'Yes')
        areNewPatchesNeeded = true;
        delete(fullfile(patchDirectory, '*.png')); % Löscht alte Patches
    end
else
    areNewPatchesNeeded = true;
end

% (Hier folgt im Skript der Code zur Erstellung der Patches, falls areNewPatchesNeeded == true)

% --- Datastore & Split Logik ---
% Lädt alle Patches
allPatches = imageDatastore(patchDirectory, 'ReadFcn', @(f)im2double(imread(f)));

% Zufällige Aufteilung in Training (90%) und Validierung (10%)
numFiles = numel(allPatches.Files);
idx = randperm(numFiles); % Erzeugt zufällige Indizes
trainFiles = allPatches.Files(idx(1:round(trainSplit * numFiles)));
valFiles = allPatches.Files(idx(round(trainSplit * numFiles) + 1:end));

% Datastores mit Rauschen (addNoise Funktion)
dsTrain = transform(imageDatastore(trainFiles), @(x)addNoise(x, useResidual));
dsVal   = transform(imageDatastore(valFiles),   @(x)addNoise(x, useResidual));

% --- Benutzer-Dialog 2: Training Modus ---
trainNewOrOldAI = questdlg('Would you like to...', 'Training Options', 'Train new AI', 'Train old AI', 'Train new AI');
initialLRate = 1e-3; % Standard Lernrate

if strcmp(trainNewOrOldAI, 'Train old AI')
    if exist(pathToSavedAI, 'file')
        loadNet = load(pathToSavedAI);
        lGraph = layerGraph(loadNet.net);
        initialLRate = 1e-4; % Feinere Lernrate für Nachtraining
    else
        trainNewOrOldAI = 'Train new AI'; % Fallback
    end
end

% --- Training Starten ---
options = trainingOptions("adam", ...
    'MaxEpochs', 70, ...
    'MiniBatchSize', 128, ...
    'InitialLearnRate', initialLRate, ...
    'LearnRateSchedule', 'piecewise', ...
    'ValidationData', dsVal, ...
    'CheckpointPath', './Checkpoints/LAB29-06', ...
    'OutputNetwork', 'best-validation-loss');

net = trainNetwork(dsTrain, lgraph, options);
save(pathToSavedAI, 'net');

Evaluierung (DnCnnEval.m)

Wendet das trainierte Modell auf neue Bilder im Ordner ./Testbilder an.

Funktionsweise

  • Preprocessing: Lädt Bilder und konvertiert Graustufenbilder künstlich in 3-Kanal-Bilder (RGB), um die Kompatibilität mit dem Netz zu gewährleisten.
  • Rauschen: Falls das Bild als "sauber" markiert ist, fügt das Skript automatisch Gaußsches Rauschen hinzu.
  • Visualisierung: Erstellt eine "Input vs. Output"-Collage.

Code-Auszug

% Modell laden
if ~exist('net', 'var')
    data = load("Path/AI_InitialLearnRate_1e-3.mat");
    net = data.net;
end

% Rauschen hinzufügen (falls nötig)
if ~isImageNoisy
    imgDouble = imnoise(imgDouble, "gaussian", 0, 0.03);
end

% Entrauschen
imgRestored = denoiseImages(net, imgDouble, 50, 50/2, true);

% Collage erstellen (Input | Separator | Output)
separatorWidth = 20;
separator = ones(h, separatorWidth, 3);
collage = [imgDouble, separator, imgRestored];

% Beschriftung einfügen
textLabels = {'Input', 'Output'};
positions = [10, 10; (w + separatorWidth + 10), 10];

collage = insertText(collage, positions, textLabels, ...
    'FontSize', max(20, round(h/25)), ...
    'BoxColor', 'white', ...
    'BoxOpacity', 0.7);

imwrite(collage, saveName);

Rausch-Generator Tool (DnCNNNoise.m)

Ein Hilfsskript, um einen kompletten Ordner von Bildern permanent zu verrauschen, falls Testdaten benötigt werden.

Funktionsweise & Code

Nutzt eine grafische Oberfläche (GUI) zur Ordnerauswahl und die Image Processing Toolbox (imnoise), um mathematisch exaktes Rauschen hinzuzufügen.

% User Interface für Auswahl
noiseChoice = questdlg('Which noise for ALL images?', 'Noise Type', ...
    'Salt & Pepper', 'Gaussian', 'Speckle', 'Gaussian');

% Rauschen anwenden
switch noiseChoice
    case 'Salt & Pepper'
        % Fügt "Salz und Pfeffer"-Rauschen hinzu (Dichte: 0.05)
        imgNoisy = imnoise(img, 'salt & pepper', 0.05);
    case 'Gaussian'
        % Fügt Gaußsches Rauschen hinzu (Mittelwert 0, Varianz 0.01)
        imgNoisy = imnoise(img, 'gaussian', 0, 0.01);
    case 'Speckle'
        % Fügt multiplikatives Rauschen hinzu (Varianz 0.04)
        imgNoisy = imnoise(img, 'speckle', 0.04);
end

Quellen

  • P. Arbeláez, M. Maire, C. Fowlkes and J. Malik, "Contour Detection and Hierarchical Image Segmentation," in IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 33, no. 5, pp. 898-916, May 2011, doi: 10.1109/TPAMI.2010.161. keywords: {Image segmentation;Pixel;Detectors;Image edge detection;Humans;Histograms;Benchmark testing;Contour detection;image segmentation;computer vision.}
  • R. Timofte, S. Gu, J. Wu, L. Van Gool, L. Zhang, M.-H. Yang, M. Haris et al., "NTIRE 2018 Challenge on Single Image Super-Resolution: Methods and Results," in The IEEE Conference on Computer Vision and Pattern Recognition (CVPR) Workshops, June 2018.