Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
917ffd276c | ||
|
|
647716ff66 | ||
|
|
c7937d6389 | ||
|
|
171dbee748 | ||
|
|
6dad1d2df2 | ||
|
|
6963e15557 |
No files matched your search
+132
-1
@@ -7,11 +7,122 @@
|
|||||||
#define FILE_HEADER_STRING "__info2_image_file_format__"
|
#define FILE_HEADER_STRING "__info2_image_file_format__"
|
||||||
|
|
||||||
// TODO Implementieren Sie geeignete Hilfsfunktionen für das Lesen der Bildserie aus einer Datei
|
// TODO Implementieren Sie geeignete Hilfsfunktionen für das Lesen der Bildserie aus einer Datei
|
||||||
|
//Hilfsfunktion zum Einlesen eines einzigen Bildes
|
||||||
|
static int read_single_image(FILE *file, unsigned short width, unsigned short height, GrayScaleImage *img, unsigned char *out_label)
|
||||||
|
{
|
||||||
|
size_t pixels_to_read = (size_t)width * height;
|
||||||
|
|
||||||
|
img->width = width; //save width and height read from file in struct
|
||||||
|
img->height = height;
|
||||||
|
|
||||||
|
img->buffer = malloc(pixels_to_read * sizeof(GrayScalePixelType)); //allocating needed memory for pixel-buffer
|
||||||
|
if(img->buffer == NULL) //Error case
|
||||||
|
{
|
||||||
|
printf("%s", "Fehler bei der Speicherreservierung!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(fread(img->buffer, sizeof(GrayScalePixelType), pixels_to_read, file) != pixels_to_read) //read all pixels one after another
|
||||||
|
{
|
||||||
|
free(img->buffer); //Error case: free allocated memory
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(fread(out_label, sizeof(unsigned char), 1, file) != 1) //read label
|
||||||
|
{
|
||||||
|
free(img->buffer); //Error case: free allocated memory
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO Vervollständigen Sie die Funktion readImages unter Benutzung Ihrer Hilfsfunktionen
|
// TODO Vervollständigen Sie die Funktion readImages unter Benutzung Ihrer Hilfsfunktionen
|
||||||
GrayScaleImageSeries *readImages(const char *path)
|
GrayScaleImageSeries *readImages(const char *path)
|
||||||
{
|
{
|
||||||
GrayScaleImageSeries *series = NULL;
|
char buffer[BUFFER_SIZE];
|
||||||
|
unsigned short num_images;
|
||||||
|
unsigned short width;
|
||||||
|
unsigned short height;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FILE *file = fopen(path, "rb");
|
||||||
|
|
||||||
|
if(file == NULL) //file can not be opened
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************************
|
||||||
|
* VERIFY IF HEADER FILE IS INCLUDED
|
||||||
|
********************************************************************************/
|
||||||
|
fread(buffer, sizeof(buffer[0]), strlen(FILE_HEADER_STRING), file);
|
||||||
|
buffer[strlen(FILE_HEADER_STRING)] = '\0';
|
||||||
|
|
||||||
|
if(strcmp(buffer, FILE_HEADER_STRING) != 0) //file does not include file header
|
||||||
|
{
|
||||||
|
fclose(file);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************************
|
||||||
|
* READ AMOUNT, WIDTH, HEIGHT
|
||||||
|
********************************************************************************/
|
||||||
|
fread(&num_images, sizeof(num_images), 1, file);
|
||||||
|
fread(&width, sizeof(width), 1, file);
|
||||||
|
fread(&height, sizeof(height), 1, file);
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************************************************************
|
||||||
|
* MEMORY ALLOCATION FOR SERIES
|
||||||
|
********************************************************************************/
|
||||||
|
GrayScaleImageSeries *series = malloc(sizeof(GrayScaleImageSeries));
|
||||||
|
if(series == NULL) //Error: memory cannot be allocated; close file
|
||||||
|
{
|
||||||
|
fclose(file);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
series->count = num_images; //save number of images in struct
|
||||||
|
series->images = malloc(num_images * sizeof(GrayScaleImage)); //allocate memory for all images
|
||||||
|
series->labels = malloc(num_images * sizeof(unsigned char)); //allocate memory for all labels
|
||||||
|
|
||||||
|
if(series->images == NULL || series->labels == NULL) //Error: memory cannot be allocated: free every memory
|
||||||
|
{
|
||||||
|
free(series->images);
|
||||||
|
free(series->labels);
|
||||||
|
free(series);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************************************************************
|
||||||
|
* READ IMAGES
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
for(int i = 0; i < num_images; i++)
|
||||||
|
{
|
||||||
|
if(read_single_image(file, width, height, &series->images[i], &series->labels[i]) != 1)
|
||||||
|
{
|
||||||
|
series->count = i;
|
||||||
|
|
||||||
|
clearSeries(series); //free all allocated memory
|
||||||
|
|
||||||
|
fclose(file); //Error case: close file
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return series;
|
return series;
|
||||||
}
|
}
|
||||||
@@ -19,4 +130,24 @@ GrayScaleImageSeries *readImages(const char *path)
|
|||||||
// TODO Vervollständigen Sie die Funktion clearSeries, welche eine Bildserie vollständig aus dem Speicher freigibt
|
// TODO Vervollständigen Sie die Funktion clearSeries, welche eine Bildserie vollständig aus dem Speicher freigibt
|
||||||
void clearSeries(GrayScaleImageSeries *series)
|
void clearSeries(GrayScaleImageSeries *series)
|
||||||
{
|
{
|
||||||
|
if(series == NULL) //pointer points to nothing; nothing to clear
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(series->images != NULL) //check if image pointer is valid
|
||||||
|
{
|
||||||
|
for(int i = 0; i < series->count; i++) //free memory allocated for all images until error occured
|
||||||
|
{
|
||||||
|
free(series->images[i].buffer);
|
||||||
|
}
|
||||||
|
free(series->images); //free memory allocated for image array
|
||||||
|
}
|
||||||
|
|
||||||
|
if(series->labels != NULL) //check if label pointer is valid
|
||||||
|
{
|
||||||
|
free(series->labels); //free memory allocated for label array
|
||||||
|
}
|
||||||
|
|
||||||
|
free(series); //free memory allocated for series
|
||||||
}
|
}
|
||||||
@@ -49,6 +49,23 @@ void test_readImagesReturnsCorrectNumberOfImages(void)
|
|||||||
remove(path);
|
remove(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_mytest_clear_function_NULL(void)
|
||||||
|
{
|
||||||
|
clearSeries(NULL); //check if function works with NULL-pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_mytest_clear_function_series_without_images_or_labels(void)
|
||||||
|
{
|
||||||
|
GrayScaleImageSeries *series = malloc(sizeof(GrayScaleImageSeries));
|
||||||
|
|
||||||
|
TEST_ASSERT_NOT_NULL(series);
|
||||||
|
series->images = NULL;
|
||||||
|
series->labels = NULL;
|
||||||
|
series->count = 0;
|
||||||
|
|
||||||
|
clearSeries(series);
|
||||||
|
}
|
||||||
|
|
||||||
void test_readImagesReturnsCorrectImageWidth(void)
|
void test_readImagesReturnsCorrectImageWidth(void)
|
||||||
{
|
{
|
||||||
GrayScaleImageSeries *series = NULL;
|
GrayScaleImageSeries *series = NULL;
|
||||||
@@ -139,5 +156,8 @@ int main()
|
|||||||
RUN_TEST(test_readImagesReturnsNullOnNotExistingPath);
|
RUN_TEST(test_readImagesReturnsNullOnNotExistingPath);
|
||||||
RUN_TEST(test_readImagesFailsOnWrongFileTag);
|
RUN_TEST(test_readImagesFailsOnWrongFileTag);
|
||||||
|
|
||||||
|
RUN_TEST(test_mytest_clear_function_NULL);
|
||||||
|
RUN_TEST(test_mytest_clear_function_series_without_images_or_labels);
|
||||||
|
|
||||||
return UNITY_END();
|
return UNITY_END();
|
||||||
}
|
}
|
||||||
+146
-11
@@ -4,32 +4,167 @@
|
|||||||
|
|
||||||
// TODO Matrix-Funktionen implementieren
|
// TODO Matrix-Funktionen implementieren
|
||||||
|
|
||||||
Matrix createMatrix(unsigned int rows, unsigned int cols)
|
Matrix createMatrix(unsigned int rows, unsigned int cols) // eine Matrix mit ner bestimmten Anzahl an Zeilen und Spalten erstellen und benötigten Speicher reservieren
|
||||||
{
|
{
|
||||||
|
// leere Matrix erstellen:
|
||||||
|
Matrix matrix = {0, 0, NULL};
|
||||||
|
|
||||||
|
if (rows == 0 || cols == 0) // Matrizen mit null Zeilen oder Spalten sind nicht erlaubt
|
||||||
|
{
|
||||||
|
return matrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearMatrix(Matrix *matrix)
|
// Speicher für alle Matrixelemente reservieren:
|
||||||
{
|
matrix.buffer = (MatrixType *)calloc(rows * cols, sizeof(MatrixType));
|
||||||
|
|
||||||
|
// prüfen, ob die Speicherreservierung erfolgreich war:
|
||||||
|
if (matrix.buffer != NULL)
|
||||||
|
{
|
||||||
|
matrix.rows = rows;
|
||||||
|
matrix.cols = cols;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx)
|
return matrix;
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx)
|
void clearMatrix(Matrix *matrix) // den dynamisch reservierten Speicher einer Matrix freigeben und die Matrix zurücksetzen
|
||||||
{
|
{
|
||||||
|
if (matrix != NULL) // prüfen, ob ein gültiger Zeiger übergeben wurde
|
||||||
|
{
|
||||||
|
free(matrix->buffer); // reservierten Speicher freigeben
|
||||||
|
|
||||||
|
// Matrix auf Ausgangszustand zurücksetzen:(kann sonst zu Speicherlecks (Memory Leaks) führen, da dynamische Speicher weiterhin belegt bleibt)
|
||||||
|
matrix->buffer = NULL;
|
||||||
|
matrix->rows = 0;
|
||||||
|
matrix->cols = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix add(const Matrix matrix1, const Matrix matrix2)
|
void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx) // einen bestimmten Wert an einer bestimmten Position der Matrix speichern
|
||||||
{
|
{
|
||||||
|
// prüfen, ob die Matrix gültig ist und die Indizes innerhalb der Matrixgrenzen liegen:
|
||||||
|
if (matrix.buffer == NULL || rowIdx >= matrix.rows || colIdx >= matrix.cols)
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix multiply(const Matrix matrix1, const Matrix matrix2)
|
unsigned int index = rowIdx * matrix.cols + colIdx; // Position im eindimensionalen Speicher berechnen
|
||||||
{
|
|
||||||
|
|
||||||
|
matrix.buffer[index] = value; // Wert an der gewünschten Position speichern
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx) // einen Wert an einer bestimmten Position auslesen
|
||||||
|
{
|
||||||
|
// prüfen, ob die Matrix gültig ist und die Indizes innerhalb der Matrixgrenzen liegen:
|
||||||
|
if (matrix.buffer == NULL || rowIdx >= matrix.rows || colIdx >= matrix.cols)
|
||||||
|
{
|
||||||
|
return UNDEFINED_MATRIX_VALUE; // in matrix.h def.
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int index = rowIdx * matrix.cols + colIdx; // eindimensionalen Index berechnen
|
||||||
|
|
||||||
|
return matrix.buffer[index]; // Wert an der gewünschten Position zurückgeben
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix add(const Matrix matrix1, const Matrix matrix2) // zwei Matrizen addieren
|
||||||
|
{
|
||||||
|
Matrix result = {0, 0, NULL}; // zunächst eine leere Ergebnismatrix erstellen
|
||||||
|
|
||||||
|
// ungültige Eingabematrizen überprüfen:
|
||||||
|
if (matrix1.buffer == NULL || matrix2.buffer == NULL || matrix1.rows == 0 || matrix1.cols == 0 || matrix2.rows == 0 || matrix2.cols == 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall 1: Beide Matrizen haben dieselben Dimensionen:
|
||||||
|
if (matrix1.rows == matrix2.rows &&
|
||||||
|
matrix1.cols == matrix2.cols)
|
||||||
|
{
|
||||||
|
result = createMatrix(matrix1.rows, matrix1.cols);
|
||||||
|
|
||||||
|
if (result.buffer == NULL)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < matrix1.rows * matrix1.cols; i++) // alle Elemente paarweise addieren
|
||||||
|
{
|
||||||
|
result.buffer[i] = matrix1.buffer[i] + matrix2.buffer[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall 2: Broadcasting mit einem Spaltenvektor:
|
||||||
|
else if (matrix1.rows == matrix2.rows && (matrix1.cols == 1 || matrix2.cols == 1))
|
||||||
|
{
|
||||||
|
// die Matrix mit mehr Spalten bestimmt die Ergebnisgröße:
|
||||||
|
const Matrix *matrix = &matrix1;
|
||||||
|
const Matrix *vector = &matrix2;
|
||||||
|
|
||||||
|
if (matrix1.cols == 1) // falls matrix1 der Spaltenvektor ist, werden die Rollen der beiden Eingabematrizen vertauscht (Broadcasting soll in beiden Reihenfolgen funktionieren)
|
||||||
|
{
|
||||||
|
matrix = &matrix2;
|
||||||
|
vector = &matrix1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = createMatrix(matrix->rows, matrix->cols);
|
||||||
|
|
||||||
|
if (result.buffer == NULL)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int row = 0; row < matrix->rows; row++) // jeden Matrixwert mit dem Vektorwert seiner Zeile addieren
|
||||||
|
{
|
||||||
|
for (unsigned int col = 0; col < matrix->cols; col++)
|
||||||
|
{
|
||||||
|
result.buffer[row * matrix->cols + col] =
|
||||||
|
matrix->buffer[row * matrix->cols + col]
|
||||||
|
+ vector->buffer[row];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result; // wenn keine der beiden Varianten möglich ist, bleibt Ergebnismatrix leer
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix multiply(const Matrix matrix1, const Matrix matrix2) // zwei Matrizen multiplizieren
|
||||||
|
{
|
||||||
|
Matrix result = {0, 0, NULL}; // leere Ergebnismatrix
|
||||||
|
|
||||||
|
// prüfen, ob beide Eingabematrizen gültig sind:
|
||||||
|
if (matrix1.buffer == NULL || matrix2.buffer == NULL || matrix1.rows == 0 || matrix1.cols == 0 || matrix2.rows == 0 || matrix2.cols == 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matrix1.cols != matrix2.rows) // Spaltenanzahl der ersten Matrix muss der Zeilenanzahl der zweiten Matrix entsprechen
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = createMatrix(matrix1.rows, matrix2.cols); // Ergebnis hat so viele Zeilen wie matrix1 und so viele Spalten wie matrix2
|
||||||
|
|
||||||
|
if (result.buffer == NULL)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int row = 0; row < matrix1.rows; row++) // Durchführung Matrixmultiplikation...
|
||||||
|
{
|
||||||
|
for (unsigned int col = 0; col < matrix2.cols; col++)
|
||||||
|
{
|
||||||
|
MatrixType sum = 0; // Summe für das aktuelle Ergebniselement
|
||||||
|
|
||||||
|
for (unsigned int k = 0; k < matrix1.cols; k++)
|
||||||
|
{
|
||||||
|
sum += matrix1.buffer[row * matrix1.cols + k] * matrix2.buffer[k * matrix2.cols + col];
|
||||||
|
// "row" durchläuft Zeilen d. ersten Matrix; "col" durchläuft Zeilen d. zweiten Matrix; "k" berechnet Summe d. Produkte
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result.buffer[row * result.cols + col] = sum; // berechnetes Ergebnis speichern
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,12 @@
|
|||||||
typedef float MatrixType;
|
typedef float MatrixType;
|
||||||
|
|
||||||
// TODO Matrixtyp definieren
|
// TODO Matrixtyp definieren
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned int rows; // Anzahl der Zeilen
|
||||||
|
unsigned int cols; // Anzahl der Spalten
|
||||||
|
MatrixType *buffer; // Zeiger auf den Speicher mit den Matrixwerten
|
||||||
|
} Matrix;
|
||||||
|
|
||||||
|
|
||||||
Matrix createMatrix(unsigned int rows, unsigned int cols);
|
Matrix createMatrix(unsigned int rows, unsigned int cols);
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ NeuralNetwork loadModel(const char *path)
|
|||||||
|
|
||||||
static Matrix imageBatchToMatrixOfImageVectors(const GrayScaleImage images[], unsigned int count)
|
static Matrix imageBatchToMatrixOfImageVectors(const GrayScaleImage images[], unsigned int count)
|
||||||
{
|
{
|
||||||
Matrix matrix = {NULL, 0, 0};
|
Matrix matrix = {0, 0, NULL};
|
||||||
|
|
||||||
if(count > 0 && images != NULL)
|
if(count > 0 && images != NULL)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,62 @@
|
|||||||
static void prepareNeuralNetworkFile(const char *path, const NeuralNetwork nn)
|
static void prepareNeuralNetworkFile(const char *path, const NeuralNetwork nn)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
|
// 1. Datei Öffnen
|
||||||
|
FILE *file = fopen(path, "wb");
|
||||||
|
|
||||||
|
// überprüfen ob das öffnen funktioniert hat
|
||||||
|
if (file == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Datei-Header schreiben (Überschrifft bzw. Stempel was in der Datei steht)
|
||||||
|
const char *fileTag = "__info2_neural_network_file_format__";
|
||||||
|
fwrite(fileTag, sizeof(char), strlen(fileTag), file);
|
||||||
|
|
||||||
|
// 3. Eingabe- und Ausgabedimension bestimmen
|
||||||
|
int inputDimension = nn.layers[0].weights.cols;
|
||||||
|
int outputDimension = nn.layers[0].weights.rows;
|
||||||
|
// in unser File speichern
|
||||||
|
fwrite(&inputDimension, sizeof(int), 1, file);
|
||||||
|
fwrite(&outputDimension, sizeof(int), 1, file);
|
||||||
|
|
||||||
|
for(unsigned int i = 0; i < nn.numberOfLayers; i++)
|
||||||
|
{
|
||||||
|
// Speichern der aktuellen Speicheradresse
|
||||||
|
const Layer *layer = &nn.layers[i];
|
||||||
|
|
||||||
|
// Gewichtsmatrix des aktuellen Layers in die Datei speichern
|
||||||
|
fwrite
|
||||||
|
(
|
||||||
|
layer->weights.buffer,
|
||||||
|
sizeof(MatrixType),
|
||||||
|
layer->weights.rows * layer->weights.cols,
|
||||||
|
file
|
||||||
|
);
|
||||||
|
|
||||||
|
//Bias-Matrix des aktuellen Layers in die Datei speichern
|
||||||
|
fwrite
|
||||||
|
(
|
||||||
|
layer->biases.buffer,
|
||||||
|
sizeof(MatrixType),
|
||||||
|
layer->biases.rows * layer->biases.cols,
|
||||||
|
file
|
||||||
|
);
|
||||||
|
|
||||||
|
//ermitteln wie viele Ausgabewerte der nächste Layer haben wird
|
||||||
|
if (i + 1 < nn.numberOfLayers)
|
||||||
|
{
|
||||||
|
outputDimension = nn.layers[i + 1].weights.rows;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
outputDimension = 0;
|
||||||
|
}
|
||||||
|
fwrite(&outputDimension, sizeof(int), 1, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_loadModelReturnsCorrectNumberOfLayers(void)
|
void test_loadModelReturnsCorrectNumberOfLayers(void)
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
Reference in new issue
Block a user