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!
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).
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;
}
}
}