如何在C#中使用Bitmap快速扫描像素并获取颜色
屏幕特定颜色扫描的性能优化方案
原代码性能瓶颈分析
你的代码之所以速度极慢,核心问题在于逐像素重复创建Bitmap、调用GDI截图和使用GetPixel:
- 每次循环都创建1x1的Bitmap对象,频繁的内存分配与回收会产生大量开销
CopyFromScreen是系统级GDI调用,逐像素调用会导致频繁的上下文切换GetPixel方法本身效率极低,它需要每次解锁并重新锁定Bitmap内存才能读取单个像素
优化方案:一次性截图+Bitmap内存锁定
直接截取整个屏幕的Bitmap,通过Bitmap.LockBits将像素内存锁定到托管区域,直接操作原始像素数据,能将性能提升几个数量级。以下是优化后的完整代码:
using System; using System.Drawing; using System.Drawing.Imaging; using System.Threading; using System.Windows.Forms; namespace ConsoleApp1 { internal class Program { // 要查找的目标颜色,可自行修改 private static readonly Color TargetColor = Color.FromArgb(255, 255, 0, 0); // 红色示例 static void Main(string[] args) { while (true) { bool colorExists = CheckScreenForTargetColor(); Console.WriteLine(colorExists ? "目标颜色存在" : "未找到目标颜色"); Thread.Sleep(1000); } } public static bool CheckScreenForTargetColor() { var screenBounds = Screen.PrimaryScreen.Bounds; // 一次性创建与屏幕同尺寸的Bitmap using (var screenBitmap = new Bitmap(screenBounds.Width, screenBounds.Height)) { using (var graphics = Graphics.FromImage(screenBitmap)) { // 一次截取整个屏幕,避免逐像素截图 graphics.CopyFromScreen(screenBounds.Location, Point.Empty, screenBounds.Size); } // 锁定Bitmap像素内存,获取直接访问权限 var bitmapData = screenBitmap.LockBits( new Rectangle(0, 0, screenBitmap.Width, screenBitmap.Height), ImageLockMode.ReadOnly, screenBitmap.PixelFormat); try { int pixelByteSize = Image.GetPixelFormatSize(screenBitmap.PixelFormat) / 8; int totalBytes = bitmapData.Stride * bitmapData.Height; byte[] pixelBytes = new byte[totalBytes]; // 将非托管内存的像素数据复制到托管数组 System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, pixelBytes, 0, totalBytes); // 遍历像素,找到目标颜色立即返回 for (int y = 0; y < screenBitmap.Height; y++) { for (int x = 0; x < screenBitmap.Width; x++) { int byteIndex = y * bitmapData.Stride + x * pixelByteSize; // 解析32位ARGB像素(大部分屏幕默认格式,字节顺序为BGR) byte blue = pixelBytes[byteIndex]; byte green = pixelBytes[byteIndex + 1]; byte red = pixelBytes[byteIndex + 2]; byte alpha = pixelBytes[byteIndex + 3]; Color currentColor = Color.FromArgb(alpha, red, green, blue); if (currentColor.Equals(TargetColor)) { return true; } } } } finally { // 必须解锁内存,避免资源泄漏 screenBitmap.UnlockBits(bitmapData); } } return false; } } }
关键优化点说明
- 一次性截图:仅调用一次
CopyFromScreen获取全屏幕像素,彻底避免逐像素的GDI调用开销 - LockBits内存锁定:跳过
GetPixel的低效内存操作,直接访问Bitmap的原始像素内存 - 直接操作字节数组:将像素数据复制到托管数组后,通过索引直接读取颜色分量,这是最快的像素访问方式
- 提前终止遍历:找到目标颜色后立即返回,无需遍历全部像素
注意事项
- 像素格式适配:如果你的屏幕不是32位ARGB格式(比如24位RGB),需要调整颜色解析逻辑(去掉alpha通道,保持BGR字节顺序)
- 资源安全:必须在
finally块中调用UnlockBits,否则会导致Bitmap资源泄漏 - 多屏幕支持:若需要扫描多个屏幕,可遍历
Screen.AllScreens分别处理每个屏幕的截图
内容的提问来源于stack exchange,提问作者Cod3rMax
相关产品推荐
相关产品推荐

