Florian
Florian

Reputation: 11

My sobel operator seems to cut off and paste a section of the image to the beginning. Any help on how to avoid this?

Here is a grayscale BMP image as input This is my input for the sobel operator. A grayscale BMP image.

My output, however seems to get a bit weird. Output with sobel operator

The output seems to cut off a section of the image and puts it in the beginning.

Below is my code.

int sobeloperator(const char* inputimage, const char* outputimage)
{
    const int Gx[3][3] = {
        {-1, 0, 1},
        {-2, 0, 2},
        {-1, 0, 1}
    };

    const int Gy[3][3] = {
        {-1, -2, -1},
        {0, 0, 0},
        {1, 2, 1}
    };

    BMPHeader header;
    BMPInfoHeader infoHeader;
    uint8_t* inputPixels;
    uint8_t* outputPixels;

    ifstream inFile(inputimage, ios::in | ios::binary);
    if (!inFile.is_open()) {
        cerr << "Error: Unable to open input file." << endl;
        return 1;
    }

    // Read BMP header

    inFile.read(reinterpret_cast<char*>(&header), sizeof(header));
    if (header.type != 0x4D42) {
        cerr << "Error: Invalid BMP file format." << endl;
        return 1;
    }

    // Read BMP info header

    inFile.read(reinterpret_cast<char*>(&infoHeader), sizeof(infoHeader));

    if (infoHeader.bpp != 8) {
        cerr << "Error: Only 8-bit BMP images are supported." << endl;
        return 1;
    }

    char colors[1024] = { 0 };
    for (int i = 0; i < 256; i++)
    {
        colors[i * 4] = (char)i;
        colors[i * 4 + 1] = (char)i;
        colors[i * 4 + 2] = (char)i;
    }

    inputPixels = new uint8_t[infoHeader.width * infoHeader.height];
    outputPixels = new uint8_t[infoHeader.width * infoHeader.height];

    inFile.read(reinterpret_cast<char*>(inputPixels), infoHeader.width * infoHeader.height);

    for (int y = 0; y < infoHeader.height; y++)
    { 
        for (int x = 0; x < infoHeader.width; x++)
        {
            int GxSum = 0;
            int GySum = 0;
            for (int k = -1; k <= 1; k++) {
                for (int l = -1; l <= 1; l++) {
                    // get pixel value at current kernel position
                    int pos = (y + k) * infoHeader.width + (x + l); //current pos, infoHeader.width is a row, multiplied by y+k = current row.

                    if (pos < 0 || pos >= infoHeader.width * infoHeader.height || y <= 0 || y >= infoHeader.height || x <= 0 || x >= infoHeader.width) {
                        continue;
                    }

                    // add pixel value multiplied by Sobel kernel value to Gx and Gy sums
                    GxSum += Gx[k+1][l+1] * inputPixels[pos];
                    GySum += Gy[k+1][l+1] * inputPixels[pos];
                }
            }
            int gradientMagnitude = sqrt(GxSum * GxSum + GySum * GySum);

            // clip gradient magnitude to 8-bit range
            if (gradientMagnitude <= 128)
            {
                gradientMagnitude = 0;
            }
            else
            {
                gradientMagnitude = 255;
            }
            //gradientMagnitude = min(255, max(0, gradientMagnitude));
            
            // set output pixel to gradient magnitude
            
            outputPixels[y * infoHeader.width + x] = gradientMagnitude;
        }

    }
    
    infoHeader.sizeImage = infoHeader.width * infoHeader.height;
    header.size = sizeof(header) + sizeof(infoHeader) + sizeof(colors) + infoHeader.sizeImage;
    header.offset = sizeof(header) + sizeof(infoHeader) + sizeof(colors);
    printf("\n\nbpp = %d\n", infoHeader.bpp);
    printf("compression = %d", infoHeader.compression);
    // Write output BMP file
    ofstream outFile("sobel.bmp", ios::out | ios::binary);
    if (!outFile.is_open()) {
        cerr << "Error: Unable to create output file." << endl;
        return 1;
    }

    outFile.write(reinterpret_cast<char*>(&header), sizeof(header));
    outFile.write(reinterpret_cast<char*>(&infoHeader), sizeof(infoHeader));
    outFile.write(colors, 1024);
    outFile.write(reinterpret_cast<char*> (outputPixels), infoHeader.sizeImage);
    delete[] inputPixels;
    delete[] outputPixels;
    inFile.close();
    outFile.close();


    return 0;

}

I've reviewed the code multiple times, re-read on how to apply the sobel operator, reviewed the portion where I apply the sobel operator, but I can't seem to be able to find a solution for my problem.

Upvotes: 0

Views: 43

Answers (1)

Oersted
Oersted

Reputation: 2776

I would have preferred to comment instead of reply but i lack the reputation to do so.

Yet I think that your issue lies in "edge effects" : in your inner loop there

int pos = (y + k) * infoHeader.width + (x + l); //current pos, infoHeader.width is a row, multiplied by y+k = current row.

you are accessing pixels out of your image area.

In order to fix your problem there is many possibilities according to what you actually want to do. Basically you have to give values for pixels out of the image area. You may set a constant value (0 for instance), repeat the valid line/column but other strategies are possible. You may also choose to compute the output image only on valid pixels (you will have an output image smaller than the input one).

In terms of coding, there is also several possibilities:

  • extending the input image with some values and looping only on the original pixels
  • looping only on inner pixels and doing special loops for borders
  • looping only on inner pixels and getting a smaller image

You may find more mathematical insight about edge handling for instance there (though it does speak about strategy for setting values outside the image).

Upvotes: 0

Related Questions