You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 14:25:17