coson
coson

Reputation: 8659

.NET Graphics Image

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

Answers (2)

Michael A. McCloskey
Michael A. McCloskey

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

Marcin Deptuła
Marcin Deptuła

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

Related Questions