Sobel.cpp

Description

Qt and GDI+ versions of the Sobel edge detection algorithm.

Requirements

Visual Studio, Qt, GDI+. Tested with Qt 3.4, but should work with Qt 4.x without too many troubles. CodeProject is a good place to find articles on using GDI+.

Details

Given image in source place Sobel edges in dest. This is what it looks like:

Copy the code below, or Download a zip.


/* Qt and GDI+ versions of the Sobel edge detection algorithm.
*/

// Qt version:
/* Given image in source place Sobel edges in dest.
Grayscale sort of, with (255,255,255) as brightest edge.
sobelDestination should be same size and depth as source.
*/
void ImageManipQt::Sobel
    (QImage     &   source,
     QImage     &   sobelDestination
    )
    {
    int          GX[3][3];
    int          GY[3][3];
    /* 3x3 GX Sobel mask.  Ref: www.cee.hw.ac.uk/hipr/html/sobel.html */
    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;

    /* 3x3 GY Sobel mask.  Ref: www.cee.hw.ac.uk/hipr/html/sobel.html */
    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;

    int      width = source.width();
    int      height = source.height();
    uint    blackPixel = qRgb(0, 0, 0);
    uchar   **jumper = source.jumpTable();
    uchar   **destJumper = sobelDestination.jumpTable();

    int      I, J;
    long sumX, sumY;
    int      SUM;
    uint    rawColour;

    for (int y = 0; y < height; ++y)
        {
        uint *p  = (uint *)jumper[y];
        uint *dp = (uint *)destJumper[y];
        for (int x = 0; x < width; ++x)
            {
            if ( y == 0 || y >= height-1
              || x == 0 || x >= width-1 )
                {
                SUM = 0;
                }
            else
                {
                sumX = 0;
                sumY = 0;
                /*-------X and Y GRADIENT APPROXIMATION------*/
                for(I=-1; I<=1; I++)
                    {
                    for(J=-1; J<=1; J++)
                        {
                        rawColour = *((uint *)jumper[y + J] + x + I);
                        sumX =
sumX + ((qRed(rawColour) + qGreen(rawColour) + qBlue(rawColour))/3) * GX[I+1][J+1];
                        sumY =
sumY + ((qRed(rawColour) + qGreen(rawColour) + qBlue(rawColour))/3) * GY[I+1][J+1];
//sumX = sumX +(int)( (*(originalImage.data + X + I + (Y + J)*originalImage.cols)) * GX[I+1][J+1]);
//sumY = sumY + (int)( (*(originalImage.data + X + I + (Y + J)*originalImage.cols)) * GY[I+1][J+1]);
                        }
                    }
                SUM = abs(sumX) + abs(sumY); /*---GRADIENT MAGNITUDE APPROXIMATION (Myler p.218)----*/
                if (SUM > 255)
                    SUM = 255;

                }
            *dp = qRgb(SUM, SUM, SUM);
            ++p;
            ++dp;
            }
        }
    }

// GDI+ version:
#define SOURCE_PIXEL_PTR(xs,ys) (pPixelSrc + ys*dLineSrc + xs*dPixelSrc)
void ImageManipGDIP::Sobel(Bitmap *source, Bitmap *sobelDestination)
    {
    int          GX[3][3];
    int          GY[3][3];
    /* 3x3 GX Sobel mask.  Ref: www.cee.hw.ac.uk/hipr/html/sobel.html */
    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;

    /* 3x3 GY Sobel mask.  Ref: www.cee.hw.ac.uk/hipr/html/sobel.html */
    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;

    const int nPlanes = 4; // NOTE we assume alpha plane is the 4th plane.
    Rect        rc(0, 0, source->GetWidth(), source->GetHeight());
    // LockBits on source
    BitmapData dataSrc;
    Status s = source->LockBits(& rc, ImageLockModeRead, PixelFormat32bppARGB, & dataSrc);
    if (s == Ok)
        {
        BitmapData dataDest;
        s = sobelDestination->LockBits(& rc, ImageLockModeRead | ImageLockModeWrite,
            PixelFormat32bppARGB, & dataDest);
        if (s == Ok)
            {
            BYTE * pStartSrc = (BYTE *) dataSrc.Scan0;
            BYTE * pStartDest = (BYTE *) dataDest.Scan0;

            UINT nLines = dataDest.Height;       // number of lines
            UINT nPixels = dataDest.Width;       // number of pixels per line
            UINT dPixelSrc = nPlanes;            // pixel step in source
            UINT dPixelDest = nPlanes;           // pixel step in destination
            UINT dLineSrc = dataSrc.Stride;      // line step in source
            UINT dLineDest = dataDest.Stride;    // line step in destination

            BYTE * pLineSrc = pStartSrc;
            BYTE * pLineDest = pStartDest;

            int      SUM;

            for (UINT y = 0; y < nLines; y++)    // loop through lines
                {
                BYTE    *pPixelSrc = pLineSrc;
                BYTE    *pPixelDest = pLineDest;

                for (UINT x = 0; x < nPixels; x++) // loop through pixels on line
                    {
                    if ( y == 0 || y >= nLines-1
                        || x == 0 || x >= nPixels-1 )
                        {
                        SUM = 0;
                        }
                    else
                        {
                        long sumX = 0;
                        long sumY = 0;
                        /*-------X and Y GRADIENT APPROXIMATION------*/
                        for(int I=-1; I<=1; I++)
                            {
                            for(int J=-1; J<=1; J++)
                                {
                                BYTE    *curP = SOURCE_PIXEL_PTR(I,J);
                                // GDI+ byte order is B G R A.
                                BYTE    b = *curP;
                                BYTE    g = *(curP+1);
                                BYTE    r = *(curP+2);
                                sumX += ((r+g+b)/3) * GX[I+1][J+1];
                                sumY += ((r+g+b)/3) * GY[I+1][J+1];
                                }
                            }

                        SUM = abs(sumX) + abs(sumY); /*---GRADIENT MAGNITUDE APPROXIMATION (Myler p.218)----*/
                        if (SUM > 255)
                            SUM = 255;
                        }

                    *(pPixelDest) = BYTE(SUM); // SUM
                    *(pPixelDest+1) = BYTE(SUM);
                    *(pPixelDest+2) = BYTE(SUM);
                    *(pPixelDest+3) = 255;

                    pPixelSrc += dPixelSrc;
                    pPixelDest += dPixelDest;
                    }

                pLineSrc += dLineSrc;
                pLineDest += dLineDest;
                }
            sobelDestination->UnlockBits(&dataDest);
            }
        source->UnlockBits(&dataSrc);
        }
    }