-1

I am receiving a Segmentation fault (core dumped) error when trying to blur an image, but I cannot find out why. To achieve a blur, I loop through each element in the 2x2 image array. I then check each of the 9x9 squares around & including it - if they exist, their RGB values are added to a sum (sumRed, sumGreen, sumBlue) for each color. I also increment a counter called numPixel each time this is successful so I can average the RGB values at the end.

There are other parts of the code, but I am certain that this blur() function is causing the segfault. This is because when I comment out the body of the function, the segfault goes away.

However, within the function I do not see what is triggering the segfault. I don't think I'm going out of bound in an array, which has been the cause of most of my segfaults in the past. From commenting out certain portions of the code, I also gathered that memcpy() is not the cause of the error (or at least not the only cause).

There's also a custom header file, which includes definitions for BYTE and RGBTRIPLE:

typedef uint8_t  BYTE;
...
typedef struct
{
    BYTE  rgbtBlue;
    BYTE  rgbtGreen;
    BYTE  rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;

The actual code is:

// TODO: Blur image

void blur(int height, int width, RGBTRIPLE image[height][width])

{
    RGBTRIPLE new_image[height][width];
    BYTE sumRed, sumGreen, sumBlue;
    BYTE numPixels;

    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; w++)
        {
            sumRed = sumGreen = sumBlue = 0;
            numPixels = 0;
            // Check from 1 higher to 1 lower
            for (int h = i - 1; h <= i + 1; h++)
            {
                // Check from 1 left to 1 right
                for (int w = j - 1; w <= j + 1; j++)
                {
                    // If neither index is out of bound, add neighboring RGB values
                    if (0 <= h < height && 0 <= w < width)
                    {
                        sumRed += image[h][w].rgbtRed;
                        sumGreen += image[h][w].rgbtGreen;
                        sumBlue += image[h][w].rgbtBlue;
                        numPixels++;
                    }
                }
            }

            new_image[i][j].rgbtRed = (BYTE) sumRed / numPixels;
            new_image[i][j].rgbtGreen = (BYTE) sumGreen / numPixels;
            new_image[i][j].rgbtBlue = (BYTE) sumBlue / numPixels;
        }
    }

    memcpy(&image[0][0], &new_image[0][0], sizeof(image[0][0]) * height * width);
    return;
}
Fe2O3
  • 6,077
  • 2
  • 4
  • 20
DarthBaguette
  • 163
  • 1
  • 7
  • Apart from "integer division", 8-bit accumulators are likely to overflow and be incorrect. AND, you need two tests, not a single one when checking "betweenness" of values... Would you like more commentary? – Fe2O3 Sep 06 '22 at 03:47
  • 5
    `if (0 <= h < height && 0 <= w < width)` is not an appropriate logical statement. This evaluates to `(0 <= h) < height && (0 <= w) < width` and that is _definitely not_ what you want, since it is always true. You can discover this by stepping through the very first few iterations of your loop with a debugger. – paddy Sep 06 '22 at 03:48
  • 1
    'if (0 <= h < height && 0 <= w < width)' no, I'm pretty sure that is not what you want. C does not support such range comparison syntax. It's not illegal, it just won't do what you want. – Martin James Sep 06 '22 at 03:49
  • 3
    Did you open the core dump in a debugger? It will trivially tell you where it crashed. Without a [mre] it's hard to help you. – Allan Wind Sep 06 '22 at 03:52
  • Fixing the conditional indeed stopped the segfault. Thank you all; that solved the problem. – DarthBaguette Sep 06 '22 at 03:54
  • I'll also keep in mind using a minimal reproducible example for next time. – DarthBaguette Sep 06 '22 at 03:55
  • You'll still want to fix the tiny accumulators AND the integer division... – Fe2O3 Sep 06 '22 at 03:56
  • Have you tried to use a [*debugger*](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) to catch the crash, and locate when and where in your code it happens? What are the values of all involved variables at the location and time of the crash? – Some programmer dude Sep 06 '22 at 03:57
  • 3
    Some advice: When it comes to debugging an issue, you need to forget everything you think you know about your program, and isolate the cause. Anything is a suspect. The reason bugs exist in the first place is the developer made an invalid assumption or an error. You already isolated the `blur` function, and you could have ruled-out the `memcpy` by simply commenting it out. That leaves very few places to look for the problem. Divide and conquer strategy can be very effective and quick in bug hunting. You don't need your program to actually "work" correctly when you're tracking down a crash. – paddy Sep 06 '22 at 03:58
  • @Fe2O3 Can you explain what you mean about the perimeter pixels? I don't get why they would be unaccounted for, but it looks like you're right because it's not blurring properly when I run it. – DarthBaguette Sep 06 '22 at 04:01
  • @paddy Thank you for the advice, I'll keep it in mind going forward! – DarthBaguette Sep 06 '22 at 04:02
  • 1
    `for (int j = 0; j < width; w++)` ??? Increment w??? and below, j++ on the loop bounded by 'w'??? – Fe2O3 Sep 06 '22 at 04:05
  • for (int h = i - 1; h <= i + 1; h++) but you start i == 0. I suggest you use max and min for those bounds instead of the if statement. h = max(i -1, 0) and h < min(i + 1, height). – Allan Wind Sep 06 '22 at 05:08
  • Everything that @paddy says:) Often, you benefit from making some change that makes the program behave even worse,(especially with heisenbugs and intermittents), just so you can narrow down the range with the bug/s. – Martin James Sep 06 '22 at 06:09

1 Answers1

2

Be sure of your logic, not relying on braces to save the day. Use simple short names in "local context". "Ease of reading" trumps being "Overly explicit."

for (int h = 0; h < height; h++)
    for (int w = 0; w < width; w++) {
        // bigger accumulators, short names, declared & init'd locally
        uint16_t sumR = 0;
        uint16_t sumG = 0;
        uint16_t sumB = 0;
        int nPix = 0;

        for (int hO = -1; hO <= 1; hO++) // height offset range
            for (int wO = -1; wO <= 1; wO++) { // width offset range
                int indH = h + hO; // Simple!
                int indW = w + wO;

                if (0 <= indH && indH < height &&  0 <= indW && indW < width) {
                    RGBTRIPLE *p = &image[ indH ][ indW ]; // short alias
                    sumR += p->rgbtRed;
                    sumG += p->rgbtGreen;
                    sumB += p->rgbtBlue;
                    nPix++;
                }
            }

        new_image[i][j].rgbtRed =   (BYTE)( sumR / nPix );
        new_image[i][j].rgbtGreen = (BYTE)( sumG / nPix );
        new_image[i][j].rgbtBlue =  (BYTE)( sumB / nPix );
    }

/* memcpy....*/

I'm still uneasy with possible confusion between "Height/width" and "vertical/Horizontal".

Here's an alternative for the two inner loops. Don't bother to set-up the width if the height is out-of-frame...

// From -1 offset, examine >>3<< pixels: -1, 0, 1...
for( int ih = h-1, limH = ih+3; ih < limH; ih++ ) { // height range
    if( ih < 0 || height <= ih ) continue;
    for( int iw = w-1, limW = iw+3;  iw < limW; iw++) { // width range
        if( iw < 0 || width <= iw ) continue;

        RGBTRIPLE *p = &image[ ih ][ iw ]; // short alias
        sumR += p->rgbtRed;
        sumG += p->rgbtGreen;
        sumB += p->rgbtBlue;
        nPix++;
    }
}
Fe2O3
  • 6,077
  • 2
  • 4
  • 20