0
$\begingroup$

I have converted this Matlab code for Canny edge detection into C# given that the Matlab version gives out a good output.

The output from Matlab code is as follows:

And my C# version's outputs are as follows:

Gaussian blur output.

Left: Horizontal Sobel output; Right: Vertical Sobel output.

Left: using Spatial domain Convolution; Right: using FFT Convolution (slower).

As we can see, my C# version's outputs are inferior. I couldn't fix them by varying the high and low threshold values.

On which step should I consider applying an improvement?

.


Source Code

public class Canny
{
    public static double[,] Apply(double[,] image)
    {
        double dTresLo = 0;
        double dTresHi = 0;

        dTresLo = 0.075;
        dTresHi = 0.175;

        double[,] dGaussKernel = new double[,] 
            {   {2.0/159.0,  4.0/159.0,  5.0/159.0, 4.0/159.0, 2.0/159.0}, 
                {4.0/159.0,  9.0/159.0, 12.0/159.0, 9.0/159.0, 4.0/159.0},
                {5.0/159.0, 12.0/159.0, 15.0/159.0,12.0/159.0, 5.0/159.0},
                {4.0/159.0,  9.0/159.0, 12.0/159.0, 9.0/159.0, 4.0/159.0},
                {2.0/159.0,  4.0/159.0,  5.0/159.0, 4.0/159.0, 2.0/159.0} };

        double[,] dSobelKernelX = new double[,]{{1, 0, -1},
                             {2, 0, -2},
                             {1, 0, -1} };

        double[,] dSobelKernelY = new double[,]{{1,   2,   1},
                        {0,   0,   0},
                        {-1, -2,  -1}};

        double[,] dGausOutput = CannyHelper.LinearConvolutionSpatial(image, dGaussKernel);
        double[,] matSobelOutX = CannyHelper.LinearConvolutionSpatial(dGausOutput, dSobelKernelX);
        double[,] matSobelOutY = CannyHelper.LinearConvolutionSpatial(dGausOutput, dSobelKernelY);

        // Calculate directions/orientations
        // Adjustment for negative directions, making all directions positive
        double[,] matDirRad = new double[image.GetLength(0), image.GetLength(1)];
        for (int x = 0; x < matDirRad.GetLength(0); x++)
        {
            for (int y = 0; y < matDirRad.GetLength(1); y++)
            {
                double atan = (matSobelOutY[x, y] / matSobelOutX[x, y]) * 180 / Math.PI;

                if (atan < 0)
                {
                    matDirRad[x, y] = atan + 360;
                }
                else
                {
                    matDirRad[x, y] = atan;
                }
            }
        }

        // Adjusting directions to nearest 0, 45, 90, or 135 degree
        double[,] arah2 = new double[image.GetLength(0), image.GetLength(1)];
        for (int i = 0; i < arah2.GetLength(0); i++)
        {
            for (int j = 1; j < arah2.GetLength(1); j++)
            {
                if ((matDirRad[i, j] >= 0) && (matDirRad[i, j] < 22.5) || (matDirRad[i, j] >= 157.5) && (matDirRad[i, j] < 202.5) || (matDirRad[i, j] >= 337.5) && (matDirRad[i, j] <= 360))
                {
                    arah2[i, j] = 0;
                }
                else if ((matDirRad[i, j] >= 22.5) && (matDirRad[i, j] < 67.5) || (matDirRad[i, j] >= 202.5) && (matDirRad[i, j] < 247.5))
                {
                    arah2[i, j] = 45;
                }
                else if ((matDirRad[i, j] >= 67.5 && matDirRad[i, j] < 112.5) || (matDirRad[i, j] >= 247.5 && matDirRad[i, j] < 292.5))
                {
                    arah2[i, j] = 90;
                }
                else if ((matDirRad[i, j] >= 112.5 && matDirRad[i, j] < 157.5) || (matDirRad[i, j] >= 292.5 && matDirRad[i, j] < 337.5))
                {
                    arah2[i, j] = 135;
                }
            }
        }

        // Calculate magnitude
        double[,] matSobelImg = new double[image.GetLength(0), image.GetLength(1)];
        for (int x = 0; x < matSobelImg.GetLength(0); x++)
        {
            for (int y = 0; y < matSobelImg.GetLength(1); y++)
            {
                matSobelImg[x, y] = Math.Sqrt((matSobelOutX[x, y] * matSobelOutX[x, y]) + (matSobelOutY[x, y] * matSobelOutY[x, y]));
            }
        }

        // Non-Maximum Supression
        double[,] matSuppImg = new double[image.GetLength(0), image.GetLength(1)];
        for (int i = 1; i < matSuppImg.GetLength(0)-1; i++)
        {
            for (int j = 1; j < matSuppImg.GetLength(1)-1; j++)
            {
                if (arah2[i, j]==0)
                {
                    if (matSobelImg[i, j] == Math.Max(Math.Max(matSobelImg[i, j], matSobelImg[i, j + 1]), matSobelImg[i, j - 1]))
                    {
                        matSuppImg[i, j] = 1;
                    }
                    else
                    {
                        matSuppImg[i, j] = 0;
                    }
                }
                else if (arah2[i, j]==45)
                {
                    if(matSobelImg[i, j] == Math.Max(Math.Max(matSobelImg[i, j], matSobelImg[i+1,j-1]), matSobelImg[i-1,j+1]))
                    {
                        matSuppImg[i, j] = 1;
                    }
                    else
                    {
                        matSuppImg[i, j] = 0;
                    }
                }
                else if (arah2[i, j]==90)
                {
                    if(matSobelImg[i, j] == Math.Max(Math.Max(matSobelImg[i, j], matSobelImg[i+1,j]), matSobelImg[i-1,j]))
                    {        
                        matSuppImg[i, j] = 1;
                    }
                    else
                    {
                        matSuppImg[i, j] = 0;
                    }
                }
                else if (arah2[i, j]==135)
                {
                    if(matSobelImg[i, j] == Math.Max(Math.Max(matSobelImg[i, j], matSobelImg[i+1,j+1]), matSobelImg[i-1,j-1]))
                    {
                        matSuppImg[i, j] = 1;
                    }
                    else
                    {
                        matSuppImg[i, j] = 0;
                    }
                }
            }
        }

        for (int i = 0; i < matSuppImg.GetLength(0); i++)
        {
            for (int j = 0; j < matSuppImg.GetLength(1); j++)
            {
                matSuppImg[i, j] = matSuppImg[i,j] * matSobelImg[i,j];
            }
        }

        // Hysteresis Thresholding
        dTresLo = dTresLo * CannyHelper.Max(matSuppImg);
        dTresHi = dTresHi * CannyHelper.Max(matSuppImg);

        double[,] T_res = new double[image.GetLength(0), image.GetLength(1)];
        for (int i = 1; i < T_res.GetLength(0) - 1; i++)
        {
            for (int j = 1; j < T_res.GetLength(1) - 1; j++)
            {
                if (matSuppImg[i, j] < dTresLo)
                    T_res[i, j] = 0;
                else if (matSuppImg[i, j] > dTresHi)
                    T_res[i, j] = 1;
                //%Using 8-connected components
                else if (matSuppImg[i + 1, j] > dTresHi
                || matSuppImg[i - 1, j] > dTresHi
                || matSuppImg[i, j + 1] > dTresHi
                || matSuppImg[i, j - 1] > dTresHi
                || matSuppImg[i - 1, j - 1] > dTresHi
                || matSuppImg[i - 1, j + 1] > dTresHi
                || matSuppImg[i + 1, j + 1] > dTresHi
                || matSuppImg[i + 1, j - 1] > dTresHi)
                    T_res[i, j] = 1;
            }
        }

        return T_res;
    }
}

By the way, the following is my convolution function look like:

    public static double[,] ConvolutionSpatial(double[,] paddedImage, double[,] mask, double offset)
    {
        double min = 0.0;
        double max = 1.0;

        double factor = GetFactor(mask);

        int paddedImageWidth = paddedImage.GetLength(0);
        int paddedImageHeight = paddedImage.GetLength(1);

        int maskWidth = mask.GetLength(0);
        int maskHeight = mask.GetLength(1);

        int imageWidth = paddedImageWidth - maskWidth;
        int imageHeight = paddedImageHeight - maskHeight;

        double[,] convolve = new double[imageWidth, imageHeight];


        for (int x = 0; x < imageWidth; x++)
        {
            for (int y = 0; y < imageHeight; y++)
            {
                double sum = Sum(paddedImage, mask, x, y);

                convolve[x, y] = Math.Min(Math.Max((sum / factor) + offset, min), max);

                string str = string.Empty;
            }
        }

        return convolve;
    }

    public static double Sum(double[,] paddedImage1, double[,] mask1, int startX, int startY)
    {
        double sum = 0;

        int maskWidth = mask1.GetLength(0);
        int maskHeight = mask1.GetLength(1);


        for (int x = startX; x < (startX + maskWidth); x++)
        {
            for (int y = startY; y < (startY + maskHeight); y++)
            {
                double img = paddedImage1[x, y];
                double msk = mask1[maskWidth - x + startX - 1, maskHeight - y + startY - 1];
                sum = sum + (img * msk);
            }
        }

        return sum;
    }

    public static double GetFactor(double[,] kernel)
    {
        double sum = 0.0;

        int width = kernel.GetLength(0);
        int height = kernel.GetLength(1);

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {

                sum += kernel[x, y];
            }
        }

        return (sum == 0) ? 1 : sum;
    }

Does anyone sense anything wrong in this?

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy