霍夫线变换实现

Hough Line Transform implementation

我正在尝试实施 Hough Line Transform

Input. 我正在使用下面的图像作为输入。这条单线预计只会在输出中产生一个正弦波交点。

期望的行为。 我的源代码预计会产生以下输出,因为它是由 AForge 框架的 sample application 生成的。

这里,我们可以看到:

  1. 输出的尺寸与输入图像相同。
  2. 几乎在中心看到正弦波的交点。
  3. 波浪的相交模式很小很简单

当前行为。我的源代码生成以下输出,这与 AForge 生成的输出不同。

为什么我的代码产生不同的输出?

源代码

下面的代码是我自己写的。以下是a Minimal, Complete, and Verifiable源代码。

public class HoughMap
{
    public int[,] houghMap { get; private set; }
    public int[,] image { get; set; }

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

            int inWidthHalf = inWidth / 2;
            int inHeightHalf = inHeight / 2;

            int outWidth = (int)Math.Sqrt(inWidth * inWidth + inHeight * inHeight);
            int outHeight = 180;
            int outHeightHalf = outHeight / 2;

            houghMap = new int[outWidth, outHeight];

            // scanning through each (x,y) pixel of the image--+
            for (int y = 0; y < inHeight; y++)               //|
            {                                                //|
                for (int x = 0; x < inWidth; x++)//<-----------+
                {
                    if (image[x, y] != 0)//if a pixel is black, skip it.
                    {
                        // We are drawing some Sine waves. So, it may 
                        // vary from -90 to +90 degrees.
                        for (int theta = -outHeightHalf; theta < outHeightHalf; theta++)
                        {
                            double rad = theta * Math.PI / 180;
                            // respective radius value is computed
                            //int radius = (int)Math.Round(Math.Cos(rad) * (x - inWidthHalf) - Math.Sin(rad) * (y - inHeightHalf));
                            //int radius = (int)Math.Round(Math.Cos(rad) * (x + inWidthHalf) - Math.Sin(rad) * (y + inHeightHalf));
                            int radius = (int)Math.Round(Math.Cos(rad) * (x) - Math.Sin(rad) * (outHeight - y));

                            // if the radious value is between 1 and 
                            if ((radius > 0) && (radius <= outWidth))
                            {
                                houghMap[radius, theta + outHeightHalf]++;
                            }
                        }
                    }
                }
            }
        }
    }
}

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

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

        int[,] intImage = ToInteger(bitmap);

        HoughMap houghMap = new HoughMap();
        houghMap.image = intImage;
        houghMap.Compute();

        int[,] normalized = Rescale(houghMap.houghMap);

        Bitmap hough = ToBitmap(normalized, bitmap.PixelFormat);

        pictureBox2.Image = hough;
    }

    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++)
            {
                double conv = imageCopy[i, j];

                minVal = (int)Math.Min(minVal, conv);
                maxVal = (int)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] = (maxRange - minRange) * (imageCopy[i,j] - minVal) / (maxVal - minVal) + minRange;
            }
        }

        return array2d;
    }

    public 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 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;
    }
}

我已经解决了 this link 的问题。这个 link 的源代码是我见过的最好的源代码。

public class HoughMap
{
    public int[,] houghMap { get; private set; }
    public int[,] image { get; set; }

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

            int centerX = Width / 2;
            int centerY = Height / 2;

            int maxTheta = 180;
            int houghHeight = (int)(Math.Sqrt(2) * Math.Max(Width, Height)) / 2;
            int doubleHeight = houghHeight * 2;
            int houghHeightHalf = houghHeight / 2;
            int houghWidthHalf = maxTheta / 2;

            houghMap = new int[doubleHeight, maxTheta];

            // scanning through each (x,y) pixel of the image--+
            for (int y = 0; y < Height; y++)                 //|
            {                                                //|
                for (int x = 0; x < Width; 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 < maxTheta; 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 <= doubleHeight))
                            {
                                houghMap[rho, theta]++;
                            }
                        }
                    }
                }
            }
        }
    }
}

看看this C++ code, and this C# code就知道了。所以,复杂而混乱,我的大脑被逮捕了。特别是 C++ 的。我从没想过有人会在一维数组中存储二维值。