views:

82

answers:

2

Good Day,

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.

TIA,

coson

+1  A: 

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.

Ravadre
AFAIK, the cylinder has been flattend down into the graphic presumably by some Photoshop application.
coson
Ok, anyway, locating left,right,top,bottom by scanning rows/columns and finding first and last row/column, and then just copying such rectangle should give you what you need. Also, you can get BitmapData and scan it in unsafe context (by manipulation pointer to raw bitmap data), which will be much faster then using GetPixel().
Ravadre
+1  A: 

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);
}
Michael McCloskey
BTW, this does not require unsafe code and/or pointers to work speedily and efficiently.
Michael McCloskey