没有缓冲区的 Sobel 边缘检测

Sobel Edge Detection without a buffer

对于嵌入式设计,我试图在不使用缓冲器的情况下在电路板上实现索贝尔边缘检测。即我直接从屏幕上阅读和写作。但是,我可以存储大约一两个图像宽度的数据以供以后引用。这是由于董事会规定的限制。但是我陷入了一些问题。无论我尝试使用 sobel 还是其他边缘检测算法,我收到的都是噪音。代码如下,大家有什么建议吗

版本 1

void sobelEdgeDetection2() {
    int GX[3][3];
    int GY[3][3];

    int sumX[3];
    int sumY[3];
    int SUM[3];
    int piX = 0;
    int piY = 0;
    //uint8_t R, G, B = 0;
    int I, J = 0;

    //UnpackedColour pixVal;
    uint16_t *buffer;
    // allocate space for even scan lines and odd scan lines
    buffer = new uint16_t[_gl->getWidth()];
    //buffer for previous line
    uint16_t *bufT;
    // allocate space for even scan lines and odd scan lines
    bufT = new uint16_t[_gl->getWidth()];

    // Masks //////////////////////////////////////
    //X//
    GX[0][0] = -1;
    GX[0][1] = 0;
    GX[0][2] = 1;
    GX[1][0] = -2;
    GX[1][1] = 0;
    GX[1][2] = 2;
    GX[2][0] = -1;
    GX[2][1] = 0;
    GX[2][2] = 1;
    //Y//
    GY[0][0] = 1;
    GY[0][1] = 2;
    GY[0][2] = 1;
    GY[1][0] = 0;
    GY[1][1] = 0;
    GY[1][2] = 0;
    GY[2][0] = -1;
    GY[2][1] = -2;
    GY[2][2] = -1;

    for (int Y = 0; Y < _gl->getHeight(); Y++) {
        for (int X = 0; X < _gl->getWidth(); X++) {
            sumX[0] = sumX[1] = sumX[2] = 0;
            sumY[0] = sumY[1] = sumY[2] = 0;

            if (Y == 0 || Y == _gl->getHeight() - 1) {
                SUM[0] = SUM[1] = SUM[2] = 0;
            } else if (X == 0 || X == _gl->getWidth() - 1) {
                SUM[0] = SUM[1] = SUM[2] = 0;
            } else {
                for (I = -1; I <= 1; I++) {
                    for (J = -1; J <= 1; J++) {
                        piX = J + X;
                        piY = I + Y;

                        pixel16 pix = getPixel(piX, piY);
                        uint8_t Red = pix.Red;
                        uint8_t Green = pix.Green;
                        uint8_t Blue = pix.Blue;

                        sumX[0] += (Red) * GX[J + 1][I + 1];
                        sumX[1] += (Green) * GX[J + 1][I + 1];
                        sumX[2] += (Blue) * GX[J + 1][I + 1];

                        sumY[0] += (Red) * GY[J + 1][I + 1];
                        sumY[1] += (Green) * GY[J + 1][I + 1];
                        sumY[2] += (Blue) * GY[J + 1][I + 1];
                    }
                }

                SUM[0] = abs(sumX[0]) + abs(sumY[0]);
                SUM[1] = abs(sumX[1]) + abs(sumY[1]);
                SUM[2] = abs(sumX[2]) + abs(sumY[2]);
            }
            if (SUM[0] > 255)
                SUM[0] = 255;
            if (SUM[0] < 0)
                SUM[0] = 0;

            if (SUM[1] > 255)
                SUM[1] = 255;
            if (SUM[1] < 0)
                SUM[1] = 0;

            if (SUM[2] > 255)
                SUM[2] = 255;
            if (SUM[2] < 0)
                SUM[2] = 0;

            int newPixel[3];
            newPixel[0] = (255 - ((unsigned char) (SUM[0])));
            newPixel[1] = (255 - ((unsigned char) (SUM[1])));
            newPixel[2] = (255 - ((unsigned char) (SUM[2])));

            pixel16 pix(newPixel[0], newPixel[1], newPixel[2]);
            buffer[X] = packColour(pix).packed565;
        }
        //Need to move cursor back
        // draw it
        this->paintRow(Point(0, Y), buffer, _gl->getWidth());
    }
    delete[] buffer;
}

版本 2

/**
     * https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/image-processing/edge_detection.html
     * 1 Iterate over every pixel in the image
     * 2 Apply the x gradient kernel
     * 3 Apply the y gradient kernel
     * 4 Find the length of the gradient using pythagoras' theorem
     * 5 Normalise the gradient length to the range 0-255
     * 6 Set the pixels to the new values
     */
    void sobelEdgeDetection4() {
        UnpackedColour colour;
        for (int x = 1; x < _gl->getWidth() - 1; x++) {
            for (int y = 1; y < _gl->getHeight() - 1; y++) {
                // initialise Gx and Gy to 0
                int Gx = 0;
                int Gy = 0;
                unsigned int intensity = 0;

                // Left column
                pixel16 pixel = this->getPixel(x - 1, y - 1);
                intensity = pixel.Red + pixel.Green + pixel.Blue;
                Gx += -intensity;
                Gy += -intensity;

                pixel = this->getPixel(x - 1, y);
                intensity = pixel.Red + pixel.Green + pixel.Blue;
                Gx += -2 * intensity;

                pixel = this->getPixel(x - 1, y + 1);
                intensity = pixel.Red + pixel.Green + pixel.Blue;
                Gx += -intensity;
                Gy += +intensity;

                // middle column
                pixel = this->getPixel(x, y - 1);
                intensity = pixel.Red + pixel.Green + pixel.Blue;
                Gy += -2 * intensity;

                pixel = this->getPixel(x, y + 1);
                intensity = pixel.Red + pixel.Green + pixel.Blue;
                Gy += +2 * intensity;

                // right column
                pixel = this->getPixel(x + 1, y - 1);
                intensity = pixel.Red + pixel.Green + pixel.Blue;
                Gx += +intensity;
                Gy += -intensity;

                pixel = this->getPixel(x + 1, y);
                intensity = pixel.Red + pixel.Green + pixel.Blue;
                Gx += +2 * intensity;

                pixel = this->getPixel(x + 1, y + 1);
                intensity = pixel.Red + pixel.Green + pixel.Blue;
                Gx += +intensity;
                Gy += +intensity;

                // calculate the gradient length
                unsigned int length = (unsigned int) sqrt(
                        (float) (Gx * Gx) + (float) (Gy * Gy));

                // normalise the length to 0 to 255
                length = length / 17;

                // draw the pixel on the edge image
                pixel16 pixel2(length,length,length);
                this->setPixel(x, y, pixel2);
            }
        }
    }

版本 3

// sobel map for the x axis
    const double _SOBEL_Gx[3][3] = { { -1.0, +0.0, +1.0 }, { -2.0, +0.0, +2.0 },
            { -1.0, +0.0, +1.0 } };
    // sobel map for the y axis
    const double _SOBEL_Gy[3][3] = { { +1.0, +2.0, +1.0 }, { +0.0, +0.0, +0.0 },
            { -1.0, -2.0, -1.0 } };
double get_sobel_gradient(int width, int height, int x, int y) {
        double sobel_gradient_x = 0, sobel_gradient_y = 0;
        int mx = 0, my = 0, sx = 0, sy = 0;

        for (mx = x; mx < x + 3; mx++) {
            sy = 0;
            for (my = y; my < y + 3; my++) {
                if (mx < width && my < height) {
                    //int r, g, b, idx;
                    int idx = (mx + width * my) * 3;

                    pixel16 pixVal = this->getPixel(idx);

                    //r = pixVal.Red;
                    //g = pixVal.Green;
                    //b = pixVal.Blue;
                    UnpackedColour col = this->packColour(pixVal);
                    sobel_gradient_x += col.packed565 * _SOBEL_Gx[sx][sy];
                    sobel_gradient_y += col.packed565 * _SOBEL_Gy[sx][sy];
                }
                sy++;
            }
            sx++;
        }

        return abs(sobel_gradient_x) + abs(sobel_gradient_y);
    }

    void sobelEdgeDetection3() {
        double threshold = 50000.0;
        UnpackedColour colour;
        for (int y = 0; y < _gl->getHeight(); y++) {
            for (int x = 0; x < _gl->getWidth(); x++) {
                if (get_sobel_gradient(_gl->getWidth(), _gl->getHeight(), x, y)
                        >= threshold) {
                    colour.packed565 = 0x0000;          //set white
                } else {
                    colour.packed565 = 0xFFFF;          //set black
                }
                this->setPixel(x, y, colour);
            }
        }
    }

对于版本 1,分配 2 个缓冲区后(只需使用 bufferbufT),创建 2 个指向当前行和上一行的指针,如下所示:

uint16_t *currentRow = buffer;
uint16_t *prevRow = bufT;

在行循环内,写入 currentRow 而不是 buffer:

pixel16 pix(newPixel[0], newPixel[1], newPixel[2]);
currentRow[X] = packColour(pix).packed565;

因为 Sobel 过滤器从前一行读取,所以在完成计算其后一行的过滤值之前,您不能覆盖该行。所以在循环的最后,也就是你当前调用 paintRow() 的地方,绘制上一行(如果存在的话),然后交换缓冲区,使当前行成为前一行,而前一行成为新的当前行(将在下一次通过循环时被覆盖)。在最后一行也绘制了当前行,否则不会绘制,因为外循环即将终止。

if(Y > 0) // draw the previous row if this is not the first row:
    this->paintRow(Point(0, Y-1), prevRow, _gl->getWidth());
if(Y == _gl->getHeight()-1) // draw the current row if it is the last:
    this->paintRow(Point(0, Y), currentRow, _gl->getWidth());
// swap row pointers:
uint16_t *temp = prevRow;
prevRow = currentRow;
currentRow = temp;

相同的策略应该适用于其他版本。