Reputation: 8659
I have several images that I want to place in a windows form. The images themselves are 85 x 85. Each image has a white background with a cylinder object (of varying sizes) that can be located at any location of the image.
For example:
Image 1: 85w x 85h has a cylinder image at: (25, 35) from the top left corner
Image 2: 85w x 85h has a cylinder image at: (28, 42) from the top left corner
I am wondering if it is possible to programmatically determine the position (25, 35) or (28, 42) with the .NET graphics library.
Basically what I want to do is reposition the cylinder to a fixed coordinate, say (10, 10) from the top left corner.
Upvotes: 1
Views: 190
Reputation: 2411
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);
}
Upvotes: 1
Reputation: 11957
I don't know how complex this cylinder is (as a shape), but you could just run through all pixels, check in which row and column you can find first non-white pixels.
Upvotes: 1