按颜色返回点定位像素

时间:2012-02-12 17:07:19

标签: c# image gdi+ lockbits

我需要找到它用指定颜色找到的第一个像素的点/坐标(x,y)。 我使用了GetPixel()方法,但它有点太慢并且正在研究LockBits。我多么无法弄清楚这是否真的可以解决我的问题。我可以使用LockBits返回找到的像素的点吗?

这是我目前的代码:

public Point FindPixel(Image Screen, Color ColorToFind)
{
    Bitmap bit = new Bitmap(Screen);
    BitmapData bmpData = bit.LockBits(new Rectangle(0, 0, bit.Width, bit.Height),
                                    ImageLockMode.ReadWrite,
                                    PixelFormat.Format32bppPArgb);
    unsafe
    {
        byte* ptrSrc = (byte*)bmpData.Scan0;
        for (int y = 0; y < bmpData.Height; y++)
        {
            for (int x = 0; x < bmpData.Width; x++)
            {
                Color c = bit.GetPixel(x, y);
                if (c == ColorToFind)
                    return new Point(x, y);
            }
        }
    }
    bit.UnlockBits(bmpData);
    return new Point(0, 0);
}

2 个答案:

答案 0 :(得分:2)

你没有停止使用GetPixel(),所以你没有领先。这样写它:

    int IntToFind = ColorToFind.ToArgb();
    int height = bmpData.Height;    // These properties are slow so read them only once
    int width = bmpData.Width;
    unsafe
    {
        for (int y = 0; y < height; y++)
        {
            int* pline = (int*)bmpData.Scan0 + y * bmpData.Stride/4;
            for (int x = 0; x < width; x++)
            {
                if (pline[x] == IntToFind)
                    return new Point(x, bit.Height - y - 1);
            }
        }
    }

奇怪的Point构造函数是必要的,因为行被倒置存储在位图中。并且在失败时不返回新的Point(0,0),这是一个有效的像素。

答案 1 :(得分:1)

您的代码几乎没有问题:

  1. 您正在使用PixelFormat.Format32bppPArgb - 您应该使用图像的像素格式,如果它们不匹配,所有像素都将被复制在引擎盖下。
  2. 你还在使用GetPixel,所以这一切都不会给你带来任何好处。
  3. 要有效地使用LockBits,您基本上想要锁定图像,然后使用不安全的指针来获取像素值。对于不同的像素格式,执行此操作的代码会有所不同,假设您确实将具有32bpp格式且蓝色在LSB上,您的代码可能如下所示:

    for (int y = 0; y < bmpData.Height; ++y)
    { 
        byte* ptrSrc = (byte*)(bmpData.Scan0 + y * bmpData.Stride);
        int* pixelPtr = (int*)ptrSrc;
    
        for (int x = 0; x < bmpData.Width; ++x)
        {
            Color col = Color.FromArgb(*pixelPtr);
    
            if (col == ColorToFind) return new Point(x, y);
    
            ++pixelPtr; //Increate ptr by 4 bytes, because it is int
        }
    }
    

    几点评论:

    • 对于每一行,使用Scan0 +步幅值计算新的ptrSrc。这是因为仅增加指针可能会失败,如果是Stride != bpp * width,则可能是这种情况。
    • 我假设蓝色像素表示为LSB,alpha表示为MSB,我认为不是这种情况,因为那些GDI像素格式是......奇怪的;),只要确保你检查它,如果它是另一种方式,使用FromArgb()方法之前的反向字节。
    • 如果你的像素格式是24bpp,那就有点棘手了,因为你不能使用int指针并将它增加1(4个字节),原因很明显。