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

屏幕图像搜索脚本运行耗时过长,寻求性能优化方案

图像搜索脚本优化方案

原代码的核心性能问题

  • 错误的循环边界:内层循环误用searchIn.Width作为y轴遍历上限,实际应为searchIn.Height,这会导致无效循环甚至越界风险
  • GetPixel的性能开销:该方法每次调用都要对位图进行锁定/解锁操作,循环中频繁调用会大幅拖慢运行速度
  • 冗余日志输出:循环内的Log语句每执行一次就触发IO操作,直接导致运行时间指数级增长
  • 多余的边界判断:循环本身已通过x < searchIn.Width、y < searchIn.Height限制范围,额外的y != searchIn.Height && x != searchIn.Width等判断完全无意义

优化措施及代码实现

关键优化点

  1. 改用BitmapData直接操作内存,规避GetPixel的频繁开销
  2. 修正循环边界,只遍历有效区域(避免越界且减少无效遍历)
  3. 移除循环内的日志输出,消除IO开销
  4. 优化像素匹配逻辑,直接操作字节数组对比

优化后的代码

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

public bool FindImageOnScreen(Bitmap searchFor, Bitmap searchIn)
{
    // 提前处理边界情况:搜索图比屏幕图大直接返回false
    if (searchFor.Width > searchIn.Width || searchFor.Height > searchIn.Height)
        return false;

    // 锁定位图到内存,直接操作字节数据
    using (var searchInData = searchIn.LockBits(new Rectangle(0, 0, searchIn.Width, searchIn.Height), ImageLockMode.ReadOnly, searchIn.PixelFormat))
    using (var searchForData = searchFor.LockBits(new Rectangle(0, 0, searchFor.Width, searchFor.Height), ImageLockMode.ReadOnly, searchFor.PixelFormat))
    {
        int pixelByteSize = Image.GetPixelFormatSize(searchIn.PixelFormat) / 8;
        int searchInStride = searchInData.Stride;
        int searchForStride = searchForData.Stride;

        // 将位图数据复制到字节数组
        byte[] searchInBytes = new byte[searchInStride * searchIn.Height];
        byte[] searchForBytes = new byte[searchForStride * searchFor.Height];
        Marshal.Copy(searchInData.Scan0, searchInBytes, 0, searchInBytes.Length);
        Marshal.Copy(searchForData.Scan0, searchForBytes, 0, searchForBytes.Length);

        // 预存搜索图的左上角和中心像素字节数据
        int centerX = searchFor.Width / 2;
        int centerY = searchFor.Height / 2;
        byte[] topLeftPixel = GetPixelBytes(searchForBytes, 0, 0, searchForStride, pixelByteSize);
        byte[] centerPixel = GetPixelBytes(searchForBytes, centerX, centerY, searchForStride, pixelByteSize);

        // 遍历屏幕图的有效区域(避免越界)
        int maxX = searchIn.Width - searchFor.Width;
        int maxY = searchIn.Height - searchFor.Height;
        for (int x = 0; x <= maxX; x++)
        {
            for (int y = 0; y <= maxY; y++)
            {
                // 快速匹配左上角像素
                if (ComparePixels(searchInBytes, x, y, searchInStride, pixelByteSize, topLeftPixel))
                {
                    // 匹配中心像素
                    if (ComparePixels(searchInBytes, x + centerX, y + centerY, searchInStride, pixelByteSize, centerPixel))
                    {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

// 获取指定坐标的像素字节数据
private byte[] GetPixelBytes(byte[] imageBytes, int x, int y, int stride, int pixelByteSize)
{
    int startIndex = y * stride + x * pixelByteSize;
    byte[] pixel = new byte[pixelByteSize];
    Array.Copy(imageBytes, startIndex, pixel, 0, pixelByteSize);
    return pixel;
}

// 对比两个像素的字节数据是否一致
private bool ComparePixels(byte[] imageBytes, int x, int y, int stride, int pixelByteSize, byte[] targetPixel)
{
    int startIndex = y * stride + x * pixelByteSize;
    for (int i = 0; i < pixelByteSize; i++)
    {
        if (imageBytes[startIndex + i] != targetPixel[i])
        {
            return false;
        }
    }
    return true;
}

额外优化建议

  • 哈希预计算:预先计算搜索图的感知哈希/平均哈希,对屏幕图候选区域快速计算哈希过滤,再进行精确匹配
  • 多线程并行遍历:用Parallel.For将屏幕图划分为多个区域并行处理,进一步提升速度
  • 匹配逻辑调整:若允许误差,可加入颜色阈值匹配;若需更高准确率,应在两点匹配通过后执行全图像素验证

内容的提问来源于stack exchange,提问作者King Tkay

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 02:42:46