纯Delphi实现屏幕找图:模板匹配算法与性能优化实战

简介:这份Delphi图像匹配资源面向需要在Windows桌面环境中实现屏幕找图的开发者,适用于自动化测试、游戏辅助、屏幕抓取等场景,帮助解决从屏幕截取到目标定位的完整链路问题。资源包为rar压缩格式,整体约6.63MB,上游未提供文件总数与类型明细,但内容围绕图像处理库选型、屏幕抓取API、模板匹配与哈希算法、特征匹配、效率优化及误报漏报处理等核心环节展开,并附有基于VCL.Graphics的像素级差分匹配示例代码,便于读者理解模板匹配的基本实现思路。已有540人学习关注,说明该主题在Delphi社区具有一定实用需求。读者可从中获得屏幕图像匹配的算法选型参考、性能优化方向以及可借鉴的代码骨架,适合具备一定Delphi基础、希望快速上手屏幕找图功能的中级开发者参考实践。

1. 屏幕找图这件事,Delphi 其实一直都能干

做自动化测试或者游戏辅助的开发者,大概率都写过“在屏幕上找一张图”的功能。很多人第一反应是上 Python + OpenCV,或者用按键精灵那套东西。但如果你的主力技术栈是 Delphi,为了一个找图功能去挂个 Python 环境,部署和维护都别扭。这个项目就是冲着这个痛点来的:用纯 Delphi 实现屏幕截图 + 模板匹配,不依赖外部运行时,编译出来就是一个 exe,拷到目标机器上双击就能跑。

它适合两类人:一类是手里有 Delphi 老项目、想加自动化识别能力的开发者;另一类是想研究模板匹配底层实现、不想被 OpenCV 黑盒挡住视线的人。核心逻辑不复杂,但要把速度做上去,里面有不少细节值得拆开看。

2. 技术选型与整体架构:为什么不用 OpenCV

2.1 纯 Delphi 方案的取舍

先说清楚这个项目为什么走纯 Delphi 路线。OpenCV 的模板匹配确实成熟,但引入它意味着你要么带一堆 dll,要么用别人封装好的 Delphi 接口,版本兼容和部署体积都是问题。这个项目的思路是:屏幕截图用 Windows 原生 GDI,匹配算法自己实现,整个工程只依赖 Delphi 自带的单元。

代价是算法要自己优化。OpenCV 底层有 SIMD 指令加速,纯 Pascal 代码在朴素实现下会慢一个数量级。所以这个项目的重点不在“能不能匹配”,而在“怎么让朴素算法跑得够快”。常见的做法是三层优化:先降采样粗定位,再在候选区域做精确匹配,最后用提前终止策略砍掉大量无效计算。

技术栈上,核心用到这几个部分:

  • Vcl.Graphics和Winapi.Windows:负责屏幕捕获,通过BitBlt把屏幕内容拷到内存位图
  • System.Diagnostics:做耗时统计,方便调优
  • 自定义的匹配单元:实现 NCC(归一化互相关)或 SAD(绝对差之和)匹配

2.2 模块划分

工程结构大致分三块。第一块是截图模块,负责把屏幕或指定窗口区域抓成TBitmap;第二块是匹配引擎,输入是源图和大图,输出是匹配位置和相似度;第三块是调度层,负责把前两块串起来,支持多目标查找和结果回调。

type TMatchResult = record X, Y: Integer; // 匹配左上角坐标 Score: Double; // 相似度 0~1 Found: Boolean; // 是否达到阈值 end; TScreenMatcher = class private FThreshold: Double; // 匹配阈值,默认 0.85 FMaxResults: Integer; // 最多返回几个结果 public function CaptureScreen: TBitmap; function FindImage(ASource, ATemplate: TBitmap): TMatchResult; function FindAll(ASource, ATemplate: TBitmap): TArray<TMatchResult>; end;

TMatchResult用 record 而不是 class,是为了避免频繁堆分配——找图过程中会产生大量中间结果,用值类型能减少 GC 压力。FThreshold控制灵敏度,设太高会漏匹配,设太低会误匹配,后面避坑章节会细说怎么调。

2.3 数据流走向

一次完整的找图流程是这样的:调度层调用CaptureScreen拿到当前屏幕位图,转成 32 位像素数组方便快速读取;然后把模板图也转成同样格式;匹配引擎在大图上滑动窗口,每个位置算一次相似度;超过阈值的记录到结果列表;最后按相似度排序返回。

这里有个关键决策:像素数据用TArray<Cardinal>存,而不是反复调TBitmap.Canvas.Pixels。后者每次访问都要走 GDI 接口,在几十万次循环里是灾难性的。一次性把位图数据读进数组,后续全在内存里算,速度差几十倍。

3. 核心匹配算法拆解:从朴素实现到能用的速度

3.1 相似度计算的选择

模板匹配的核心是衡量“模板”和“大图某个区域”有多像。最直觉的是 SAD,就是把对应像素的差值绝对值加起来,越小越像。但 SAD 对亮度变化敏感——屏幕亮度调一下,同样的图可能就匹配不上了。所以这个项目用的是 NCC,归一化互相关,它对线性亮度变化不敏感,鲁棒性更好。

NCC 的公式是:把两个区域都减去各自均值,然后算归一化的点积。结果在 -1 到 1 之间,1 表示完全一致。实现时要注意用浮点还是定点——纯浮点在大量循环里也慢,常见做法是用 Int64 累加,最后再转浮点算比值。

function NCCScore(Src, Tpl: PByteArray; SrcStride, TplStride: Integer; TplW, TplH: Integer): Double; var i, j: Integer; SumS, SumT, SumST, SumS2, SumT2: Int64; MeanS, MeanT, Num, DenS, DenT: Double; ps, pt: PByte; begin SumS := 0; SumT := 0; SumST := 0; SumS2 := 0; SumT2 := 0; for j := 0 to TplH - 1 do begin ps := @Src[j * SrcStride]; pt := @Tpl[j * TplStride]; for i := 0 to TplW - 1 do begin SumS := SumS + ps[i]; SumT := SumT + pt[i]; SumST := SumST + ps[i] * pt[i]; SumS2 := SumS2 + ps[i] * ps[i]; SumT2 := SumT2 + pt[i] * pt[i]; end; end; // 计算均值与归一化系数 MeanS := SumS / (TplW * TplH); MeanT := SumT / (TplW * TplH); Num := SumST - SumS * MeanT - SumT * MeanS + TplW * TplH * MeanS * MeanT; DenS := Sqrt(SumS2 - 2 * MeanS * SumS + TplW * TplH * MeanS * MeanS); DenT := Sqrt(SumT2 - 2 * MeanT * SumT + TplW * TplH * MeanT * MeanT); if (DenS * DenT) < 1e-9 then Result := 0 else Result := Num / (DenS * DenT); end;

这段代码里SrcStride和TplStride是位图的行字节数,通常比宽度大(有对齐填充),所以不能直接用j * Width来定位行首。Int64累加是为了防止大模板下平方和溢出——一个 100x100 的模板,像素值 255,平方和能到 6.5 亿,接近 32 位上限,用 Int64 稳妥。

3.2 提前终止与降采样

朴素 NCC 要对大图每个位置都算一遍完整模板,假设屏幕 1920x1080,模板 50x50,那就是约 200 万个位置乘以 2500 次像素运算,五十亿次操作,纯 Pascal 跑下来要好几秒。这个项目用了两个策略把时间压到几百毫秒。

第一个是降采样粗定位。先把大图和模板都缩小到 1/4,在缩略图上跑一遍匹配,找出相似度超过一个较低阈值(比如 0.6)的候选区域。缩略图上像素少 16 倍,这一步很快。然后只在这些候选区域附近做全分辨率精确匹配。

第二个是提前终止。在算 NCC 的过程中,如果累加到一半发现当前的部分和已经明显低于阈值对应的水平,就直接跳出,不用算完。这个判断要小心,因为 NCC 是归一化的,部分和不能直接比。常见做法是先用 SAD 做快速筛选,SAD 超过某个界限的直接丢弃,只有 SAD 达标的才去算完整的 NCC。

// 快速筛选:SAD 超过阈值直接跳过 function QuickReject(Src, Tpl: PByteArray; SrcStride, TplStride: Integer; TplW, TplH: Integer; MaxSAD: Int64): Boolean; var i, j: Integer; Sad: Int64; ps, pt: PByte; begin Sad := 0; for j := 0 to TplH - 1 do begin ps := @Src[j * SrcStride]; pt := @Tpl[j * TplStride]; for i := 0 to TplW - 1 do begin Sad := Sad + Abs(ps[i] - pt[i]); if Sad > MaxSAD then Exit(True); // 提前判定为不匹配 end; end; Result := False; end;

MaxSAD这个参数需要根据模板大小和图像内容来定。经验值是模板像素数乘以一个系数,比如 30。模板越大,允许的总差值越大。这个值设得太松,筛选效果差;设得太紧,可能把真正匹配的区域也筛掉。我一般会先用几张典型截图跑一遍,看正确匹配位置的 SAD 分布,取一个比最大值略大的数。

3.3 多目标查找与去重

有时候屏幕上同一个图标出现多次,需要全部找出来。FindAll的做法是:在匹配过程中,凡是超过阈值的都记录下来,但相邻位置会有大量重叠的结果——因为模板滑动时,匹配位置附近几个像素的相似度都很高。所以需要非极大值抑制:按相似度排序,依次取最高的,然后把它周围一定范围内的其他结果删掉。

function SuppressNonMax(Results: TArray<TMatchResult>; MinDist: Integer): TArray<TMatchResult>; var i, j: Integer; Keep: TArray<TMatchResult>; begin // 先按 Score 降序排序 TArray.Sort<TMatchResult>(Results, TComparer<TMatchResult>.Construct( function(const L, R: TMatchResult): Integer begin Result := CompareValue(R.Score, L.Score); end)); SetLength(Keep, 0); for i := 0 to High(Results) do begin if not Results[i].Found then Continue; // 检查是否与已保留的结果太近 for j := 0 to High(Keep) do if (Abs(Results[i].X - Keep[j].X) < MinDist) and (Abs(Results[i].Y - Keep[j].Y) < MinDist) then begin Results[i].Found := False; Break; end; if Results[i].Found then begin SetLength(Keep, Length(Keep) + 1); Keep[High(Keep)] := Results[i]; end; end; Result := Keep; end;

MinDist一般取模板宽度的一半左右。太小了去重不干净,太大了可能把两个真实相邻的目标合并成一个。如果目标之间间距很小,这个值要相应调小。

4. 避坑与常见问题:找图不准、太慢、崩溃怎么排查

4.1 匹配位置总是偏几个像素

现象是找到的坐标和实际位置差 2 到 5 个像素,肉眼能看出来但不算太离谱。原因通常是 DPI 缩放。Windows 在高分屏上默认 125% 或 150% 缩放,BitBlt抓到的位图是缩放后的,但模板图可能是原始尺寸,两者对不上。解决办法是在程序清单里声明 DPI 感知,或者在截图前调SetProcessDpiAwareness,让系统不做缩放。另一个可能是位图 stride 对齐问题,如果手动构造位图数据时没考虑 4 字节对齐,行与行之间会错位。

4.2 相似度阈值设 0.9 还是 0.8

这个没有标准答案,取决于图像内容。纯色背景上的图标,正确匹配的 NCC 通常在 0.95 以上,阈值可以设 0.9。但如果背景有渐变或噪声,正确匹配可能只有 0.85。我一般会先跑一遍,把正确匹配和错误匹配的分数都打印出来,看两者之间有没有明显的分界。如果分界模糊,说明模板选得不好——模板里包含了太多背景变化区域,应该裁剪得更紧一些,只保留目标本身的特征部分。

4.3 找图过程中程序卡死

如果匹配循环跑在主线程里,几秒钟的阻塞会让界面无响应。解决办法是把匹配放到后台线程,通过TThread或TTask执行,结果用回调或消息通知主线程。注意截图必须在主线程做,因为 GDI 的BitBlt涉及窗口句柄,跨线程调用可能出问题。常见做法是主线程截图,把位图数据传给后台线程做匹配。

4.4 内存占用越来越高

每次找图都创建新的TBitmap和像素数组,如果不释放,内存会持续增长。Delphi 的TBitmap需要显式Free,像素数组虽然是托管的,但如果放在全局变量里持有引用也不会释放。建议把截图和匹配封装成一次性的操作,用完即释放。如果频繁找图,可以复用同一个TBitmap对象,每次只更新内容,避免反复分配。

4.5 模板图边缘有透明通道怎么办

从 PNG 加载的模板可能带 Alpha 通道,但屏幕截图没有透明度。如果直接忽略 Alpha,透明区域的颜色会参与匹配,导致分数偏低。处理方式有两种:一是把透明像素替换成一种在屏幕上不会出现的颜色(比如纯品红),然后在匹配时跳过这些像素;二是把模板的透明区域裁掉,只保留不透明部分。前者更通用,后者更快。

5. 二次开发与性能调优:几个能直接抄的改造点

5.1 用查找表加速像素运算

NCC 里最耗时的部分是逐像素的乘法和平方。如果图像是 8 位灰度,像素值只有 0 到 255,可以预先算好一张 256x256 的乘法表,把ps[i] * pt[i]变成查表。这样能把乘法指令换成内存访问,在现代 CPU 上不一定更快,但在老机器上效果明显。更实际的做法是把图像转成灰度后再匹配,三个通道变一个通道,计算量直接降到三分之一。

// 预计算乘法表,避免循环内重复乘法 var MulTable: array[0..255, 0..255] of Word; procedure InitMulTable; var i, j: Integer; begin for i := 0 to 255 do for j := 0 to 255 do MulTable[i, j] := i * j; end;

这个表占 128KB 内存,换来的是循环里少一次乘法。在模板匹配这种计算密集场景下,收益是实打实的。

5.2 支持指定窗口区域截图

全屏找图很多时候是浪费,目标只在某个窗口里。可以加一个参数指定窗口句柄,用GetWindowRect拿到窗口位置,只截取那个区域。这样大图尺寸可能从 1920x1080 降到 800x600,匹配时间减少一半以上。注意窗口可能有边框和标题栏,需要根据实际情况调整截取范围。

5.3 多模板并行匹配

如果要在同一张截图里找多个不同的目标,可以开多个线程并行匹配。每个线程负责一个模板,共享同一份截图数据。Delphi 的TParallel.For或者手动创建线程池都可以。注意共享的截图数据要只读,避免线程安全问题。并行度不要超过 CPU 核心数,否则线程切换的开销会抵消并行收益。

5.4 结果缓存与增量匹配

如果屏幕内容变化不大,没必要每次都全图重新匹配。可以缓存上一次的匹配结果,下一次只在上次结果附近的小范围内搜索。如果目标移动幅度不大,这个策略能把搜索范围缩小到原来的十分之一。判断是否需要全图重搜的条件可以是:上次匹配分数下降超过一定幅度,或者超过一定时间间隔。

5.5 验证改造效果的方法

改完之后怎么知道有没有效果?我一般会准备一组测试用例:固定几张屏幕截图和对应的模板,记录正确匹配的位置。每次改完跑一遍,看匹配位置是否一致、耗时是否下降。耗时用TStopwatch统计,跑十次取平均,避免单次波动。如果匹配位置变了,说明改动影响了算法正确性,需要回退排查。

var SW: TStopwatch; R: TMatchResult; begin SW := TStopwatch.StartNew; R := Matcher.FindImage(ScreenBmp, TemplateBmp); SW.Stop; Memo1.Lines.Add(Format('位置(%d,%d) 分数%.3f 耗时%dms', [R.X, R.Y, R.Score, SW.ElapsedMilliseconds])); end;

这段代码把位置、分数、耗时一起打出来,方便对比不同参数下的表现。分数用来判断匹配质量,耗时用来判断性能,两个指标一起看才能确定改动是正向的。

从那以后我每次调匹配参数,都强制先跑一遍这组基准测试,确认分数没掉、耗时没涨,才敢往正式代码里合。找图这东西,肉眼看着“差不多”往往差很多,数据说话最靠谱。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

DNGM(1,1)离散灰色模型:小样本预测的Python实战与GM(1,1)对比

DNGM(1,1)离散灰色模型:小样本预测的Python实战与GM(1,1)对比

做数据分析的人&#xff0c;大概率都撞上过同一个尴尬&#xff1a;想预测下个月或者下一周期的数值&#xff0c;手里却只有五六个、七八个历史样本。经典统计模型要求大样本&#xff0c;深度学习更是无底洞式地吃数据&#xff0c;这俩在“贫数据”面前基本束手无策。灰色预测就…

2026/10/21 3:08:09 阅读更多 →
无人机航拍小目标检测数据集构建:从场景设计到智慧交通与应急救援落地

无人机航拍小目标检测数据集构建:从场景设计到智慧交通与应急救援落地

我做无人机视觉落地也有几年了&#xff0c;今年被问得最多的问题不是“模型怎么调”&#xff0c;而是“模型怎么到了现场就不灵”。很多朋友拿着公开的航拍检测模型&#xff0c;在桌面演示上mAP能到七八十个点&#xff0c;可一部署到国内的十字路口、老小区、城乡结合部&#x…

2026/10/21 3:07:07 阅读更多 →
Seay源代码审计系统实战:PHP漏洞扫描与规则扩展

Seay源代码审计系统实战:PHP漏洞扫描与规则扩展

简介&#xff1a;Seay源代码审计系统是一款基于C#开发的PHP代码安全审计工具&#xff0c;主要运行于Windows平台&#xff0c;面向网络安全初学者、CTF/AWD参赛选手及渗透测试人员&#xff0c;用于快速定位PHP项目中的安全隐患。它能够识别SQL注入、代码执行、命令执行、文件包含…

2026/10/21 3:07:07 阅读更多 →

最新新闻

单图生成3D高斯泼溅实战:API接入与交互展示

单图生成3D高斯泼溅实战:API接入与交互展示

1. 从一张图到可交互3D&#xff1a;这个项目到底在做什么单图生成3D高斯泼溅&#xff08;3D Gaussian Splatting&#xff0c;后面统一简称3DGS&#xff09;这个方向&#xff0c;最近半年热度一直没降下来。原因很直接&#xff1a;传统摄影测量或者多视角重建&#xff0c;你得围…

2026/10/21 3:52:33 阅读更多 →
Docker存储卷实战:三种挂载方式与数据持久化指南

Docker存储卷实战:三种挂载方式与数据持久化指南

容器跑起来只是第一步&#xff0c;真正麻烦的是数据往哪儿放。不少新手用 Docker 跑了个数据库或文件服务&#xff0c;重启容器后数据“凭空消失”&#xff0c;或者多个容器想共享同一份配置、日志&#xff0c;结果各写各的互不相通。这个问题的标准解法就是 Docker 存储卷&…

2026/10/21 3:52:33 阅读更多 →
Linux高级存储管理实战:LVM、RAID、磁盘配额与Swap调优

Linux高级存储管理实战:LVM、RAID、磁盘配额与Swap调优

1. 从“磁盘满了”说起&#xff1a;为什么基础分区不够用了很多人第一次接触Linux存储&#xff0c;都是从fdisk分区、mkfs格式化、mount挂载这三板斧开始的。这套流程应付单块磁盘、固定容量的场景确实够用&#xff0c;但只要你的机器开始承载真实业务&#xff0c;很快就会撞上…

2026/10/21 3:52:33 阅读更多 →
AI大模型落地制造业:六大系统智能化改造路径与避坑指南

AI大模型落地制造业:六大系统智能化改造路径与避坑指南

这两年跑过不少制造企业的数字化项目&#xff0c;发现一个很有意思的变化&#xff1a;ERP、MES、SRM、APS、WMS、EMS这些系统&#xff0c;能上的基本都上了&#xff0c;可多数老板仍然说不清“系统到底帮我省了多少钱、提了多少速”。数据是有了&#xff0c;但站在数据面前做决…

2026/10/21 3:52:33 阅读更多 →
C#手动拼PDU发短信:串口AT指令硬核实践

C#手动拼PDU发短信:串口AT指令硬核实践

简介&#xff1a;这是一份面向C#中级开发者与通信协议学习者的PDU短信发送实战项目&#xff0c;聚焦GSM底层通信原理在Windows平台的落地实现。资源通过完整的SMSCSharp工程&#xff0c;封装了PDU编码/解码核心类、Submit PDU构造逻辑、MSISDN与SMSC号码二进制转换、多段长短信…

2026/10/21 3:52:33 阅读更多 →
Excel中ROW与COLUMN函数:从自动编号到矩阵运算的实战指南

Excel中ROW与COLUMN函数:从自动编号到矩阵运算的实战指南

做了十年表哥表姐&#xff0c;我最常听的一句话是&#xff1a;“帮我把这个表重新排个序&#xff0c;序号也跟着变一下。”如果序号是手工敲进去的&#xff0c;那接下来就是一场手动改号的噩梦。但如果从一开始就用ROW函数生成序号&#xff0c;就算数据排序翻来覆去&#xff0c…

2026/10/21 3:51:33 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →