1

I'm trying to build an edge detection program using the code below. I have faced a variety of problems though, that I don't know how to solve.

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#define xrows 700
#define ycolumns 1244

int Gradient[xrows][ycolumns];
int Image_input[xrows][ycolumns];
int G_x[xrows][ycolumns];
int G_y[xrows][ycolumns];

int main() {
    FILE *fw = fopen("sobel_outt.txt", "w");
    FILE *fr = fopen("ny.txt", "r");

    int x, y, row, column, num;
    int i = 0;

    int XLENGTH = 700;
    int YLENGTH = 1244;

    for (row = 0; row < XLENGTH; row++) {
        for (column = 0; column < YLENGTH; column++) {
            fscanf(fr, "%d " ",", &num);
            Image_input[row][column] = num;
        }
    }

    fclose(fr);

    for (x = 0; x < XLENGTH; x += 3) {
        i++;
        for (y = 0; y < YLENGTH; y += 3) {
            if ((x == 0) || (x == XLENGTH - 1) || (y == 0) || (y == YLENGTH - 1)) {
                G_x[x][y] = G_y[x][y] = Gradient[x][y] = 0;
            } else {
                G_x[x][y] = Image_input[x + 1][y - 1]
                          + 2 * Image_input[x + 1][y]
                          + Image_input[x + 1][y + 1]
                          - Image_input[x - 1][y - 1]
                          - 2 * Image_input[x - 1][y]
                          - Image_input[x - 1][y + 1];

                G_y[x][y] = Image_input[x - 1][y + 1]
                          + 2 * Image_input[x][y + 1]
                          + Image_input[x + 1][y + 1]
                          - Image_input[x - 1][y - 1]
                          - 2 * Image_input[x][y - 1]
                          - Image_input[x + 1][y - 1];

                Gradient[x][y] = (abs(G_x[x][y]) + abs(G_y[x][y]));
                if (Gradient[x][y] > 255) {
                    Gradient[x][y] = 255;
                }
            }

            fprintf(fw, "%d,\n", Gradient[x][y]);
        }
    }

    printf("i= %d", i);

    fclose(fw);
    return 0;
}

The program seems to execute fine when run in the devcpp IDE and all of the matrices are declared as global variables. Whenever I declare them inside the main function, the program crashes.

I tried to run the program using Visual Studio, but I faced a couple more problems. I got some error messages stating that fscanf is ignored and fprintf is unsafe.

Last but not least I got another error, stating that I used up all of the stack memory available.

Any suggestions would be welcomed .

EDIT: Many of you suggested that I caused a stack overflow. I will try to use the heap memory as an alternative. My second problem still remains though.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    Almost all compilers put local variables on the *stack*. And the stack is a limited resource, on Windows it's only one single megabyte by default. Assuming 4-byte `int`, your arrays are around 13 megabytes. That won't fit. Variables defined outside of functions are placed somewhere else in memory, where you have much more space. – Some programmer dude May 14 '22 at 15:01
  • _The program seems to excecute fine when run in devcpp ide and all of the matrices are declared as global variables.Whenever i declare them inside the main function,the program crashes._ In the latter case, they end up on the stack. There is only a limited amount of stack space. On linux, the default stack size is 8MB, but on windows, IIRC, it's only 4MB You have: `700 * 1244 * 4 * 4` --> 13,923,800 bytes – Craig Estey May 14 '22 at 15:01

1 Answers1

1

Defining your matrices as local variables with automatic storage uses close to 14MB of stack space. This can definitely cause a stack overflow on many platforms. Allocating the data from the heap is recommended.

Microsoft's Visual C compiler is configured to complain about fscanf and fprintf and advocates using fscanf_s and fprintf_s instead. They managed to get this and other functions included the C Standard (Annex K) but the API was changed in subtle ways for consistency (using size_t instead of UINT for array lengths) and Microsoft did not change their version. This difference was unimportant for 32-bit targets but types size_t and unsigned now differ on most 64-bit platforms.

Using fscanf_s() is therefore not recommended for portable programs. You can disable the compiler warning by adding #define _CRT_SECURE_NO_WARNINGS before including <stdio.h>.

Note however that you should not ignore the return value of fscanf() to detect invalid or missing data: if the conversion fails the destination variable is unchanged, leading to incorrect results or even undefined behavior.

Here is a modified version allocating the matrices from the heap:

#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS  // disable warnings in fscanf
#endif

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ROWS 700
#define COLS 1244

int main() {
    int (*Gradient)[COLS] = calloc(sizeof(*Gradient), ROWS);
    int (*Image_input)[COLS] = calloc(sizeof(*Image_input), ROWS);
    int (*G_x)[COLS] = calloc(sizeof(*G_x), ROWS);
    int (*G_y)[COLS] = calloc(sizeof(*G_y), ROWS);

    if (!Gradient || !Image_input || !G_x || !G_y) {
        fprintf(stderr, "cannot allocate memory\n");
        return 1;
    }

    FILE *fr = fopen("ny.txt", "r");
    if (fr == NULL) {
        fprintf(stderr, "cannot open ny.txt: %s\n", strerror(errno));
        return 1;
    }
    FILE *fw = fopen("sobel_outt.txt", "w");
    if (fw == NULL) {
        fprintf(stderr, "cannot open sobel_outt.txt: %s\n", 
                strerror(errno));
        return 1;
    }

    int x, y, row, column, num;
    int i = 0;

    int XLENGTH = ROWS;
    int YLENGTH = COLS;

    for (row = 0; row < XLENGTH; row++) {
        for (column = 0; column < YLENGTH; column++) {
            if (fscanf(fr, "%d ,", &num) != 1) {
                fprintf(stderr, "cannot read value for Image_input[%d][%d]\n",
                        row, column);
                return 1;
            }
            Image_input[row][column] = num;
        }
    }

    fclose(fr);

    for (x = 0; x < XLENGTH; x += 3) {
        i++;
        for (y = 0; y < YLENGTH; y += 3) {
            if (x == 0 || x == XLENGTH - 1 || y == 0 || y == YLENGTH - 1) {
                G_x[x][y] = G_y[x][y] = Gradient[x][y] = 0;
            } else {
                G_x[x][y] = Image_input[x + 1][y - 1]
                          + 2 * Image_input[x + 1][y]
                          + Image_input[x + 1][y + 1]
                          - Image_input[x - 1][y - 1]
                          - 2 * Image_input[x - 1][y]
                          - Image_input[x - 1][y + 1];

                G_y[x][y] = Image_input[x - 1][y + 1]
                          + 2 * Image_input[x][y + 1]
                          + Image_input[x + 1][y + 1]
                          - Image_input[x - 1][y - 1]
                          - 2 * Image_input[x][y - 1]
                          - Image_input[x + 1][y - 1];

                Gradient[x][y] = abs(G_x[x][y]) + abs(G_y[x][y]);
                if (Gradient[x][y] > 255) {
                    Gradient[x][y] = 255;
                }
            }
            fprintf(fw, "%d,\n", Gradient[x][y]);
        }
    }

    fclose(fw);

    printf("i= %d\n", i);

    free(Gradient);
    free(Image_input);
    free(G_x);
    free(G_y);

    return 0;
}

Note that the final value of i should always be XLENGTH.

Here is an alternative using a single structure for all data, easier to handle than allocated 2D matrices:

#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ROWS 700
#define COLS 1244

struct sobel {
    int Gradient[ROWS][COLS];
    int Image_input[ROWS][COLS];
    int G_x[ROWS][COLS];
    int G_y[ROWS][COLS];
};

int main() {
    struct sobel *data = (struct sobel *)calloc(sizeof(*data), 1);
    if (!data) {
        fprintf(stderr, "cannot allocate memory\n");
        return 1;
    }
    FILE *fr = fopen("ny.txt", "r");
    if (fr == NULL) {
        fprintf(stderr, "cannot open ny.txt: %s\n", strerror(errno));
        return 1;
    }
    FILE *fw = fopen("sobel_outt.txt", "w");
    if (fw == NULL) {
        fprintf(stderr, "cannot open sobel_outt.txt: %s\n", strerror(errno));
        return 1;
    }

    int x, y, row, column, num;
    int i = 0;

    int XLENGTH = ROWS;
    int YLENGTH = COLS;

    for (row = 0; row < XLENGTH; row++) {
        for (column = 0; column < YLENGTH; column++) {
            if (fscanf(fr, "%d " ",", &num) != 1) {
                fprintf(stderr, "cannot read value for Image_input[%d][%d]\n", row, column);
                return 1;
            }
            data->Image_input[row][column] = num;
        }
    }

    fclose(fr);

    for (x = 0; x < XLENGTH; x += 3) {
        i++;
        for (y = 0; y < YLENGTH; y += 3) {
            if (x == 0 || x == XLENGTH - 1 || y == 0 || y == YLENGTH - 1) {
                data->G_x[x][y] = data->G_y[x][y] = data->Gradient[x][y] = 0;
            } else {
                data->G_x[x][y] = data->Image_input[x + 1][y - 1]
                                + 2 * data->Image_input[x + 1][y]
                                + data->Image_input[x + 1][y + 1]
                                - data->Image_input[x - 1][y - 1]
                                - 2 * data->Image_input[x - 1][y]
                                - data->Image_input[x - 1][y + 1];

                data->G_y[x][y] = data->Image_input[x - 1][y + 1]
                                + 2 * data->Image_input[x][y + 1]
                                + data->Image_input[x + 1][y + 1]
                                - data->Image_input[x - 1][y - 1]
                                - 2 * data->Image_input[x][y - 1]
                                - data->Image_input[x + 1][y - 1];

                data->Gradient[x][y] = abs(data->G_x[x][y]) + abs(data->G_y[x][y]);
                if (data->Gradient[x][y] > 255) {
                    data->Gradient[x][y] = 255;
                }
            }
            fprintf(fw, "%d,\n", data->Gradient[x][y]);
        }
    }
    fclose(fw);

    printf("i= %d\n", i);

    free(data);
    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Hey!Just tried your code.It seems that there is something wrong with the dynamic matrices' definition.I get the following error. A value of type "void *" cannot be used to initialize an entity of type "int (*)[1244]" – AlexandrosS May 15 '22 at 10:16
  • @AlexandrosS: your compiler is not configured to compile C programs, but C++ instead. `void *` can be converted implicitly to any data type in C, unlike C++ where an explicit cast is required. The cast would be `(int (*)[COLS])`. – chqrlie May 15 '22 at 10:28
  • So, should i try something like (int(*)Gradient[COLS]) = calloc(sizeof(*Gradient), ROWS); ? Sorry for asking questions that might sound stupid, but it's the first time that i'm doing something like this . – AlexandrosS May 15 '22 at 10:56
  • 1
    @AlexandrosS: no, you should first try and coerce the Microsoft IDE to honor the **.c** file extension and compile your code as C, not C++. If you cannot do this, use `int (*Gradient)[COLS] = (int (*)[COLS])calloc(sizeof(*Gradient), ROWS);` – chqrlie May 15 '22 at 11:12
  • @AlexandrosS: I posted an alternative using a single structure that is probably easier to understand than allocated 2D matrices. – chqrlie May 15 '22 at 11:20
  • @AlexandrosS: you can accept the answer by clicking on the grey checkmark below its score. – chqrlie May 15 '22 at 22:36