Question

lets say that i have this image:
http://srv2.jpg.co.il/9/51c614f7c280e.png
i want to get the white ball position(x,y),
this is a verey big image,
then i cut the image by rectangle.
(because when the image is smaller everything is faster),
the results:
http://srv2.jpg.co.il/1/51c616787a3fa.png
now i want to track the white ball position by his color(white=rbg(255,255,255)),
my code:

Public Function GetBallPosition(ByRef HaxScreenOnly As Bitmap) As Point
    For y = 0 To HaxScreenOnly.Height - 1
        For x = 0 To HaxScreenOnly.Width - 1
            If HaxScreenOnly.GetPixel(x, y) = Color.FromArgb(0, 0, 0) Then
                If HaxScreenOnly.GetPixel(x + 8, y) = Color.FromArgb(0, 0, 0) And HaxScreenOnly.GetPixel(x + 8, y + 3) = Color.FromArgb(255, 255, 255) Then
                    Return New Point(x, y)

                End If
            End If
        Next
    Next
    Return New Point(0, 0)
End Function


If the color of the current pixel is black and the color of the current pixel(x+8,y+3) is white then this is the ball
it's working...but its verey slow, something like 200 miliseconds to track the ball position.
this is not fast enough.
there is faster way to track the white ball(C# or VB.net)?

Was it helpful?

Solution

Finally, I have you a solution for you. Calling GetPixel is a costly process, but you use Bitmap.LockBits and manipulate / access the image data from a pointer. I took the LockBitmap class from this article.

I checked on the performance from what I was getting previously, which was exactly like you mentioned, around 200ms~.

Here is a picture of the result using LockBitmap, rescanning the image continuously with the optimized code! Here's a picture

static void Main(string[] args)
{
    byte[] data = new WebClient().DownloadData("http://srv2.jpg.co.il/1/51c616787a3fa.png");
    Image image = Image.FromStream(new MemoryStream(data));

    LockBitmap bitmap = new LockBitmap(new Bitmap(image));
    // this essentially copies the data into memory and copies from a pointer to an array
    bitmap.LockBits();

    Color black = Color.FromArgb(0, 0, 0);
    Color white = Color.FromArgb(255, 255, 255);

    Stopwatch stopwatch = Stopwatch.StartNew();

    for (int y = 0; y < bitmap.Height; y++)
    {
        for (int x = 0; x < bitmap.Width; x++)
        {
            // GetPixel is a nice abstraction the author in the Article created so we don't have to do any of the gritty stuff.
            if (bitmap.GetPixel(x, y) == black)
            {
                if (bitmap.GetPixel(x + 8, y) == black && bitmap.GetPixel(x + 8, y + 3) == white)
                {
                    Console.WriteLine("White Ball Found in {0}", stopwatch.Elapsed.ToString());
                    break;
                }
            }
        }
    }

    bitmap.UnlockBits(); // copies the data from the array back to the original pointer

    Console.Read();
}

Hope this helps, it was certainly an interesting read for me.

Update:

As mentioned by King, I was able to further reduce the timing for you based on the algorithm improvement. So we've gone from O(n) to O(n log) time complexity (I think).

Algorithm improvement

for (int y = 0; y < bitmap.Height; y += 3) // As we know the radius of the ball
{
    for (int x = 0; x < bitmap.Width; x += 3) // We can increase this
    {
        if (bitmap.GetPixel(x, y) == black && bitmap.GetPixel(x, y + 3) == white)
        {
            Console.WriteLine("White Ball Found ({0},{1}) in {2}", x, y, stopwatch.Elapsed.ToString());
            break;
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top