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/neuralNetworkTests.c b/neuralNetworkTests.c index 9be45e2..26551a9 100644 --- a/neuralNetworkTests.c +++ b/neuralNetworkTests.c @@ -4,44 +4,44 @@ #include #include "unity.h" #include "neuralNetwork.h" -//Dateischichten sind in neuralNetwork.h definiert -// Dateiname: __info2_neural_network_file_format__ +#define FILE_HEADER_STRING "__info2_neural_network_file_format__" + +//Testdatei schreiben static void prepareNeuralNetworkFile(const char *path, const NeuralNetwork nn) { - FILE *file = fopen(path, "wb"); - if (!file) return; +// Datei öffnen +FILE *file = fopen(path, "wb"); +if (file == NULL) +return; - // Header schreiben - fwrite(FILE_HEADER_STRING, 1, strlen(FILE_HEADER_STRING), file); +// Header inklusive Nullterminator schreiben +fwrite(FILE_HEADER_STRING, sizeof(char), strlen(FILE_HEADER_STRING), file); - // Anzahl Layer schreiben - uint32_t nLayers = nn.numberOfLayers; - fwrite(&nLayers, sizeof(uint32_t), 1, file); +for (unsigned int i = 0; i < nn.numberOfLayers; i++) +{ + const Layer *layer = &nn.layers[i]; - for (uint32_t i = 0; i < nLayers; i++) { - Layer layer = nn.layers[i]; + + int inputDim = layer->weights.cols; // Spalten + int outputDim = layer->weights.rows; // Zeilen - // Weights-Dimensionen - uint32_t rW = layer.weights.rows; - uint32_t cW = layer.weights.cols; - fwrite(&rW, sizeof(uint32_t), 1, file); - fwrite(&cW, sizeof(uint32_t), 1, file); + // Dimensionen schreiben + fwrite(&inputDim, sizeof(int), 1, file); + fwrite(&outputDim, sizeof(int), 1, file); - // Weights-Werte - fwrite(layer.weights.buffer, sizeof(MatrixType), rW * cW, file); + // Gewichtswerte schreiben + fwrite(layer->weights.buffer, sizeof(MatrixType), layer->weights.rows * layer->weights.cols, file); - // Bias-Dimensionen - uint32_t rB = layer.biases.rows; - uint32_t cB = layer.biases.cols; - fwrite(&rB, sizeof(uint32_t), 1, file); - fwrite(&cB, sizeof(uint32_t), 1, file); + // Biaswerte schreiben + fwrite(layer->biases.buffer, sizeof(MatrixType), layer->biases.rows * layer->biases.cols, file); +} + +int zero = 0; +fwrite(&zero, sizeof(int), 1, file); - // Bias-Werte - fwrite(layer.biases.buffer, sizeof(MatrixType), rB * cB, file); - } - - fclose(file); +// Datei schließen +fclose(file); }