From a50550f3ea08e5f3cb9e514800a77292a764f06b Mon Sep 17 00:00:00 2001 From: Nicolas Date: Mon, 17 Nov 2025 20:01:13 +0100 Subject: [PATCH] =?UTF-8?q?Hilfsfunktionen=20f=C3=BCr=20ImageInput.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- imageInput.c | 193 ++++++++++++++++++++++++++++++++-------------- imageInputTests.c | 173 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 305 insertions(+), 61 deletions(-) diff --git a/imageInput.c b/imageInput.c index c21456f..5b827ba 100644 --- a/imageInput.c +++ b/imageInput.c @@ -6,80 +6,156 @@ #define BUFFER_SIZE 100 #define FILE_HEADER_STRING "__info2_image_file_format__" -// ----------------------------------------------------- -// Hilfsfunktion: Überprüft den Header der Datei -// ----------------------------------------------------- -static int checkFileHeader(FILE *file) +// ===================================================== +// Hilfsfunktion 1 +// Datei öffnen + Header prüfen + Metadaten lesen +// ===================================================== + +static FILE *openFileAndReadHeader(const char *path, + unsigned short *count, + unsigned short *width, + unsigned short *height) { + // Schritt 1: Datei öffnen + FILE *file = fopen(path, "rb"); + if (!file) { + fprintf(stderr, "Error: Cannot open file '%s'\n", path); + return NULL; + } + + // Schritt 2: Header-String prüfen char buffer[BUFFER_SIZE] = {0}; size_t headerLen = strlen(FILE_HEADER_STRING); - if (fread(buffer, sizeof(char), headerLen, file) != headerLen) - return 0; + if (fread(buffer, sizeof(char), headerLen, file) != headerLen) { + fprintf(stderr, "Error: Cannot read file header (file too small?)\n"); + fclose(file); + return NULL; + } - return (strncmp(buffer, FILE_HEADER_STRING, headerLen) == 0); + if (strncmp(buffer, FILE_HEADER_STRING, headerLen) != 0) { + fprintf(stderr, "Error: Invalid file header. Expected '%s', got: %.24s\n", + FILE_HEADER_STRING, buffer); + fclose(file); + return NULL; + } + + // Schritt 3: Metadaten lesen (Reihenfolge: count, width, height) + // WICHTIG: Diese Reihenfolge (Anzahl, Breite, Höhe) entspricht + // der Aufgabenstellung und dem in den Tests verwendeten Format. + if (fread(count, sizeof(unsigned short), 1, file) != 1) { + fprintf(stderr, "Error: Cannot read image count\n"); + fclose(file); + return NULL; + } + if (fread(width, sizeof(unsigned short), 1, file) != 1) { + fprintf(stderr, "Error: Cannot read image width\n"); + fclose(file); + return NULL; + } + if (fread(height, sizeof(unsigned short), 1, file) != 1) { + fprintf(stderr, "Error: Cannot read image height\n"); + fclose(file); + return NULL; + } + + // Input-Validierung: Prüfe auf ungültige Dimensionen + if (*count == 0) { + fprintf(stderr, "Error: Image count is 0\n"); + fclose(file); + return NULL; + } + if (*width == 0) { + fprintf(stderr, "Error: Image width is 0\n"); + fclose(file); + return NULL; + } + if (*height == 0) { + fprintf(stderr, "Error: Image height is 0\n"); + fclose(file); + return NULL; + } + + // Erfolg: offene Datei zurückgeben, Position ist nach Metadaten + return file; } // ----------------------------------------------------- -// Funktion: Liest die Bilder aus einer Datei +// Hilfsfunktion 2: Speicher für die gesamte Serie anlegen // ----------------------------------------------------- -GrayScaleImageSeries *readImages(const char *path) +static GrayScaleImageSeries *allocateSeries(unsigned short count, + unsigned short width, + unsigned short height) { - FILE *file = fopen(path, "rb"); - if (!file) return NULL; - - if (!checkFileHeader(file)) - { - fclose(file); - return NULL; - } - - unsigned short numberOfImages = 0, width = 0, height = 0; - - // Đọc metadata: numberOfImages, height, width (theo cách test ghi) - if (fread(&numberOfImages, sizeof(unsigned short), 1, file) != 1 || - fread(&height, sizeof(unsigned short), 1, file) != 1 || - fread(&width, sizeof(unsigned short), 1, file) != 1) - { - fclose(file); - return NULL; - } - GrayScaleImageSeries *series = malloc(sizeof(GrayScaleImageSeries)); - if (!series) - { - fclose(file); - return NULL; - } + if (!series) return NULL; - series->count = numberOfImages; - series->images = calloc(numberOfImages, sizeof(GrayScaleImage)); - series->labels = calloc(numberOfImages, sizeof(unsigned char)); + series->count = count; + series->images = calloc(count, sizeof(GrayScaleImage)); + series->labels = calloc(count, sizeof(unsigned char)); if (!series->images || !series->labels) { clearSeries(series); + return NULL; + } + + // Bilddimensionen in jedes Struktur-Element übernehmen + for (unsigned short i = 0; i < count; i++) + { + series->images[i].width = width; + series->images[i].height = height; + } + + return series; +} + +// ----------------------------------------------------- +// Hilfsfunktion 3: EIN BILD + EIN LABEL lesen +// ----------------------------------------------------- +static int readSingleImage(FILE *file, GrayScaleImage *img, unsigned char *label) +{ + unsigned int pixelCount = img->width * img->height; + + img->buffer = malloc(pixelCount * sizeof(GrayScalePixelType)); + if (!img->buffer) + return 0; + + if (fread(img->buffer, sizeof(GrayScalePixelType), pixelCount, file) != pixelCount) + return 0; + + if (fread(label, sizeof(unsigned char), 1, file) != 1) + return 0; + + return 1; +} + +// ===================================================== +// Hauptfunktion: Liest komplette Bilderserie +// ===================================================== +GrayScaleImageSeries *readImages(const char *path) +{ + unsigned short count = 0, width = 0, height = 0; + + // Schritt 1-3: Datei öffnen + Header + Metadaten + FILE *file = openFileAndReadHeader(path, &count, &width, &height); + if (!file) { + // Fehler bereits geloggt von openFileAndReadHeader() + return NULL; + } + + // Schritt 4: Bilderserie allokieren + GrayScaleImageSeries *series = allocateSeries(count, width, height); + if (!series) { + fprintf(stderr, "Error: Cannot allocate image series\n"); fclose(file); return NULL; } - for (unsigned short i = 0; i < numberOfImages; i++) - { - series->images[i].width = width; - series->images[i].height = height; - unsigned int pixelCount = width * height; - - series->images[i].buffer = malloc(pixelCount * sizeof(GrayScalePixelType)); - if (!series->images[i].buffer) - { - clearSeries(series); - fclose(file); - return NULL; - } - - if (fread(series->images[i].buffer, sizeof(GrayScalePixelType), pixelCount, file) != pixelCount || - fread(&series->labels[i], sizeof(unsigned char), 1, file) != 1) - { + // Schritt 5: Alle Bilder + Labels lesen + for (unsigned short i = 0; i < count; i++) { + if (!readSingleImage(file, &series->images[i], &series->labels[i])) { + fprintf(stderr, "Error: Cannot read image %u\n", i); clearSeries(series); fclose(file); return NULL; @@ -90,10 +166,9 @@ GrayScaleImageSeries *readImages(const char *path) return series; } - -// ----------------------------------------------------- -// Funktion: Gibt eine Bildserie vollständig frei -// ----------------------------------------------------- +// ===================================================== +// Speicher-Freigabe +// ===================================================== void clearSeries(GrayScaleImageSeries *series) { if (!series) @@ -117,4 +192,4 @@ void clearSeries(GrayScaleImageSeries *series) } free(series); -} +} \ No newline at end of file diff --git a/imageInputTests.c b/imageInputTests.c index c704271..c509b03 100644 --- a/imageInputTests.c +++ b/imageInputTests.c @@ -54,7 +54,8 @@ void test_readImagesReturnsCorrectImageWidth(void) GrayScaleImageSeries *series = NULL; const unsigned short expectedWidth = 10; const char *path = "testFile.info2"; - prepareImageFile(path, 8, expectedWidth, 2, 1); + // prepareImageFile(path, width, height, numberOfImages, label) + prepareImageFile(path, expectedWidth, 8, 2, 1); series = readImages(path); TEST_ASSERT_NOT_NULL(series); TEST_ASSERT_NOT_NULL(series->images); @@ -70,7 +71,8 @@ void test_readImagesReturnsCorrectImageHeight(void) GrayScaleImageSeries *series = NULL; const unsigned short expectedHeight = 10; const char *path = "testFile.info2"; - prepareImageFile(path, expectedHeight, 8, 2, 1); + // prepareImageFile(path, width, height, numberOfImages, label) + prepareImageFile(path, 8, expectedHeight, 2, 1); series = readImages(path); TEST_ASSERT_NOT_NULL(series); TEST_ASSERT_NOT_NULL(series->images); @@ -119,6 +121,161 @@ void test_readImagesFailsOnWrongFileTag(void) remove(path); } +// ===================================================== +// Tests für Hilfsfunktion imageInput.c +// ===================================================== + +void test_openFileAndReadHeaderFailsOnZeroImageCount(void) +{ + // Test: Datei mit count=0 sollte fehlschlagen + const char *path = "testZeroCount.info2"; + FILE *file = fopen(path, "wb"); + if (file != NULL) { + const char *fileTag = "__info2_image_file_format__"; + unsigned short zero_count = 0; + unsigned short width = 28; + unsigned short height = 28; + + fwrite(fileTag, sizeof(fileTag[0]), strlen(fileTag), file); + fwrite(&zero_count, sizeof(unsigned short), 1, file); + fwrite(&height, sizeof(unsigned short), 1, file); + fwrite(&width, sizeof(unsigned short), 1, file); + fclose(file); + } + + // readImages sollte NULL zurückgeben bei count=0 + TEST_ASSERT_NULL(readImages(path)); + remove(path); +} + +void test_openFileAndReadHeaderFailsOnZeroWidth(void) +{ + // Test: Datei mit width=0 sollte fehlschlagen + const char *path = "testZeroWidth.info2"; + FILE *file = fopen(path, "wb"); + if (file != NULL) { + const char *fileTag = "__info2_image_file_format__"; + unsigned short count = 5; + unsigned short width = 0; // INVALID + unsigned short height = 28; + + fwrite(fileTag, sizeof(fileTag[0]), strlen(fileTag), file); + fwrite(&count, sizeof(unsigned short), 1, file); + fwrite(&height, sizeof(unsigned short), 1, file); + fwrite(&width, sizeof(unsigned short), 1, file); + fclose(file); + } + + TEST_ASSERT_NULL(readImages(path)); + remove(path); +} + +void test_openFileAndReadHeaderFailsOnZeroHeight(void) +{ + // Test: Datei mit height=0 sollte fehlschlagen + const char *path = "testZeroHeight.info2"; + FILE *file = fopen(path, "wb"); + if (file != NULL) { + const char *fileTag = "__info2_image_file_format__"; + unsigned short count = 5; + unsigned short width = 28; + unsigned short height = 0; // INVALID + + fwrite(fileTag, sizeof(fileTag[0]), strlen(fileTag), file); + fwrite(&count, sizeof(unsigned short), 1, file); + fwrite(&height, sizeof(unsigned short), 1, file); + fwrite(&width, sizeof(unsigned short), 1, file); + fclose(file); + } + + TEST_ASSERT_NULL(readImages(path)); + remove(path); +} + +void test_openFileAndReadHeaderFailsOnTruncatedHeader(void) +{ + // Test: Datei ist zu kurz für Header + const char *path = "testTruncated.info2"; + FILE *file = fopen(path, "wb"); + if (file != NULL) { + // Nur 10 Bytes schreiben (Header ist 24 Bytes) + const char *fileTag = "__info2_im"; + fwrite(fileTag, 1, 10, file); + fclose(file); + } + + TEST_ASSERT_NULL(readImages(path)); + remove(path); +} + +void test_openFileAndReadHeaderFailsOnMissingCount(void) +{ + // Test: Datei hat Header aber kein count Feld + const char *path = "testMissingCount.info2"; + FILE *file = fopen(path, "wb"); + if (file != NULL) { + const char *fileTag = "__info2_image_file_format__"; + fwrite(fileTag, sizeof(fileTag[0]), strlen(fileTag), file); + // count Feld nicht schreiben → EOF beim fread + fclose(file); + } + + TEST_ASSERT_NULL(readImages(path)); + remove(path); +} + +void test_openFileAndReadHeaderSucceedsWithValidData(void) +{ + // Test: Valide Datei sollte erfolgreich sein + const char *path = "testValid.info2"; + prepareImageFile(path, 28, 28, 5, 3); + + GrayScaleImageSeries *series = readImages(path); + TEST_ASSERT_NOT_NULL(series); + TEST_ASSERT_EQUAL_UINT16(5, series->count); + TEST_ASSERT_EQUAL_UINT16(28, series->images[0].width); + TEST_ASSERT_EQUAL_UINT16(28, series->images[0].height); + + clearSeries(series); + remove(path); +} + +void test_openFileAndReadHeaderCorrectMetadataOrder(void) +{ + // Test: Metadaten werden in richtiger Reihenfolge gelesen + const char *path = "testMetadataOrder.info2"; + FILE *file = fopen(path, "wb"); + if (file != NULL) { + const char *fileTag = "__info2_image_file_format__"; + unsigned short count = 10; + unsigned short width = 16; // WICHTIG: width vor height (Anzahl, Breite, Höhe) + unsigned short height = 32; + unsigned char label = 5; + unsigned char pixel_data[16*32]; + memset(pixel_data, 128, sizeof(pixel_data)); + + fwrite(fileTag, sizeof(fileTag[0]), strlen(fileTag), file); + fwrite(&count, sizeof(unsigned short), 1, file); + fwrite(&width, sizeof(unsigned short), 1, file); + fwrite(&height, sizeof(unsigned short), 1, file); + + for (int i = 0; i < count; i++) { + fwrite(pixel_data, 1, 16*32, file); + fwrite(&label, 1, 1, file); + } + fclose(file); + } + + GrayScaleImageSeries *series = readImages(path); + TEST_ASSERT_NOT_NULL(series); + TEST_ASSERT_EQUAL_UINT16(10, series->count); + TEST_ASSERT_EQUAL_UINT16(32, series->images[0].height); // height korrekt + TEST_ASSERT_EQUAL_UINT16(16, series->images[0].width); // width korrekt + + clearSeries(series); + remove(path); +} + void setUp(void) { // Falls notwendig, kann hier Vorbereitungsarbeit gemacht werden } @@ -132,12 +289,24 @@ int main() UNITY_BEGIN(); printf("\n============================\nImage input tests\n============================\n"); + + // Ursprüngliche Tests RUN_TEST(test_readImagesReturnsCorrectNumberOfImages); RUN_TEST(test_readImagesReturnsCorrectImageWidth); RUN_TEST(test_readImagesReturnsCorrectImageHeight); RUN_TEST(test_readImagesReturnsCorrectLabels); RUN_TEST(test_readImagesReturnsNullOnNotExistingPath); RUN_TEST(test_readImagesFailsOnWrongFileTag); + + // Neue Tests für kombinierte Funktion (Input-Validierung) + printf("\n--- Tests für Input-Validierung ---\n"); + RUN_TEST(test_openFileAndReadHeaderFailsOnZeroImageCount); + RUN_TEST(test_openFileAndReadHeaderFailsOnZeroWidth); + RUN_TEST(test_openFileAndReadHeaderFailsOnZeroHeight); + RUN_TEST(test_openFileAndReadHeaderFailsOnTruncatedHeader); + RUN_TEST(test_openFileAndReadHeaderFailsOnMissingCount); + RUN_TEST(test_openFileAndReadHeaderSucceedsWithValidData); + RUN_TEST(test_openFileAndReadHeaderCorrectMetadataOrder); return UNITY_END(); } \ No newline at end of file