【问题标题】:Locate pixel by color return point按颜色返回点定位像素
【发布时间】:2012-02-12 17:07:19
【问题描述】:

我需要用指定的颜色找到它找到的第一个像素的点/坐标 (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);
}

【问题讨论】:

    标签: c# image gdi+ lockbits


    【解决方案1】:

    你没有停止使用 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 构造函数是必要的,因为线在位图中是倒置存储的。并且不要在失败时返回 new Point(0, 0),这是一个有效的像素。

    【讨论】:

    • 我从来没有想过为此目的使用Color.ToArgb()。太棒了!
    • 感谢您的回复。我无法让指针返回正确的值。也许我的图像格式错误或其他什么。无论如何,谢谢:)
    • 很难猜,注意 alpha 值。
    【解决方案2】:

    您的代码有几处错误:

    1. 您正在使用 PixelFormat.Format32bppPArgb - 您应该使用图像的像素格式,如果它们不匹配,则无论如何都会在后台复制所有像素。
    2. 您仍在使用GetPixel,因此所有这些麻烦不会给您带来任何好处。

    要有效地使用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 个字节)。

    【讨论】:

    • 非常感谢,这非常有效,现在速度非常快! :) 我仍在使用 GetPixel(),只是为了说明我想用 LockBits 做什么。
    猜你喜欢
    • 2015-12-01
    • 1970-01-01
    • 2014-02-28
    • 1970-01-01
    • 1970-01-01
    • 2012-04-23
    • 2010-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多