#StackBounty: #c# #image-processing #hough-transform Longest line detection using Hough Line Transform

Bounty: 100

I want to detect longest line in an image using Hough Transform.

Input image

enter image description here

Expected output

enter image description here

Present output

enter image description here

We can see that it detected the incorrect line.

Where, in the following code, should I look for the bug?

There is one catch though. The source code appears to produce correct output if I increase the threshold value from 50 to 150. But, to me, this doesn’t make any sense as increased threshold means excluding lowly voted lines.

.

Source Code

enter image description here

Here is the entire solution as a downloadable zipped file from DropBox.

HouughMap.cs

public class HoughMap
{
    private int[,] Map { get; set; }
    public int[,] Image { get; set; }

    public int PointsCount { get; set; }
    public int Width { get { return Map.GetLength(0); } }  
    public int Height { get { return Map.GetLength(1); } }

    public void Compute()
    {
        if (Image != null)
        {
            // get source image size
            int imgWidth = Image.GetLength(0);
            int imgHeight = Image.GetLength(1);

            int centerX = imgWidth / 2;
            int centerY = imgHeight / 2;

            int maxTheta = 180;
            int houghHeight = (int)(Math.Sqrt(2) * Math.Max(imgWidth, imgHeight)) / 2;
            int doubleHoughHeight = houghHeight * 2;
            int houghHeightHalf = houghHeight / 2;
            int houghWidthHalf = maxTheta / 2;

            Map = new int[doubleHoughHeight, maxTheta];

            // scanning through each (x,y) pixel of the image--+
            for (int y = 0; y < imgHeight; y++)                 //|
            {                                                //|
                for (int x = 0; x < imgWidth; x++)//<-------------+
                {
                    if (Image[x, y] != 0)//if a pixel is black, skip it.
                    {
                        // We are drawing some Sine waves.  
                        // It may vary from -90 to +90 degrees.
                        for (int theta = 0; theta < Map.GetLength(1); theta++)
                        {
                            double rad = theta * Math.PI / 180;
                            // respective radius value is computed
                            int rho = (int)(((x - centerX) * Math.Cos(rad)) + ((y - centerY) * Math.Sin(rad)));

                            // get rid of negative value
                            rho += houghHeight;

                            // if the radious value is between 
                            // 1 and twice the houghHeight 
                            if ((rho > 0) && (rho <= Map.GetLength(0)))
                            {
                                Map[rho, theta]++;
                                PointsCount++;
                            }
                        }
                    }
                }
            }
        }
    }

    public int this[int rho, int theta]
    {
        get
        {
            return Map[rho, theta];
        }
        set
        {
            Map[rho, theta] = value;
        }
    }

    public Bitmap ToBitmap()
    {
        int[,] normalized = Tools.Rescale(Map);
        return Tools.ToBitmap(normalized, PixelFormat.Format32bppRgb);
    }
}

HoughLineTransform.cs

public class Line
{
    public Point Start { get; set; }
    public Point End { get; set; }
    public int Length 
    {
        get 
        {
            return (int)Math.Sqrt(Math.Pow(End.X - Start.X, 2) + Math.Pow(End.Y - Start.Y, 2)); ;
        }
    }
    public Line()
    {

    }
    public Line(Point start, Point end)
    {
        Start = start;
        End = end;
    }
}

public class HoughLineTransform
{
    public HoughMap Accumulator { get; set; }

    public HoughLineTransform() {}

    public Line GetLongestLine()
    {
        List<Line> lines = GetLines(50);

        int maxIndex = 0;
        double maxLength = -1.0;

        for (int i = 0; i < lines.Count; i++)
        {
            if (maxLength < lines[i].Length)
            {
                maxIndex = i;
                maxLength = lines[i].Length;
            }
        }

        return lines[maxIndex];
    }

    public List<Line> GetLines(int threshold)
    {
        if (Accumulator == null)
        {
            throw new Exception("HoughMap is null");
        }

        int houghWidth = Accumulator.Width;
        int houghHeight = Accumulator.Height;
        int imageWidth = Accumulator.Image.GetLength(0);
        int imageHeight = Accumulator.Image.GetLength(1);

        List<Line> lines = new List<Line>();

        if (Accumulator == null)
            return lines;

        for (int rho = 0; rho < houghWidth; rho++)
        {
            for (int theta = 0; theta < houghHeight; theta++)
            {
                if ((int)Accumulator[rho, theta] > threshold)
                {
                    //Is this point a local maxima (9x9)
                    int peak = Accumulator[rho, theta];

                    for (int ly = -4; ly <= 4; ly++)
                    {
                        for (int lx = -4; lx <= 4; lx++)
                        {
                            if ((ly + rho >= 0 && ly + rho < houghWidth) && (lx + theta >= 0 && lx + theta < houghHeight))
                            {
                                if ((int)Accumulator[rho + ly, theta + lx] > peak)
                                {
                                    peak = Accumulator[rho + ly, theta + lx];
                                    ly = lx = 5;
                                }
                            }
                        }
                    }

                    if (peak > (int)Accumulator[rho, theta])
                        continue;

                    int x1, y1, x2, y2;
                    x1 = y1 = x2 = y2 = 0;

                    double rad = theta * Math.PI / 180;

                    if (theta >= 45 && theta <= 135)
                    {
                        //y = (r - x Math.Cos(t)) / Math.Sin(t)
                        x1 = 0;
                        y1 = (int)(((double)(rho - (houghWidth / 2)) - ((x1 - (imageWidth / 2)) * Math.Cos(rad))) / Math.Sin(rad) + (imageHeight / 2));
                        x2 = imageWidth - 0;
                        y2 = (int)(((double)(rho - (houghWidth / 2)) - ((x2 - (imageWidth / 2)) * Math.Cos(rad))) / Math.Sin(rad) + (imageHeight / 2));
                    }
                    else
                    {
                        //x = (r - y Math.Sin(t)) / Math.Cos(t);
                        y1 = 0;
                        x1 = (int)(((double)(rho - (houghWidth / 2)) - ((y1 - (imageHeight / 2)) * Math.Sin(rad))) / Math.Cos(rad) + (imageWidth / 2));
                        y2 = imageHeight - 0;
                        x2 = (int)(((double)(rho - (houghWidth / 2)) - ((y2 - (imageHeight / 2)) * Math.Sin(rad))) / Math.Cos(rad) + (imageWidth / 2));
                    }

                    lines.Add(new Line(new Point(x1, y1), new Point(x2, y2)));
                }
            }
        }

        return lines;
    }
}

Tools.cs

public class Tools
{
    public static int[,] ToInteger(Bitmap input)
    {
        int Width = input.Width;
        int Height = input.Height;

        int[,] array2d = new int[Width, Height];

        for (int y = 0; y < Height; y++)
        {
            for (int x = 0; x < Width; x++)
            {
                Color cl = input.GetPixel(x, y);

                int gray = (int)Convert.ChangeType(cl.R * 0.3 + cl.G * 0.59 + cl.B * 0.11, typeof(int));

                array2d[x, y] = gray;
            }
        }

        return array2d;
    }

    public static int[,] Rescale(int[,] image)
    {
        int [,] imageCopy = (int[,])image.Clone();

        int Width = imageCopy.GetLength(0);
        int Height = imageCopy.GetLength(1);

        int minVal = 0;
        int maxVal = 0;

        for (int j = 0; j < Height; j++)
        {
            for (int i = 0; i < Width; i++)
            {
                int conv = imageCopy[i, j];

                minVal = Math.Min(minVal, conv);
                maxVal = Math.Max(maxVal, conv);
            }
        }

        int minRange = 0;
        int maxRange = 255;

        int[,] array2d = new int[Width, Height];

        for (int j = 0; j < Height; j++)
        {
            for (int i = 0; i < Width; i++)
            {
                array2d[i, j] = ConstraintInt(imageCopy[i, j], minVal, maxVal, minRange, maxRange);
            }
        }

        return array2d;
    }

    private static int ConstraintInt(int value, int minVal, int maxVal, int minRange, int maxRange)
    {
        return (maxRange - minRange) * (value - minVal) / (maxVal - minVal) + minRange;
    }

    public static Bitmap ToBitmap(int[,] image, PixelFormat pixelFormat)
    {
        int[,] imageCopy = (int[,])image.Clone();

        int Width = imageCopy.GetLength(0);
        int Height = imageCopy.GetLength(1);

        Bitmap bitmap = new Bitmap(Width, Height, pixelFormat);

        for (int y = 0; y < Height; y++)
        {
            for (int x = 0; x < Width; x++)
            {
                int iii = imageCopy[x, y];

                Color clr = Color.FromArgb(iii, iii, iii);

                bitmap.SetPixel(x, y, clr);
            }
        }

        return bitmap;
    }
}

HoughLinesForm.cs

public partial class HoughLinesForm : Form
{
    public HoughLinesForm()
    {
        InitializeComponent();

        Bitmap image = (Bitmap)pictureBox1.Image as Bitmap;

        HoughMap map = new HoughMap();
        map.Image = Tools.ToInteger(image);
        map.Compute();

        pictureBox2.Image = map.ToBitmap();

        HoughLineTransform hough = new HoughLineTransform();
        hough.Accumulator = map;

        List<Line> lines = new List<Line>();
        lines.Add(hough.GetLongestLine());
        Bitmap newImage = new Bitmap(image, image.Width, image.Height);

        using (Graphics g = Graphics.FromImage(newImage))
        {
            Pen pen = new Pen(Color.Red);

            foreach (Line item in lines)
            {
                g.DrawLine(pen, item.Start, item.End);
            }
        }

        pictureBox3.Image = newImage;
    }
}


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.