step three
parent
065f3d2bd1
commit
eb15f88b6e
@ -0,0 +1,228 @@
|
|||||||
|
% 2023-01-15
|
||||||
|
|
||||||
|
% based on the script for task one, this is rcnnscript for direct
|
||||||
|
% classification
|
||||||
|
|
||||||
|
% Requirements:
|
||||||
|
% data has to be provided as PicturesResizedLabelsResizedSignsCutted.zip
|
||||||
|
% in script location
|
||||||
|
% (this script unzips data and renames two files, but there is unlabeled
|
||||||
|
% image-material, that has to be removed by hand after unzipping)
|
||||||
|
|
||||||
|
% additional scriptfiles:
|
||||||
|
% - func_setupData.m
|
||||||
|
% unpack data etc.
|
||||||
|
% - func_groundTruthFromLabelPic.m
|
||||||
|
% (generate groundtruthtablle from image-data)
|
||||||
|
% - augmentData.m
|
||||||
|
% (dataaugmentation für RCNN)
|
||||||
|
% - helperSanitizeBoxes.m
|
||||||
|
% (part of augmentation)
|
||||||
|
% - preprocessData.m
|
||||||
|
% (Resize image and bounding boxes to targetSize.
|
||||||
|
%
|
||||||
|
|
||||||
|
% required add-on(s):
|
||||||
|
% - 'Deep Learning Toolbox Model for ResNet-50 Network'
|
||||||
|
% - 'image processing toolbox'
|
||||||
|
% - 'Computer Vision Toolbox '
|
||||||
|
|
||||||
|
% recommended add-on(s) - if gpu is apt for the job....
|
||||||
|
% - 'Parallel Computing Toolbox'
|
||||||
|
|
||||||
|
% adjustable parameters
|
||||||
|
% - if there is no trained net, it can be trained with this script:
|
||||||
|
% set doTraining to true
|
||||||
|
% - the training can use augmentation or not:
|
||||||
|
% set doAugmentation accordingly
|
||||||
|
|
||||||
|
close all;
|
||||||
|
clear;
|
||||||
|
|
||||||
|
doTraining = true;
|
||||||
|
|
||||||
|
% first we need the data...
|
||||||
|
dataDir = 'Picturedata'; % Destination-Folder for provided (img) Data
|
||||||
|
zippedDataFile = 'PicturesResizedLabelsResizedSignsCutted.zip'; %Data provided by TA
|
||||||
|
grDataFile = 'signDatasetGroundTruth_stepthree.mat';
|
||||||
|
func_setupData_stepthree(dataDir, zippedDataFile, grDataFile);
|
||||||
|
|
||||||
|
%load data
|
||||||
|
data = load(grDataFile);
|
||||||
|
traficSignDataset = data.DataSet;
|
||||||
|
|
||||||
|
% ----- split the dataset into training, validation, and test sets.
|
||||||
|
% Select 60% of the data for training, 10% for validation, and the
|
||||||
|
% rest for testing the trained detector
|
||||||
|
|
||||||
|
rng(0)
|
||||||
|
shuffledIndices = randperm(height(traficSignDataset));
|
||||||
|
idx = floor(0.6 * height(traficSignDataset));
|
||||||
|
|
||||||
|
trainingIdx = 1:idx;
|
||||||
|
trainingDataTbl = traficSignDataset(shuffledIndices(trainingIdx),:);
|
||||||
|
|
||||||
|
validationIdx = idx+1 : idx + 1 + floor(0.1 * length(shuffledIndices) );
|
||||||
|
validationDataTbl = traficSignDataset(shuffledIndices(validationIdx),:);
|
||||||
|
|
||||||
|
testIdx = validationIdx(end)+1 : length(shuffledIndices);
|
||||||
|
testDataTbl = traficSignDataset(shuffledIndices(testIdx),:);
|
||||||
|
|
||||||
|
% ----- use imageDatastore and boxLabelDatastore to create datastores
|
||||||
|
% for loading the image and label data during training and evaluation.
|
||||||
|
|
||||||
|
imdsTrain = imageDatastore(trainingDataTbl{:,'imageFilename'});
|
||||||
|
bldsTrain = boxLabelDatastore(trainingDataTbl(:,2:end))
|
||||||
|
|
||||||
|
imdsValidation = imageDatastore(validationDataTbl{:,'imageFilename'});
|
||||||
|
bldsValidation = boxLabelDatastore(trainingDataTbl(:,2:end))
|
||||||
|
|
||||||
|
imdsTest = imageDatastore(testDataTbl{:,'imageFilename'});
|
||||||
|
bldsTest = boxLabelDatastore(trainingDataTbl(:,2:end))
|
||||||
|
|
||||||
|
% combine image and box label datastores.
|
||||||
|
|
||||||
|
trainingData = combine(imdsTrain,bldsTrain); % erzeugt 'CombinedDatastore
|
||||||
|
validationData = combine(imdsValidation,bldsValidation);
|
||||||
|
testData = combine(imdsTest,bldsTest);
|
||||||
|
|
||||||
|
% display one of the training images and box labels.
|
||||||
|
|
||||||
|
data = read(trainingData);
|
||||||
|
I = data{1};
|
||||||
|
bbox = data{2};
|
||||||
|
annotatedImage = insertShape(I,'Rectangle',bbox);
|
||||||
|
annotatedImage = imresize(annotatedImage,4); % nur fuer Darstellung
|
||||||
|
figure
|
||||||
|
imshow(annotatedImage)
|
||||||
|
|
||||||
|
% ----- Create Faster R-CNN Detection Network
|
||||||
|
|
||||||
|
inputSize = [224 224 3];
|
||||||
|
|
||||||
|
preprocessedTrainingData = transform(trainingData, @(data)preprocessData(data,inputSize));
|
||||||
|
% Achtung: dieser DS wird nur zur Ermittlung der BoundingBoxes verwendet
|
||||||
|
|
||||||
|
% display one of the training images and box labels.
|
||||||
|
while 1==0 %hasdata(preprocessedTrainingData)
|
||||||
|
data = read(preprocessedTrainingData);
|
||||||
|
I = data{1};
|
||||||
|
bbox = data{2};
|
||||||
|
annotatedImage = insertShape(I,'Rectangle',bbox);
|
||||||
|
annotatedImage = imresize(annotatedImage,4); % nur fuer Darstellung
|
||||||
|
figure(1)
|
||||||
|
imshow(annotatedImage)
|
||||||
|
pause(0.100)
|
||||||
|
end
|
||||||
|
|
||||||
|
% Auswahl der anchor boxes
|
||||||
|
% Infos dazu: https://de.mathworks.com/help/vision/ug/estimate-anchor-boxes-from-training-data.html
|
||||||
|
numAnchors = 3;
|
||||||
|
anchorBoxes = estimateAnchorBoxes(preprocessedTrainingData,numAnchors);
|
||||||
|
|
||||||
|
% und das feature CNN
|
||||||
|
featureExtractionNetwork = resnet50;
|
||||||
|
featureLayer = 'activation_40_relu';
|
||||||
|
numClasses = width(traficSignDataset)-1; % also hier: 1, es sollen nur Verkehrsschilder erkannt werden
|
||||||
|
|
||||||
|
lgraph = fasterRCNNLayers(inputSize,numClasses,anchorBoxes,featureExtractionNetwork,featureLayer);
|
||||||
|
|
||||||
|
% Netzwerk ansehen
|
||||||
|
% analyzeNetwork(lgraph)
|
||||||
|
augmentedTrainingData = transform(trainingData,@augmentData_stepthree);
|
||||||
|
trainingData = transform(augmentedTrainingData,@(data)preprocessData(data,inputSize));
|
||||||
|
validationData = transform(validationData,@(data)preprocessData(data,inputSize));
|
||||||
|
|
||||||
|
|
||||||
|
options = trainingOptions('sgdm',...
|
||||||
|
'MaxEpochs',10,...
|
||||||
|
'MiniBatchSize',2,...
|
||||||
|
'InitialLearnRate',1e-3,...
|
||||||
|
'CheckpointPath',tempdir,...
|
||||||
|
'ValidationData',validationData);
|
||||||
|
|
||||||
|
netname = "netDetectorResNet50_stepthree.mat"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if doTraining
|
||||||
|
% Train the Faster R-CNN detector.
|
||||||
|
% * Adjust NegativeOverlapRange and PositiveOverlapRange to ensure
|
||||||
|
% that training samples tightly overlap with ground truth.
|
||||||
|
[detector, info] = trainFasterRCNNObjectDetector(trainingData,lgraph,options, ...
|
||||||
|
'NegativeOverlapRange',[0 0.3], ...
|
||||||
|
'PositiveOverlapRange',[0.6 1]);
|
||||||
|
save netname detector;
|
||||||
|
else
|
||||||
|
% Load pretrained detector for the example.
|
||||||
|
load netname detector;
|
||||||
|
end
|
||||||
|
|
||||||
|
% ----- quick check/test
|
||||||
|
|
||||||
|
I = imresize(I,inputSize(1:2));
|
||||||
|
[bboxes,scores] = detect(detector,I);
|
||||||
|
% Display the results.
|
||||||
|
|
||||||
|
sfigTitle = ""
|
||||||
|
if height(bboxes) > 0
|
||||||
|
I = insertObjectAnnotation(I,'rectangle',bboxes,scores);
|
||||||
|
sfigTitle = "Detected";
|
||||||
|
else
|
||||||
|
sfigTitle = "Not Detected";
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
figure;
|
||||||
|
imshow(I);
|
||||||
|
annotation('textbox', [0.5, 0.2, 0.1, 0.1], 'String', sfigTitle)
|
||||||
|
|
||||||
|
% ----- Testing
|
||||||
|
|
||||||
|
testData = transform(testData,@(data)preprocessData(data,inputSize));
|
||||||
|
|
||||||
|
% Run the detector on all the test images.
|
||||||
|
|
||||||
|
detectionResults = detect(detector,testData,'MinibatchSize',4);
|
||||||
|
|
||||||
|
% Evaluate the object detector using the average precision metric.
|
||||||
|
|
||||||
|
[ap, recall, precision] = evaluateDetectionPrecision(detectionResults,testData);
|
||||||
|
% The precision/recall (PR) curve highlights how precise a detector is at varying levels of recall. The ideal precision is 1 at all recall levels. The use of more data can help improve the average precision but might require more training time. Plot the PR curve.
|
||||||
|
|
||||||
|
figure
|
||||||
|
plot(recall,precision)
|
||||||
|
xlabel('Recall')
|
||||||
|
ylabel('Precision')
|
||||||
|
grid on
|
||||||
|
title(sprintf('Average Precision = %.2f', ap))
|
||||||
|
|
||||||
|
|
||||||
|
% ----- Helper functions
|
||||||
|
|
||||||
|
% function data = augmentData(data)
|
||||||
|
% % Randomly flip images and bounding boxes horizontally.
|
||||||
|
% tform = randomAffine2d('XReflection',true);
|
||||||
|
% sz = size(data{1});
|
||||||
|
% rout = affineOutputView(sz,tform);
|
||||||
|
% data{1} = imwarp(data{1},tform,'OutputView',rout);
|
||||||
|
%
|
||||||
|
% % Sanitize box data, if needed.
|
||||||
|
% data{2} = helperSanitizeBoxes(data{2}, sz);
|
||||||
|
%
|
||||||
|
% % Warp boxes.
|
||||||
|
% data{2} = bboxwarp(data{2},tform,rout);
|
||||||
|
% end
|
||||||
|
%
|
||||||
|
% function data = preprocessData(data,targetSize)
|
||||||
|
% % Resize image and bounding boxes to targetSize.
|
||||||
|
% sz = size(data{1},[1 2]);
|
||||||
|
% scale = targetSize(1:2)./sz;
|
||||||
|
% data{1} = imresize(data{1},targetSize(1:2));
|
||||||
|
%
|
||||||
|
% % Sanitize box data, if needed.
|
||||||
|
% data{2} = helperSanitizeBoxes(data{2}, sz);
|
||||||
|
%
|
||||||
|
% % Resize boxes.
|
||||||
|
% data{2} = bboxresize(data{2},scale);
|
||||||
|
% end
|
@ -0,0 +1,21 @@
|
|||||||
|
function data = augmentData_stepthree(data)
|
||||||
|
% Randomly flip images and bounding boxes horizontally.
|
||||||
|
tform = randomAffine2d('XReflection',true);
|
||||||
|
sz = size(data{1});
|
||||||
|
rout = affineOutputView(sz,tform);
|
||||||
|
|
||||||
|
% jitter
|
||||||
|
data{1} = jitterColorHSV(data{1},...
|
||||||
|
Contrast=0.2,...
|
||||||
|
Hue=0,...
|
||||||
|
Saturation=0.1,...
|
||||||
|
Brightness=0.2);
|
||||||
|
|
||||||
|
data{1} = imwarp(data{1},tform,'OutputView',rout);
|
||||||
|
|
||||||
|
% Sanitize box data, if needed.
|
||||||
|
data{2} = helperSanitizeBoxes(data{2}, sz);
|
||||||
|
|
||||||
|
% Warp boxes.
|
||||||
|
data{2} = bboxwarp(data{2},tform,rout);
|
||||||
|
end
|
@ -0,0 +1,135 @@
|
|||||||
|
function [ ] = func_groundTruthFromLabelPic( dataStorePicturePath, dataStoreLabelPath, outFile )
|
||||||
|
%
|
||||||
|
% erstellt us einem Picture-Datastore und einem Label-Datastore
|
||||||
|
% eine Groundtruth-Tabelle, wie sie z.B. in FasterRCNN.m
|
||||||
|
% benoetigt wird
|
||||||
|
%
|
||||||
|
% file von tas
|
||||||
|
% adaptiert als func 2022/12/28 vh
|
||||||
|
%
|
||||||
|
|
||||||
|
labelDS = imageDatastore(dataStoreLabelPath, 'IncludeSubfolders', true);
|
||||||
|
pictureDS = imageDatastore(dataStorePicturePath, 'IncludeSubfolders', true);
|
||||||
|
|
||||||
|
labelCount = numel(labelDS.Files)
|
||||||
|
pictureCount = numel(pictureDS.Files)
|
||||||
|
|
||||||
|
if labelCount ~= pictureCount
|
||||||
|
fprintf("!!!! Error: Die Anzahl der Bilder und Anzahl der LabelPicture sind ungleich -> Abbruch");
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
fprintf("-----------------------------------------------------------\n");
|
||||||
|
fprintf("Picture-Verzeichnis: %s\n", dataStorePicturePath)
|
||||||
|
fprintf("Anzahl Images: %d\n", pictureCount)
|
||||||
|
fprintf("Label-Verzeichnis: %s\n", dataStoreLabelPath)
|
||||||
|
fprintf("Anzahl Images: %d\n", labelCount)
|
||||||
|
fprintf("BE PATIENT.... \n")
|
||||||
|
fprintf("-----------------------------------------------------------\n");
|
||||||
|
|
||||||
|
% table anlegen
|
||||||
|
sz = [pictureCount 6];
|
||||||
|
varTypes = ["cellstr","cell","cell","cell","cell","logical"];
|
||||||
|
varNames = ["imageFilename","30GBS","50GBS","60GBS","AndereGBS","valid"];
|
||||||
|
DataSet = table('Size',sz,'VariableTypes',varTypes,'VariableNames',varNames);
|
||||||
|
|
||||||
|
rng(0)
|
||||||
|
shuffledIndices = randperm(pictureCount);
|
||||||
|
|
||||||
|
% los gehts
|
||||||
|
for i = 1:pictureCount
|
||||||
|
shuffeldIndex = shuffledIndices(i);
|
||||||
|
[imPic imPic_INFO]= readimage(pictureDS, shuffeldIndex);
|
||||||
|
[im_path imPic_name im_ext]=fileparts(imPic_INFO.Filename);
|
||||||
|
|
||||||
|
[imLabel imLabel_INFO]= readimage(labelDS, shuffeldIndex);
|
||||||
|
[lbl_path imLabel_name im_ext]=fileparts(imLabel_INFO.Filename);
|
||||||
|
|
||||||
|
%fprintf("picture: %s label: %s\n", imPic_name, imLabel_name);
|
||||||
|
%fprintf("picturepath: %s labelpath: %s\n", im_path, lbl_path);
|
||||||
|
box = [0,0,0,0]; %default if theres no labelimg
|
||||||
|
|
||||||
|
v = true;
|
||||||
|
|
||||||
|
if ~strcmp(imPic_name, imLabel_name)
|
||||||
|
fprintf("!!!! Error: zum Picture gibt es kein entsprechendes LabelPicture -> Abbruch");
|
||||||
|
imPic_name
|
||||||
|
imLabel_name
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
% LabelRegion aus Image ausschneiden
|
||||||
|
bw = imLabel;
|
||||||
|
s = regionprops(bw, 'BoundingBox');
|
||||||
|
box = cat(1, s.BoundingBox); % structure to matrix
|
||||||
|
box = round(box);
|
||||||
|
|
||||||
|
% falls mehrere Marker vorhanden sind, ignorieren wir den Datensatz
|
||||||
|
% das dürfte für das Training Region detection besser sein ??
|
||||||
|
if (height(box) > 1)
|
||||||
|
v = false;
|
||||||
|
end
|
||||||
|
box = box(1,:);
|
||||||
|
|
||||||
|
if (numel(box) ~= 4)
|
||||||
|
fprintf("Boxkoordinaten nicht ok: %s %s\n", imPic_name, imLabel_name)
|
||||||
|
box
|
||||||
|
v = false
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
a = [];
|
||||||
|
b = [];
|
||||||
|
c = [];
|
||||||
|
d = [];
|
||||||
|
|
||||||
|
|
||||||
|
if contains(im_path,'30GBS') == 1
|
||||||
|
a = num2cell(box, 2);
|
||||||
|
elseif contains(im_path,'50GBS') == 1
|
||||||
|
b = num2cell(box, 2);
|
||||||
|
elseif contains(im_path,'60GBS') == 1
|
||||||
|
c = num2cell(box, 2);
|
||||||
|
else
|
||||||
|
d = num2cell(box, 2);
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
%check for boxes which are somewhat wrong
|
||||||
|
if v
|
||||||
|
if (box(3) < 8.) ...
|
||||||
|
|| (box(4) < 8.) ...
|
||||||
|
|| (abs(box(3) - box(4)) > 2) ...
|
||||||
|
|| (box(1) + box(3) > 1024) ...
|
||||||
|
|| (box(2) + box(4) > 768)
|
||||||
|
fprintf("boxkoordinaten nicht i.o. %s (%d %d %d %d) \n", imLabel_name, box(1),box(2),box(3),box(4));
|
||||||
|
v = false;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
DataSet(shuffeldIndex,:) = {imPic_INFO.Filename,a,b,c,d,v};
|
||||||
|
|
||||||
|
% display one of the training images and box labels.
|
||||||
|
if (shuffeldIndex == 4)
|
||||||
|
annotatedImage = insertShape(imPic,'Rectangle',box);
|
||||||
|
figure
|
||||||
|
imshow(annotatedImage)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
%Die Daten sind teilweise nicht in Ordnung, am einfachsten ist natürlich
|
||||||
|
%die Einträge zu löschen, die nicht gut sind.
|
||||||
|
fprintf("Groundtruth hat %d eintraege vor der Bereinigung \n ", height(DataSet) )
|
||||||
|
|
||||||
|
toDelete = DataSet.valid == false;
|
||||||
|
DataSet(toDelete,:) = [];
|
||||||
|
|
||||||
|
DataSet.valid=[];
|
||||||
|
|
||||||
|
fprintf("Groundtruth hat %d eintraege nach der Bereinigung \n", height(DataSet) )
|
||||||
|
|
||||||
|
save(outFile, 'DataSet' );
|
||||||
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
|||||||
|
function [ ] = func_setupData( dataDir, zippedDataFile, grDataFile )
|
||||||
|
|
||||||
|
% script func_setupData:
|
||||||
|
% - entpackt die trainingsdaten,
|
||||||
|
% - räumt ein bisschen auf, bzw. liefert Hinweise zum aufräumen
|
||||||
|
% - erstellt grounddata
|
||||||
|
% - (alles nur, falls es das nicht schon gibt)
|
||||||
|
|
||||||
|
if not(exist(dataDir , 'dir'))
|
||||||
|
% unzip data
|
||||||
|
if (not(exist(zippedDataFile , 'file')))
|
||||||
|
fprintf("Data file is missing please copy %s to script folder !", zippedDataFile);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
fprintf("unzipping Data");
|
||||||
|
unzip (zippedDataFile, dataDir)
|
||||||
|
|
||||||
|
%rename files correctly (there are two wrong):
|
||||||
|
fprintf("fix faulty falenames\n");
|
||||||
|
movefile (append(dataDir, '/Labels_1024_768/60GBS/60GBS_Gruppe05_SS21_Nr49.png'), append(dataDir, '/Labels_1024_768/60GBS/60GBS_Gruppe05_SS21_49.png'));
|
||||||
|
movefile (append(dataDir, '/Labels_1024_768/60GBS/60GBS_Gruppe05_SS21_Nr50.png'), append(dataDir, '/Labels_1024_768/60GBS/60GBS_Gruppe05_SS21_50.png'));
|
||||||
|
|
||||||
|
fprintf("Labeldata is incomplete\n");
|
||||||
|
fprintf("please compare data in keinGBS and delete what's to much manually\n");
|
||||||
|
fprintf("otherwise grounddatageneration will fail\n");
|
||||||
|
frpintf("then restart script \n");
|
||||||
|
return;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
% generate Grounddata from Pictures
|
||||||
|
|
||||||
|
if (not(exist(grDataFile , 'file')))
|
||||||
|
dataStorePicturePath = append(pwd,'/', dataDir,'/Pictures_1024_768/');
|
||||||
|
dataStoreLabelPath = append(pwd,'/', dataDir, '/Labels_1024_768/');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% Die Tabelle wird in einer Funktion erstellt und gespeichert
|
||||||
|
% dabei werden Datensätze, wo die Label nicht passen entfernt.
|
||||||
|
func_groundTruthFromLabelPic_stepthree(dataStorePicturePath, dataStoreLabelPath, grDataFile);
|
||||||
|
end
|
Loading…
Reference in New Issue