diff --git a/.idea/editor.xml b/.idea/editor.xml new file mode 100644 index 0000000..45a9ffc --- /dev/null +++ b/.idea/editor.xml @@ -0,0 +1,341 @@ + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..5533b38 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + "associatedIndex": 5 +} + + + + { + "keyToString": { + "RunOnceActivity.RadMigrateCodeStyle": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "RunOnceActivity.cidr.known.project.marker": "true", + "RunOnceActivity.git.unshallow": "true", + "RunOnceActivity.readMode.enableVisualFormatting": "true", + "cf.first.check.clang-format": "false", + "cidr.known.project.marker": "true", + "git-widget-placeholder": "main", + "last_opened_file_path": "C:/Users/Silvana/NeuronalesNetz", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "vue.rearranger.settings.migration": "true" + } +} + + + + + 1762940933833 + + + + + + + + + + file://$PROJECT_DIR$/imageInput.c + + + + + \ No newline at end of file diff --git a/imageInput.c b/imageInput.c index bb30de1..f987e39 100644 --- a/imageInput.c +++ b/imageInput.c @@ -6,17 +6,209 @@ #define BUFFER_SIZE 100 #define FILE_HEADER_STRING "__info2_image_file_format__" -// TODO Implementieren Sie geeignete Hilfsfunktionen für das Lesen der Bildserie aus einer Datei -// TODO Vervollständigen Sie die Funktion readImages unter Benutzung Ihrer Hilfsfunktionen +/* + * Diese Hilfsfunktionen kümmern sich um: + * - Das Öffnen der Datei und Überprüfen des Dateinamens + * - Das Überprüfen des Headers, um sicherzustellen, dass es sich um die richtige Datei handelt + * - Das Einlesen der Metadaten (Anzahl der Bilder, Bildbreite und -höhe) + * -> Die Hilfsfunktionen garantieren, dass die Datei gültig ist, bevor mit dem Einlesen der Bilddaten fortgefahren wird. + */ + + +static FILE *openImageFile(const char *path) // Checks if the given filename pointer is valid (not NULL). +{ + if (path == NULL) + { + return NULL; + } + + return fopen(path, "rb"); //opening document in binear +} + +static int readAndCheckHeader(FILE *file) // gets the length of the header text +{ + size_t headerLength = strlen(FILE_HEADER_STRING); + char buffer[BUFFER_SIZE]; + + if (headerLength + 1 > BUFFER_SIZE) //checks if buffer is big enough for header size + { + return 0; + } + + if (fread(buffer, 1, headerLength, file) != headerLength) // Checks if reading the expected number of header bytes from the file succeeded + { + return 0; + } + + buffer[headerLength] = '\0'; // add string terminator so the header becomes a valid C-string + + if (strcmp(buffer, FILE_HEADER_STRING) != 0) // checks if the expected header matches the header read from the file + { + return 0; + } + + return 1; /* Header ok */ +} + + +static int readImageMetaData(FILE *file, // reads the metadata (count, width, height) from the file and stores them in the provided pointers + unsigned short *count, + unsigned short *width, + unsigned short *height) +{ + if (fread(count, sizeof(unsigned short), 1, file) != 1) // read the number of images from the file + { + return 0; + } + if (fread(width, sizeof(unsigned short), 1, file) != 1) // reads the image width + { + return 0; + } + if (fread(height, sizeof(unsigned short), 1, file) != 1) //reads the image height + { + return 0; + } + + if (*count == 0 || *width == 0 || *height == 0) // check for invalid metadata (count, width or height cannot be zero) + { + return 0; + } + + return 1; +} + + +/* + Hauptfunktion +*/ GrayScaleImageSeries *readImages(const char *path) { - GrayScaleImageSeries *series = NULL; - + // 1. Open the file + FILE *file = openImageFile(path); + if (file == NULL) + { + return NULL; + } + + // 2. Check the header + if (!readAndCheckHeader(file)) + { + fclose(file); + return NULL; + } + + // 3. Read image metadata + unsigned short count = 0; + unsigned short width = 0; + unsigned short height = 0; + + if (!readImageMetaData(file, &count, &width, &height)) + { + fclose(file); + return NULL; + } + + // 4. Allocate memory for image series + GrayScaleImageSeries *series = (GrayScaleImageSeries *)malloc(sizeof(GrayScaleImageSeries)); // Allocate memory for the image series, images, and labels. Return NULL if allocation fails. + if (series == NULL) + { + fclose(file); + return NULL; + } + + series->count = count; + series->images = (GrayScaleImage *)calloc(count, sizeof(GrayScaleImage)); + series->labels = (unsigned char *)malloc(count * sizeof(unsigned char)); + + if (series->images == NULL || series->labels == NULL) + { + free(series->images); + free(series->labels); + free(series); + fclose(file); + return NULL; + } + + // 5. Read the images and labels + for (unsigned int i = 0; i < count; i++) + { + GrayScaleImage *image = &series->images[i]; + + image->width = (unsigned int)width; + image->height = (unsigned int)height; + + size_t numPixels = (size_t)width * (size_t)height; + + image->buffer = (GrayScalePixelType *)malloc(numPixels * sizeof(GrayScalePixelType)); + if (image->buffer == NULL) + { + for (unsigned int j = 0; j < i; j++) + { + free(series->images[j].buffer); + } + free(series->images); + free(series->labels); + free(series); + fclose(file); + return NULL; + } + + if (fread(image->buffer, sizeof(GrayScalePixelType), numPixels, file) != numPixels) // Check if the correct number of pixel values (width * height) were read for the image + { + for (unsigned int j = 0; j <= i; j++) + { + free(series->images[j].buffer); + } + free(series->images); + free(series->labels); + free(series); + fclose(file); + return NULL; + } + + if (fread(&series->labels[i], sizeof(unsigned char), 1, file) != 1) // Check if the label for the image was successfully read (1 byte) + { + for (unsigned int j = 0; j <= i; j++) + { + free(series->images[j].buffer); + } + free(series->images); + free(series->labels); + free(series); + fclose(file); + return NULL; + } + } + + fclose(file); return series; } // TODO Vervollständigen Sie die Funktion clearSeries, welche eine Bildserie vollständig aus dem Speicher freigibt void clearSeries(GrayScaleImageSeries *series) { -} \ No newline at end of file + if (series == NULL) + { + return; + } + + if (series->images != NULL) + { + for (unsigned int i = 0; i < series->count; i++) + { + free(series->images[i].buffer); + series->images[i].buffer = NULL; + } + free(series->images); + series->images = NULL; + } + + if (series->labels != NULL) + { + free(series->labels); + series->labels = NULL; + } + + free(series); +} diff --git a/matrix.c b/matrix.c index ad00628..9b27187 100644 --- a/matrix.c +++ b/matrix.c @@ -6,30 +6,148 @@ Matrix createMatrix(unsigned int rows, unsigned int cols) { - + if(rows && cols){ + Matrix matrix; + matrix.rows = rows; + matrix.cols = cols; + matrix.buffer = malloc(rows * cols * sizeof(MatrixType)); + + matrix.buffer = malloc(rows * cols * sizeof(MatrixType)); + if (!matrix.buffer) { + matrix.rows = 0; + matrix.cols = 0; + return matrix; // malloc ist fehlgeschlagen + } + + + for (int i = 0; i < (rows * cols); i++) + matrix.buffer[i] = 0; + + return matrix; + } + Matrix matrix; + matrix.rows = 0; + matrix.cols = 0; + matrix.buffer = 0; + return matrix; } + void clearMatrix(Matrix *matrix) { - + free(matrix->buffer); + matrix->buffer = NULL; + matrix->rows = 0; + matrix->cols = 0; } void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx) { - + matrix.buffer[rowIdx * matrix.cols + colIdx] = value; } MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx) { - + if(rowIdx >= matrix.rows || colIdx >= matrix.cols){ + return UNDEFINED_MATRIX_VALUE; + } + return matrix.buffer[rowIdx * matrix.cols + colIdx]; } Matrix add(const Matrix matrix1, const Matrix matrix2) { - + int rows1 = rows(matrix1); + int rows2 = rows(matrix2); + int cols1 = cols(matrix1); + int cols2 = cols(matrix2); + if((cols1 == 1 || cols2 == 1) && rows1 == rows2) //Broadcasting + { + if(cols1 == 1) //Wenn die erste Matrix der Vektor ist + { + return broadcasting(matrix1, matrix2); + } + else + { + return broadcasting(matrix2, matrix1); + } + } + else if(rows1 == rows2 && cols1 == cols2) //Addition nur moeglich, wenn beide Matrizen gleiche ANzahl an Zeilen und Spalten haben + { + Matrix addition = createMatrix(rows1, cols2); //Matrix erstellt, in die die addierten Werte geschrieben werden + MatrixType wert1; + MatrixType wert2; + for(int i = 0; i < rows1; i++){ //soll fuer jedes Element durchlaufen, sodass alle Werte von beiden Matrizen aufaddiert werden. + for(int j = 0; j < cols1; j++){ + wert1 = getMatrixAt(matrix1, i, j); + wert2 = getMatrixAt(matrix2, i, j); + MatrixType addierterWert = wert1 + wert2; + setMatrixAt(addierterWert, addition, i, j); + } + } + return addition; + } + else{ + return createMatrix(0,0); + } } +Matrix broadcasting(const Matrix vektor, const Matrix matrix) + { + int rowsM = rows(matrix); + int colsM = cols(matrix); + Matrix result = createMatrix(rowsM, colsM); + MatrixType wert; + MatrixType koordinate; + for(int i = 0; i < rowsM; i++) + { + for(int j = 0; j < colsM; j++){ + wert = getMatrixAt(matrix, i, j); + koordinate = getMatrixAt(vektor, i, 0); + setMatrixAt((koordinate + wert), result, i, j); + } + } + return result; + } + Matrix multiply(const Matrix matrix1, const Matrix matrix2) { - + int rows1 = rows(matrix1); + int cols1 = cols(matrix1); + int rows2 = rows(matrix2); + int cols2 = cols(matrix2); + if(cols1 == rows2) //Bedingung fuer Multiplikation: Spalten der ersten Matrix gleich Zeilen 2. Matrix + { + Matrix result = createMatrix(rows1, cols2); + MatrixType wert1; + MatrixType wert2; + MatrixType mul; + MatrixType add = 0; + for(int i = 0; i < rows1; i++) + { + for(int k = 0; k < cols2; k++) + { + for(int j = 0; j < cols1; j++) + { + wert1 = getMatrixAt(matrix1, i, j); + wert2 = getMatrixAt(matrix2, j, k); + mul = wert1 * wert2; + add += mul; + } + setMatrixAt(add, result, i, k); + add = 0; + } + } + return result; + } + return createMatrix(0,0); +} + +int rows(const Matrix matrix) +{ + return matrix.rows; +} + +int cols(const Matrix matrix) +{ + return matrix.cols; } \ No newline at end of file diff --git a/matrix.h b/matrix.h index cc640d1..dfe05c9 100644 --- a/matrix.h +++ b/matrix.h @@ -6,6 +6,11 @@ typedef float MatrixType; // TODO Matrixtyp definieren +typedef struct{ + unsigned int rows; + unsigned int cols; + MatrixType *buffer; //Data wird in einem eindimensionalen Array gespeichert (Spalten und Reihen liegen ja im Speicher hintereinannder) +} Matrix; Matrix createMatrix(unsigned int rows, unsigned int cols); @@ -13,7 +18,10 @@ void clearMatrix(Matrix *matrix); void setMatrixAt(MatrixType value, Matrix matrix, unsigned int rowIdx, unsigned int colIdx); MatrixType getMatrixAt(const Matrix matrix, unsigned int rowIdx, unsigned int colIdx); Matrix add(const Matrix matrix1, const Matrix matrix2); +Matrix broadcasting(const Matrix matrix1, const Matrix matrix2); Matrix multiply(const Matrix matrix1, const Matrix matrix2); +int rows(const Matrix matrix); +int cols(const Matrix matrix); #endif diff --git a/neuralNetwork.c b/neuralNetwork.c index bd8f164..5bec5b4 100644 --- a/neuralNetwork.c +++ b/neuralNetwork.c @@ -170,7 +170,7 @@ NeuralNetwork loadModel(const char *path) static Matrix imageBatchToMatrixOfImageVectors(const GrayScaleImage images[], unsigned int count) { - Matrix matrix = {NULL, 0, 0}; + Matrix matrix = {0,0, NULL}; if(count > 0 && images != NULL) { diff --git a/neuralNetwork.h b/neuralNetwork.h index 7f06607..78a6102 100644 --- a/neuralNetwork.h +++ b/neuralNetwork.h @@ -1,6 +1,8 @@ #ifndef NEURALNETWORK_H #define NEURALNETWORK_H +#define FILE_HEADER_STRING "__info2_neural_network_file_format__" + #include "imageInput.h" #include "matrix.h" diff --git a/neuralNetworkTests.c b/neuralNetworkTests.c index 21ab370..f87cee9 100644 --- a/neuralNetworkTests.c +++ b/neuralNetworkTests.c @@ -6,11 +6,44 @@ #include "neuralNetwork.h" +//Testdatei schreiben static void prepareNeuralNetworkFile(const char *path, const NeuralNetwork nn) { - // TODO +// Datei öffnen +FILE *file = fopen(path, "wb"); +if (file == NULL) +return; + +// Header schreiben +const char *fileTag = "__info2_neural_network_file_format__"; +fwrite(fileTag, 1, strlen(fileTag), file); + +// input Dimension schreiben +int inputDim = nn.layers[0].weights.cols; +fwrite(&inputDim, sizeof(int), 1, file); + +// für weiter Layer nur output Dimension schreiben +for (unsigned int i = 0; i < nn.numberOfLayers; i++) +{ + int outputDim = nn.layers[i].weights.rows; + fwrite(&outputDim, sizeof(int), 1, file); + + int weightCount = nn.layers[i].weights.rows * nn.layers[i].weights.cols; + fwrite(nn.layers[i].weights.buffer, sizeof(MatrixType), weightCount, file); + + int biasesCount = nn.layers[i].biases.rows * nn.layers[i].biases.cols; + fwrite(nn.layers[i].biases.buffer, sizeof(MatrixType), biasesCount, file); } +// Ende: loadModel liest 0 ein +int fileEnd = 0; +fwrite(&fileEnd, sizeof(int), 1, file); + +// Datei schließen +fclose(file); +} + + void test_loadModelReturnsCorrectNumberOfLayers(void) { const char *path = "some__nn_test_file.info2";