The Bitmap class contains a GetPixel method that returns the Color of a pixel in the bitmap, given its X and Y coordinates. One technique might be to walk the rows and columns of data to determine the lowest X and Y coordinates at which a non-white pixel exists. This technique is adequate if the images are small and/or performance is not a prime consideration since calling GetPixel is rather slow.
Another approach would be to get the Bitmap image data into a byte[] and then walk the bytes in the array to determine the location of the non-white pixels. This approach requires some knowledge of the way bytes are laid out for a given bitmap type (e.g. 32 bit, 24 bit, 1 bit, etc.).
To get the bytes for a bitmap, you call the LockBits method on the bitmap to lock a region of the bitmap and obtain a BitmapData object. You then use the Stride and Height properties of the BitmapData object to determine the size of the byte array needed to contain the bitmap.
I whipped up and tested the following method, which seems to work speedily on a couple test images I created to detect the location of an ellipse.
private Point DetectLocation(Bitmap original)
{
Bitmap source = null;
// If original bitmap is not already in 32 BPP, ARGB format, then convert
if (original.PixelFormat != PixelFormat.Format32bppArgb)
{
source = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
source.SetResolution(original.HorizontalResolution, original.VerticalResolution);
using (Graphics g = Graphics.FromImage(source))
{
g.DrawImageUnscaled(original, 0, 0);
}
}
else
{
source = original;
}
// Lock source bitmap in memory
BitmapData sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
// Copy image data to binary array
int imageSize = sourceData.Stride * sourceData.Height;
byte[] sourceBuffer = new byte[imageSize];
Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize);
// Unlock source bitmap
source.UnlockBits(sourceData);
int sourceIndex = 0;
int pixelTotal = 0;
int height = source.Height;
int width = source.Width;
int threshold = 255 * 3;
int minX = width;
int minY = height;
// Iterate lines
for (int y = 0; y < height; y++)
{
sourceIndex = y * sourceData.Stride;
// Iterate pixels
for (int x = 0; x < width; x++)
{
// Compute pixel brightness (i.e. total of Red, Green, and Blue values)
pixelTotal = sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2] + sourceBuffer[sourceIndex + 3];
if (pixelTotal < threshold)
{
minX = Math.Min(minX, x);
minY = Math.Min(minY, y);
}
sourceIndex += 4;
}
}
return new Point(minX, minY);
}