#include #include #include #include "imageInput.h" #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 static FILE* openFileCheckHeader(const char *path) { FILE *fp = NULL; fp = fopen(path, "rb"); if(fp == NULL) return NULL; char headerbuffer[BUFFER_SIZE]; size_t headerlength = strlen(FILE_HEADER_STRING); if((fread(headerbuffer, sizeof(char), headerlength, fp)) < headerbuffer) { fclose(fp); return NULL; } if(strncmp(FILE_HEADER_STRING, headerbuffer, headerlength) != 0) { flcose(fp); return NULL; } return fp; }//Oeffnet die Datei. 2. liest aus Datei headerlaenge laenge aus speichert in array vergleicht mit gewolltem header. ///Gibt bei Erfolg (header laenge richtig, richtiger header) den FILE* Zeiger zurück welcher nun gespeichert hat ///dass nun der header Teil gelesen wurde und somit die nächste Funktion ab dieser Stelle weiter liest. ///NULL, wenn falscher header. // TODO Vervollständigen Sie die Funktion readImages unter Benutzung Ihrer Hilfsfunktionen static int readWidthHeightCount(FILE *fp, unsigned int *count, unsigned int *width, unsigned int *height) { unsigned short count_s; unsigned short width_s; unsigned short height_s; if(fread(&count_s, sizeof(unsigned short), 1, fp) != 1) { return -1; } if(fread(&width_s, sizeof(unsigned short), 1, fp) != 1) { return -1; } if(fread(&height_s, sizeof(unsigned short), 1, fp) != 1) { return -1; } *count = (unsigned int)count_s; *width = (unsigned int)width_s; *height = (unsigned int)height_s; return 0; } static GrayScaleImageSeries* allocateSeries(unsigned int count) { // 1. Speicher reservieren für Hauptstruktur. Speicheradresse dieses Speicherbereichs einem Strukt ptr zuweisen GrayScaleImageSeries *series = malloc(sizeof(GrayScaleImageSeries)); // 2. Prüfen, ob Speicherreservierung nicht funktioniert hat if (series == NULL) { return NULL; } // 3. Initialiseren der Struktur. series->count = count; series->images = NULL; series->labels = NULL; // 4. Speicher reservieren für images Struktur array series->images = malloc(count * sizeof(GrayScaleImage));//Startadresse des images Array wird in die series gespeichert // 5. Prüfen, ob das images-Array nicht reserviert werden konnte if (series->images == NULL) { // Fehler! Rufen wir clearSeries auf, um aufzuräumen, was wir // bisher haben (nur die 'series'-Struktur selbst). clearSeries(series);//zu implementieren return NULL; } // 6. WICHTIG: Alle "buffer"-Zeiger in den "Seiten" initialisieren // Das ist der "sichere" Zustand für den nächsten Schritt. for (unsigned int i = 0; i < count; i++) { series->images[i].buffer = NULL; } // 7. "malloc" für das "labels"-Array series->labels = malloc(count * sizeof(unsigned char)); // 8. Prüfen, ob das "labels"-Array reserviert werden konnte if (series->labels == NULL) { // Fehler! clearSeries räumt jetzt die 'series'-Struktur // UND das 'images'-Array auf. clearSeries(series); return NULL; } // 9. Erfolg! Alle Container sind reserviert. return series; } /** * Liest die Pixel-Daten und Labels für alle Bilder in die vorreservierte Serie. * Reserviert den Speicher für jeden einzelnen Pixel-Buffer. * * @param fp Der geöffnete und bereits "vorgespulte" Datei-Zeiger. * @param series Die vorreservierte Serie (Hülle, images-Array, labels-Array). * @param width Die Breite, die jedes Bild hat. * @param height Die Höhe, die jedes Bild hat. * @return 0 bei Erfolg, -1 bei einem Speicher- oder Lesefehler. */ static int readImageData(FILE *fp, GrayScaleImageSeries *series, unsigned int width, unsigned int height) { // 1. Berechne die Anzahl der Pixel pro Bild (mach das nur einmal) const unsigned int numPixels = width * height; // 2. Gehe in einer Schleife durch jedes Bild (von 0 bis count-1) for (unsigned int i = 0; i < series->count; i++) { // 3. Setze die Metadaten für das aktuelle Bild series->images[i].width = width; series->images[i].height = height; // 4. "malloc" für den Pixel-Buffer DIESES EINEN Bildes series->images[i].buffer = malloc(numPixels * sizeof(GrayScalePixelType)); // 5. Prüfe auf Speicherfehler if (series->images[i].buffer == NULL) { // Fehler! Es gibt nicht genug Speicher. // Gib -1 zurück. Der "Chef" (readImages) muss clearSeries aufrufen. return -1; } // 6. Lies die Pixel-Daten aus der Datei in den neuen Buffer // Wir wollen 'numPixels' Blöcke lesen, die 'sizeof(GrayScalePixelType)' groß sind if (fread(series->images[i].buffer, sizeof(GrayScalePixelType), numPixels, fp) != numPixels) { // Fehler! Konnte nicht die erwartete Anzahl an Pixeln lesen. // (z.B. Datei endet zu früh) return -1; } // 7. Lies das Label für dieses Bild aus der Datei // Wir wollen 1 Block lesen, der 'sizeof(unsigned char)' groß ist if (fread(&series->labels[i], sizeof(unsigned char), 1, fp) != 1) { // Fehler! Konnte das Label nicht lesen. return -1; } } // Die Schleife geht zum nächsten Bild (i++) // 8. Wenn die Schleife komplett durchläuft, war alles erfolgreich. return 0; } /* readWidthHeightCount: liest die drei unsigned shorts ein, welche Hoehe, Breite und Anzahl sind. (SHORT!!) unsigned shorts werden zeigern übergeben, welche werte in unsigned int abspeichern. */ GrayScaleImageSeries *readImages(const char *path) { // --- Vorbereitung --- // Wir brauchen Variablen, um die Ergebnisse der Helfer zu speichern FILE *fp = NULL; GrayScaleImageSeries *series = NULL; unsigned int count = 0, width = 0, height = 0; // --- Schritt 1: Datei öffnen und Header prüfen --- fp = openAndValidateHeader(path); if (fp == NULL) { fprintf(stderr, "Fehler: Datei konnte nicht geöffnet oder validiert werden.\n"); return NULL; // Kein Aufräumen nötig, da nichts reserviert/geöffnet wurde } // --- Schritt 2: Metadaten (count, width, height) lesen --- if (readWidthHeightCount(fp, &count, &width, &height) != 0) { fprintf(stderr, "Fehler: Metadaten konnten nicht gelesen werden.\n"); fclose(fp); // Aufräumen: Datei schließen return NULL; } // --- Schritt 3: Speicher-Gerüst reservieren --- series = allocateSeries(count); if (series == NULL) { fprintf(stderr, "Fehler: Speicher konnte nicht reserviert werden.\n"); fclose(fp); // Aufräumen: Datei schließen return NULL; } // --- Schritt 4: Daten (Pixel & Labels) einlesen --- if (readImageData(fp, series, width, height) != 0) { fprintf(stderr, "Fehler: Bilddaten konnten nicht gelesen werden.\n"); clearSeries(series); // Aufräumen: Speicher freigeben fclose(fp); // Aufräumen: Datei schließen return NULL; } // --- Erfolg! --- // Alle Schritte waren erfolgreich. fclose(fp); // Datei schließen (wichtiger letzter Schritt) return series; // Den Zeiger auf die fertige Datenstruktur zurückgeben } // TODO Vervollständigen Sie die Funktion clearSeries, welche eine Bildserie vollständig aus dem Speicher freigibt void clearSeries(GrayScaleImageSeries *series) { }