#include #include #include #include "imageInput.h" #define BUFFER_SIZE 100 #define FILE_HEADER_STRING "__info2_image_file_format__" #define HEADER_LEN (sizeof(FILE_HEADER_STRING) - 1) //-1, um \0 Character rauszufiltern // TODO Implementieren Sie geeignete Hilfsfunktionen für das Lesen der Bildserie aus einer Datei static FILE *openImageFile(const char *path) { return fopen(path, "rb"); } static int ReadCheckHeader(FILE *fp) { char header[HEADER_LEN + 1]; //Array mit Platz für Header Länge + \0 Character -> String if(fread(header, 1, HEADER_LEN, fp) != HEADER_LEN) return 0; header[HEADER_LEN] = '\0'; // Nullterminator am Ende anhängen if(strcmp(header, FILE_HEADER_STRING) != 0) // wenn beide identische sind gibt cmp 0 zurück return 0; return 1; } static int readMetaData(FILE *fp, unsigned short *numImages, unsigned short *width, unsigned short *height) { if(fread(numImages, sizeof(unsigned short), 1, fp) != 1 || fread(width, sizeof(unsigned short), 1, fp) != 1 || fread(height, sizeof(unsigned short), 1, fp)!= 1) { return 0; } return 1; } static int checkDimensions(unsigned int width, unsigned int height) { int ok = 0; if(width > 0 && height > 0) return ok = 1; else return ok = 0; } static GrayScaleImageSeries *allocateSeriesStruct(unsigned int numImages) { GrayScaleImageSeries *series = malloc(sizeof(GrayScaleImageSeries)); if(!series) return NULL; series->count = numImages; //Speicher für die Pointer an sich belegen series->images = malloc(numImages * sizeof(GrayScaleImage)); //Speicher für alle Bild-Strukturen, nicht aber für die Pixel selbst (denn image ist nochmal ein Array selbst) series->labels = malloc(numImages * sizeof(unsigned char)); //Speicher für alle Labels, diese beiden zeilen in die funktion drüber mit rein //Prüfen, ob Speicher für die Arrays reserviert werden konnte, wenn nicht -> Freigabe if(series->images == NULL || series->labels == NULL){ free(series->images); //wenn trotzdem was reingeschrieben wurde, dann freigeben free(series->labels); free(series); return NULL; } return series; } static int AllocateCheckPixelData(GrayScaleImageSeries *series, unsigned int numImages, unsigned int width, unsigned int height) { for(unsigned int i = 0; i < numImages; i++){ series -> images[i].buffer = malloc(width * height); //Speicher für Pixeldaten selbst if(series->images[i].buffer == NULL){ for(unsigned int j = 0; j < i; j++){ free(series->images[j].buffer); //wenn Speicher nicht reserviert werden konnte, dann zuerst das Array mit Pixeldaten freigeben } free(series->images); //anschließend auch die Speicher für Bildstrukturen und Label-Arrays freigeben, weil zuvor für diese schon Speicher reserviert wurde free(series->labels); free(series); return 0; } } return 1; } static int ReadImagesAndLabels(FILE *fp, GrayScaleImageSeries *series, unsigned int numImages, unsigned int width, unsigned int height) { for(int i = 0; i < numImages; i++) { //Breite und Höhe werden gesetzt (GrayScaleImage) series -> images[i].width = width; series -> images[i].height = height; //Pixeldaten einlesen und Prüfen ob alle essentiellen Pixel eingelesen wurden if(fread(series -> images[i].buffer, 1, width * height, fp) != (width * height)) { clearSeries(series); return 0; } //Jedes Bild hat ein Label, was direkt danach eingelesen wird und auf Größe geprüft wird if(fread(&series -> labels[i], 1, 1, fp) != 1){ clearSeries(series); return 0; } } return 1; } // TODO Vervollständigen Sie die Funktion readImages unter Benutzung Ihrer Hilfsfunktionen GrayScaleImageSeries *readImages(const char *path)//funktionsnamen immer klein anfangen { GrayScaleImageSeries *series = NULL; //Datei im Binärmodus öffnen FILE *fp = openImageFile(path); //funktion weglassen weil sie keine zusätzlichen infos bringt if(!fp) return NULL; //Buffer zum Einlesen des Headers char header[HEADER_LEN]; //Prüfen, ob genau die HEADER_LEN Bytes eingelesen werden if(!ReadCheckHeader(fp)){ fclose(fp); return NULL; } //Anzahl der Bilder, Breite und Höhe einlesen unsigned short numImages, width, height; if(!readMetaData(fp, &numImages, &width, &height)){ fclose(fp); return NULL; } //Prüfen, ob gültige Länge oder Breite if(!checkDimensions(width, height)){ fclose(fp); return NULL; } //Speicher für die gesamte Bildserie reservieren (aber nur für die Struktur) series = allocateSeriesStruct(numImages); if(!series) { fclose(fp); return NULL; //Fehler bei Speicherreservierung } //Speicher für die Pixeldaten selbst reserviern und prüfen, ob er reserviert werden konnte if(!AllocateCheckPixelData(series, numImages, width, height)){ fclose(fp); return NULL; } //Einlesen der Pixeldaten und des zugehörigen Labels und prüfen auf Fehler if(!ReadImagesAndLabels(fp, series, numImages, width, height)){ fclose(fp); return NULL; } // funktionen anders aufteilen: erst für alles speicher holen und dann bild und label einlesen aber ist geschmackssache, ziel ist es ohne kommentare auskommen zu können fclose(fp); return series; } // TODO Vervollständigen Sie die Funktion clearSeries, welche eine Bildserie vollständig aus dem Speicher freigibt void clearSeries(GrayScaleImageSeries *series) { if (series == NULL){ return; } for(unsigned int i = 0; i < series->count; i++){ free(series->images[i].buffer); } free(series->images); free(series->labels); free(series); }