Search code examples
ccs50edge-detectionsobel

cs50 pset4 - edge detection filter not working - sobel operator


The following code i have written has the purpose of detecting the edges in a pixel using the sobel operator. However, it fails all of the tests given by check50 (tool offered by cs50). The output image is also the exact same as the input.

before you continue reading, visit the pset's link

  • note: I'm supposed to form a 3x3 grid around the pixel I want to filter so that I can iterate over each value in the GX and GY values. I've used ints hh and ww to do this.
       // Detect edges
        void edges(int height, int width, RGBTRIPLE image[height][width])
        {
            int sqrtRedd;
            int sqrtGreenn;
            int sqrtBluee;
        
        //make copy of image
            RGBTRIPLE copy[height][width];
        
            for(int h = 0; h < height; h++)
            {
                for(int w = 0; w < width; w++)
                {
                    copy[h][w] = image[h][w];
                }
            }
        
        //loop through pixels
            for(int h = 0; h < height; h++)
            {
                for(int w = 0; w < width; w++)
                {
                    int GXred = 0;
                    int GYred = 0;
                    int GXgreen = 0;
                    int GYgreen = 0;
                    int GXblue = 0;
                    int GYblue = 0;
                    
                    for(int hh = -1; hh <= 1; hh++)
                    {
                        for(int ww = -1; ww <= 1; ww++)
                        {
                            if( h + hh >= 0 && h + hh < height && w + ww >= 0 && w + ww < width)
                            {
                                //form 3x3 grid
                            GXred += ww * copy[2 - hh * hh][2 - ww * ww].rgbtRed;
                            GYred += hh * copy[2 - hh * hh][2 - ww * ww].rgbtRed;
     
    
                       
                        GXgreen += ww * copy[2 - hh * hh][2 - ww * ww].rgbtGreen;
                        GYgreen += hh * copy[2 - hh * hh][2 - ww * ww].rgbtGreen;
                        
                        GXblue += ww * copy[2 - hh * hh][2 - ww * ww].rgbtBlue;
                        GXblue += hh * copy[2 - hh * hh][2 - ww * ww].rgbtBlue;
                        
                            }
                        }
                    }
                    
                    int red = round(sqrt(GXred * GXred + GYred * GYred));
                    int green = round(sqrt(GXgreen * GXgreen + GYgreen * GXgreen));
                    int blue = round(sqrt(GXblue * GXblue + GYblue * GYblue));
                    
                    if(red > 225)
                    {
                        red = 225;
                    }
                    
                    else if(green > 225)
                    {
                        green = 225;
                    }
                    
                    else if(blue > 225)
                    {
                        blue = 225
                    }
                    
                    image[h][w].rgbtRed = red;
                    image[h][w].rgbtGreen = green;
                    image[h][w].rgbtBlue = blue;
                    
                }
            }
                
            return;
        }

RGBTRIPLE:

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

These are the error messages given by check50:

    :( edges correctly filters middle pixel
        expected "210 150 60\n", not "143 105 30\n"
    :( edges correctly filters pixel on edge
        expected "213 228 255\n", not "164 144 79\n"
    :( edges correctly filters pixel in corner
        expected "76 117 255\n", not "58 77 64\n"
    :( edges correctly filters 3x3 image
        expected "76 117 255\n21...", not "58 77 64\n164 ..."
    :( edges correctly filters 4x4 image
        expected "76 117 255\n21...", not "58 77 64\n164 ..."

As you can see, the output values are way off and not even near what they should actually be. the problem is: I don't know if these errors are caused by a) my way of trying to find the GX and GY values from the kernels or b)by the way i'm applying the sobel operator.

I've tried finding GX and GY values in other ways (didn't work) such as:

                         if(hh == -1)
                         {
                            GYred += copy[h - 1][w + ww].rgbtRed * -1;
                            GYgreen += copy[h - 1][w + ww].rgbtGreen * -1;
                            GYblue += copy[h - 1][w + ww].rgbtBlue * -1;
                         }
                         
                         else if( hh == 0)
                         {
                             GYred += copy[h][w + ww].rgbtRed * 0;
                             GYgreen += copy[h][w + ww].rgbtGreen * 0;
                             GYblue += copy[h][w + ww].rgbtBlue * 0;
                         }
                         
                         else if(hh == 1)
                         {
                             GYred += copy[h + 1][w + ww].rgbtRed * 1;
                             GYgreen += copy[h + 1][w + ww].rgbtGreen * 1;
                             GYblue += copy[h + 1][w + ww].rgbtBlue * 2;
                         }
                         
                         else if(hh == 2)
                         {
                             GYred += copy[h + 2][w + ww].rgbtRed * 2;
                             GYgreen += copy[h + 2][w + ww].rgbtGreen * 2;
                             GYblue += copy[h + 2][w + ww].rgbtBlue * 2;
                         }
                         
    //start setting GX values
                         if(ww == -2)
                         {
                             GXred += copy[h + hh][w - 2].rgbtRed * -2;
                             GXgreen += copy[h + hh][w - 2].rgbtGreen * -2;
                             GXblue += copy[h + hh][w - 2].rgbtBlue * -2;
                         }
                         
                         else if(ww == -1)
                         {
                             GXred += copy[h + hh][w - 1].rgbtRed * -1;
                             GXgreen += copy[h + hh][w - 1].rgbtGreen * -1;
                             GXblue += copy[h + hh][w - 1].rgbtBlue * -1;
                         }
                         
                         else if(ww == 0)
                         {
                             GXred += copy[h + hh][w].rgbtRed * 0;
                             GXgreen += copy[h + hh][w].rgbtGreen * 0;
                             GXblue += copy[h + hh][w].rgbtBlue * 0;
                         }
                         
                         else if(ww == 1)
                         {
                             GXred += copy[h + hh][w + 1].rgbtRed * 1;
                             GXgreen += copy[h + hh][w + 1].rgbtGreen * 1;
                             GXblue += copy[h + hh][w + 1].rgbtBlue * 1;
                         }

I've been stuck on this pset for almost a week now and so at this point I don't know what else to try.


Solution

  • You have quite some parts that do not really make sense or are missing.

    • You do not apply the sobel factors anywhere. Just taking the offset inside your 3x3 grid does not yield the correct values,
    • You only limit 1 color channel in case of overflow,
    • You limit to 225 instead of 255,
    • You mixed GXblue and GYblue, same for GXgreen and GYgreen.

    I have prepared a new version that should do the trick. Now tested and with test data from initially failed test for 4x4 image.

    
    #include <stdio.h>
    #include <math.h>
    
    typedef unsigned char BYTE;
    
    typedef struct
    {
        BYTE  rgbtBlue;
        BYTE  rgbtGreen;
        BYTE  rgbtRed;
    } __attribute__((__packed__))
    RGBTRIPLE;
    
    // Detect edges
    void edges(int height, int width, RGBTRIPLE image[height][width])
    {
        int sqrtRedd;
        int sqrtGreenn;
        int sqrtBluee;
    
        //make copy of image
        RGBTRIPLE copy[height][width];
        for(int h = 0; h < height; h++)
        {
            for(int w = 0; w < width; w++)
            {
                copy[h][w] = image[h][w];
            }
        }
    
        //loop through pixels
        for(int h = 0; h < height; h++)
        {
            for(int w = 0; w < width; w++)
            {
                int GXred = 0;
                int GYred = 0;
                int GXgreen = 0;
                int GYgreen = 0;
                int GXblue = 0;
                int GYblue = 0;
                
                int index = 0;
                int factorsX[] = {-1,  0,  1, -2, 0, 2, -1, 0, 1};
                int factorsY[] = {-1, -2, -1,  0, 0, 0,  1, 2, 1};
    
                //form 3x3 grid
                for(int hh = -1; hh <= 1; hh++)
                {
                    for(int ww = -1; ww <= 1; ww++)
                    {
                        int x = w+ww;
                        int y = h+hh;
                        if( y >= 0 && y < height && x >= 0 && x < width)
                        {
                            GXred += factorsX[index] * copy[y][x].rgbtRed;
                            GYred += factorsY[index] * copy[y][x].rgbtRed;
    
                            GXgreen += factorsX[index] * copy[y][x].rgbtGreen;
                            GYgreen += factorsY[index] * copy[y][x].rgbtGreen;
    
                            GXblue += factorsX[index] * copy[y][x].rgbtBlue;
                            GYblue += factorsY[index] * copy[y][x].rgbtBlue;
                        }                   
                        index++;
                    }
                }
    
                int red = round(sqrt(GXred * GXred + GYred * GYred));
                int green = round(sqrt(GXgreen * GXgreen + GYgreen * GYgreen));
                int blue = round(sqrt(GXblue * GXblue + GYblue * GYblue));
    
                if(red > 255)
                {
                    red = 255;
                }
                if(green > 255)
                {
                    green = 255;
                }
                if(blue > 255)
                {
                    blue = 255;
                }
    
                image[h][w].rgbtRed = red;
                image[h][w].rgbtGreen = green;
                image[h][w].rgbtBlue = blue;
            }
        }
    
        return;
    }
    
    int main(void)
    {
        RGBTRIPLE test_4x4[4][4] = {
            {{0, 10, 25}, {0, 10, 30}, {40, 60, 80}, {50, 60, 80}},
            {{20, 30, 90}, {30, 40, 100}, {80, 70, 90}, {80, 80, 90}},
            {{20, 20, 40}, {30, 10, 30}, {50, 40, 10}, {50, 40, 100}},
            {{50, 20, 40}, {50, 20, 40}, {50, 40, 80}, {50, 40, 80}},
        };
    
        edges(4, 4, test_4x4);
        for(int h = 0; h < 4; h++)
        {
            for(int w = 0; w < 4; w++)
            {
                printf("%d %d %d\n", test_4x4[h][w].rgbtBlue, test_4x4[h][w].rgbtGreen, test_4x4[h][w].rgbtRed);
            }
        }
    
        return 0;
    }