C# WinForms 摄像头采集:AForge.NET 拍照与避坑指南
简介本资源面向具备一定C#基础的Winform开发者与计算机视觉入门者聚焦使用Aforge框架调用摄像头并完成拍照这一常见需求帮助读者快速搭建可运行的摄像头采集与拍照Demo。包内为1个PDF文档压缩包约177KB内容以图文与代码示例为主涵盖Aforge框架简介、Nuget引用配置、窗体控件设计以及摄像头枚举、分辨率获取、视频源连接与拍照等关键实现环节并附有可直接参考的C#代码片段。目前已有860人学习下载适合希望将摄像头拍照能力集成到安防、医疗、零售等场景应用中的开发者也可作为计算机视觉项目前期验证的参考材料便于对照代码理解Aforge在Winform中的实际用法与常见处理思路。1. 从一次产线工控机的翻车说起为什么我还在用 AForge 抓摄像头去年帮一个做视觉检测的朋友救急产线上一台工控机跑着 WinForms 程序需要定时抓取工位摄像头的画面存档。他之前用某商业 SDK授权到期后弹窗锁死整条线停了两个小时。我过去看了一眼需求其实简单到离谱打开摄像头、预览、点一下按钮存一张 JPG。这种场景我一般不会上 OpenCVSharp 那种重家伙直接 AForge.NET 走起半小时改完收工。AForge.NET 是个老牌的 C# 计算机视觉与人工智能库里面的AForge.Video和AForge.Video.DirectShow两个命名空间专门负责视频采集底层封装的是 DirectShow。它最大的好处是纯托管、零外部依赖、NuGet 一装就能用对 WinForms 和 WPF 都友好。你要做的只是把FilterInfoCollection列出来的摄像头设备绑到一个下拉框用户选一个VideoCaptureDevice一开NewFrame事件里把Bitmap往PictureBox上一丢拍照就是顺手Clone()一份存盘的事。适合谁做上位机、工控 HMI、实验室数据采集、教学演示的 C# 开发者。不适合谁需要高帧率、多路并发、GPU 加速的场景那还是老老实实上 OpenCVSharp 或者厂商 SDK。这篇就把我这些年用 AForge 抓摄像头的完整流程、参数细节和踩过的坑一次讲透。2. 环境搭建与设备枚举从 NuGet 包到摄像头列表2.1 装包与项目配置AForge 在 NuGet 上的包名分得很细拍照场景只需要两个AForge和AForge.Video.DirectShow。后者依赖前者装的时候 NuGet 会自动带上。我一般用 Package Manager Console 装版本号不写死让它拉最新的稳定版# 在 Visual Studio 的 Package Manager Console 里执行 Install-Package AForge Install-Package AForge.Video.DirectShow如果你习惯用 .NET CLI等价命令是dotnet add package AForge dotnet add package AForge.Video.DirectShow装完检查一下引用列表AForge.Video.DirectShow下面应该能看到AForge.Video.DirectShow.dll。有个细节要注意AForge 的包目标框架比较老如果你项目是 .NET 6/7/8NuGet 可能会提示兼容性警告但实际在 Windows 上跑没问题因为它底层调的是系统 DirectShow不依赖特定运行时。我一般会忽略这个警告但如果你项目开了TreatWarningsAsErrors记得把 NU1701 排除掉。提示AForge 系列包最后更新停在好几年前但功能稳定不需要追新。真正要留意的是你的项目平台目标必须显式设为 x86 或 x64不能是 AnyCPU否则 DirectShow 的 COM 互操作在某些机器上会报COMException。2.2 枚举摄像头设备DirectShow 枚举设备靠的是FilterInfoCollection传入FilterCategory.VideoInputDevice就只列摄像头不会把音频设备混进来。下面这段代码我一般放在窗体构造函数或者Load事件里using AForge.Video.DirectShow; // 声明为窗体级字段后面开流和切换设备都要用 private FilterInfoCollection _videoDevices; private VideoCaptureDevice _videoSource; private void EnumerateCameras() { // 只枚举视频输入设备排除音频采集设备 _videoDevices new FilterInfoCollection(FilterCategory.VideoInputDevice); if (_videoDevices.Count 0) { // 没有摄像头时给出明确提示不要静默失败 MessageBox.Show(未检测到任何摄像头设备请检查连接后重试。); return; } // 清空下拉框避免重复枚举时叠加 cmbCameras.Items.Clear(); foreach (FilterInfo device in _videoDevices) { // device.Name 是设备友好名device.MonikerString 是唯一标识 cmbCameras.Items.Add(device.Name); } // 默认选中第一个省得用户还要手动点 cmbCameras.SelectedIndex 0; }逻辑说明FilterInfoCollection构造时会去问 DirectShow 要设备列表Count为 0 说明系统层面就没认到摄像头这时候别急着往下走先提示用户。FilterInfo有两个关键属性Name是给人看的设备名MonikerString是给 DirectShow 用的唯一标识字符串。实际开流的时候我建议用MonikerString而不是Name因为有些机器上两个同型号摄像头Name会重名用MonikerString才能精确指定。参数说明FilterCategory.VideoInputDevice这个枚举值决定了枚举范围换成AudioInputDevice就是麦克风别搞混。cmbCameras是 WinForms 的ComboBox如果你用 WPF换成ComboBox控件逻辑一样。2.3 设备热插拔的处理工控现场经常有人拔了摄像头再插回去程序如果只在启动时枚举一次后面就抓瞎了。我的做法是在窗体上放一个「刷新设备」按钮点击时先停掉当前流再重新枚举private void btnRefresh_Click(object sender, EventArgs e) { // 先停流否则设备被占用重新枚举可能拿不到 StopCamera(); EnumerateCameras(); } private void StopCamera() { if (_videoSource ! null _videoSource.IsRunning) { // 发停止信号等待内部线程退出 _videoSource.SignalToStop(); _videoSource.WaitForStop(); _videoSource null; } }这里SignalToStop和WaitForStop必须成对出现。只调SignalToStop是异步的设备可能还没释放你就去枚举了结果新列表里看不到刚插上的设备。WaitForStop会阻塞到采集线程真正退出虽然会卡一下 UI但比出玄学问题强。如果嫌卡可以放到Task.Run里但记得回来更新 UI 要Invoke。3. 开流、预览与拍照NewFrame 事件里的完整链路3.1 启动视频流选好设备后用MonikerString构造VideoCaptureDevice然后订阅NewFrame事件最后调Start()。这三步顺序不能乱先订阅再启动否则第一帧可能丢。private void StartCamera() { if (cmbCameras.SelectedIndex 0) return; // 用 MonikerString 精确定位设备避免重名问题 string moniker _videoDevices[cmbCameras.SelectedIndex].MonikerString; _videoSource new VideoCaptureDevice(moniker); // 设置期望的分辨率具体支持哪些要看设备能力 _videoSource.DesiredFrameSize new Size(1280, 720); _videoSource.DesiredFrameRate 30; // 先订阅事件再启动 _videoSource.NewFrame OnNewFrame; _videoSource.Start(); } private void OnNewFrame(object sender, NewFrameEventArgs eventArgs) { // 注意这个回调在采集线程上不是 UI 线程 Bitmap frame (Bitmap)eventArgs.Frame.Clone(); // 用 Invoke 切回 UI 线程更新 PictureBox if (picPreview.InvokeRequired) { picPreview.Invoke(new Action(() { // 释放旧图避免 GDI 对象泄漏 Image old picPreview.Image; picPreview.Image frame; old?.Dispose(); })); } else { Image old picPreview.Image; picPreview.Image frame; old?.Dispose(); } }逻辑说明NewFrame事件的eventArgs.Frame是一张复用的BitmapAForge 内部会不断往同一块内存写新帧。所以你绝对不能直接把eventArgs.Frame赋给PictureBox.Image否则下一帧来了你看到的图就变了而且多线程访问会直接崩。必须Clone()一份出来。Clone()出来的Bitmap归你管用完要Dispose不然跑一晚上 GDI 对象数能飙到几万程序直接卡死。参数说明DesiredFrameSize和DesiredFrameRate是「期望值」不是「强制值」。摄像头驱动会挑一个最接近的支持格式。比如你写 1280x720但设备只支持 640x480 和 1920x1080它可能给你 1920x1080。想确认实际生效的分辨率可以在NewFrame里打印eventArgs.Frame.Width和Height看一眼。DesiredFrameRate同理设 30 但设备只给 15 是常事。3.2 拍照存盘拍照的本质就是在某一帧上Clone()一份然后Save成文件。我一般把当前帧先存到一个字段里按钮点击时再存盘这样避免在事件回调里做 IO 阻塞采集线程。// 窗体级字段保存最新一帧的副本 private Bitmap _latestFrame; private void OnNewFrame(object sender, NewFrameEventArgs eventArgs) { Bitmap frame (Bitmap)eventArgs.Frame.Clone(); // 更新最新帧引用旧的释放掉 Bitmap oldFrame _latestFrame; _latestFrame frame; oldFrame?.Dispose(); // 预览更新逻辑同上略 } private void btnCapture_Click(object sender, EventArgs e) { if (_latestFrame null) { MessageBox.Show(还没有收到视频帧请确认摄像头已启动。); return; } // 按时间戳命名避免覆盖 string fileName $capture_{DateTime.Now:yyyyMMdd_HHmmss_fff}.jpg; string fullPath Path.Combine(Application.StartupPath, Captures, fileName); // 确保目录存在 Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); // 用 Clone 再存避免存盘期间采集线程改写原图 using (Bitmap snapshot (Bitmap)_latestFrame.Clone()) { // 第二个参数指定 JPEG 质量不传会用默认值 snapshot.Save(fullPath, ImageFormat.Jpeg); } // 可选在界面上给个反馈 lblStatus.Text $已保存{fileName}; }逻辑说明_latestFrame始终持有最新一帧的副本拍照时再Clone一次是为了防止存盘过程中采集线程继续改写_latestFrame指向的内存。虽然_latestFrame本身已经是副本但Save是 IO 操作耗时可能几十毫秒这期间新帧来了会把_latestFrame换掉你正在存的图就可能被释放。多Clone一次代价很小换来的是稳定。参数说明ImageFormat.Jpeg是存盘格式想存 PNG 就换ImageFormat.Png无损但文件大。JPEG 质量默认是 75 左右想控制质量得用EncoderParameters这个后面进阶部分讲。文件名用fff毫秒后缀防止一秒内连拍覆盖。3.3 关流与资源释放程序退出或者切换设备时必须把流关干净否则摄像头指示灯一直亮着下次打开还可能报「设备被占用」。private void Form1_FormClosing(object sender, FormClosingEventArgs e) { StopCamera(); _latestFrame?.Dispose(); }StopCamera就是前面写的SignalToStopWaitForStop。这里有个血泪经验如果你在FormClosing里只调SignalToStop不调WaitForStop程序退出时采集线程可能还在跑进程会残留任务管理器里能看到一个僵尸进程。加上WaitForStop就干净了。4. 避坑与排查那些年 AForge 抓摄像头翻过的车4.1 现象程序启动报 COMExceptionHRESULT 0x80070005原因项目平台目标设成了 AnyCPU在 64 位系统上以 64 位进程运行但某些摄像头驱动的 DirectShow 滤镜只注册了 32 位版本COM 组件加载失败。解决把项目平台目标显式改成 x86 或 x64跟驱动匹配。不确定驱动位数就两个都试一下工控机上 x86 兼容性通常更好。改完记得清理解决方案重新生成光改配置有时候不生效。4.2 现象预览画面卡顿、延迟越来越大原因NewFrame回调里做了耗时操作比如直接Save存盘、或者Invoke到 UI 线程时 UI 正忙导致采集线程被阻塞帧在缓冲区里堆积。解决回调里只做Clone和赋值存盘、图像处理全部挪到按钮事件或者独立线程。Invoke如果 UI 忙会阻塞可以改用BeginInvoke但要注意帧率太高时BeginInvoke会排队反而更卡。我的做法是在回调里判断一下如果上一帧还没处理完就跳过这一帧丢帧比堆积强。4.3 现象跑几个小时后程序崩溃报 OutOfMemoryException原因PictureBox.Image每次赋新图时旧图没释放GDI 对象持续泄漏。Windows 单进程 GDI 对象上限默认是 10000跑满就崩。解决每次赋新图前把旧图Dispose掉就是前面代码里old?.Dispose()那一步。另外_latestFrame换新的时候也要释放旧的。想确认泄漏可以在任务管理器里打开「GDI 对象」列盯着数字看正常应该稳定在几百一直涨就是漏了。4.4 现象切换摄像头后新设备打不开提示被占用原因旧设备的VideoCaptureDevice没有WaitForStop就置空了底层采集线程还在跑设备句柄没释放。解决切换前必须走完整的SignalToStopWaitForStop流程确认IsRunning变成 false 再构造新设备。如果还是不行可能是驱动层面的释放延迟加一个 200 毫秒的Thread.Sleep再开新流虽然土但管用。4.5 现象DesiredFrameSize 设了没效果实际分辨率不对原因DesiredFrameSize只是期望值设备驱动会挑最接近的支持格式。有些摄像头只支持特定几种分辨率组合你设的它不支持就给你换一个。解决先查设备支持哪些格式。VideoCaptureDevice有个VideoCapabilities属性是个VideoCapabilities数组遍历打印FrameSize和AverageFrameRate就能看到全部支持项。选一个设备真正支持的尺寸去设别拍脑袋写。// 打印设备支持的所有视频格式 foreach (VideoCapabilities cap in _videoSource.VideoCapabilities) { System.Diagnostics.Debug.WriteLine( $支持{cap.FrameSize.Width}x{cap.FrameSize.Height} {cap.AverageFrameRate}fps); }5. 进阶技巧JPEG 质量控制与定时连拍5.1 控制 JPEG 压缩质量默认Save出来的 JPEG 质量大概 75做存档够用但要做图像分析可能嫌糊。想精确控制质量得用EncoderParametersusing System.Drawing.Imaging; private void SaveJpegWithQuality(Bitmap bmp, string path, long quality) { // 获取 JPEG 编码器 ImageCodecInfo jpegEncoder ImageCodecInfo.GetImageEncoders() .FirstOrDefault(c c.FormatID ImageFormat.Jpeg.Guid); if (jpegEncoder null) { // 理论上不会发生但防御一下 bmp.Save(path, ImageFormat.Jpeg); return; } // 构造质量参数quality 范围 0-100 using (EncoderParameters parameters new EncoderParameters(1)) { parameters.Param[0] new EncoderParameter(Encoder.Quality, quality); bmp.Save(path, jpegEncoder, parameters); } }逻辑说明ImageCodecInfo.GetImageEncoders()返回系统所有图像编码器按FormatID筛出 JPEG 那个。EncoderParameters是个参数容器Encoder.Quality是质量参数值越高文件越大。我一般存档用 85做后续处理用 95再高收益就不明显了。参数说明quality传long类型范围 0 到 100。传 0 会得到极小但极糊的图传 100 文件可能比原图还大。实测 85 是画质和体积的甜点。5.2 定时连拍有些场景要每隔几秒拍一张比如做延时记录。用System.Windows.Forms.Timer最简单但要注意别在 Tick 里做重活否则 UI 卡private System.Windows.Forms.Timer _captureTimer; private void InitTimer() { _captureTimer new System.Windows.Forms.Timer(); // 间隔 5000 毫秒即 5 秒一张 _captureTimer.Interval 5000; _captureTimer.Tick (s, e) btnCapture_Click(s, e); } private void btnStartTimedCapture_Click(object sender, EventArgs e) { _captureTimer.Start(); lblStatus.Text 定时连拍已启动每 5 秒一张。; } private void btnStopTimedCapture_Click(object sender, EventArgs e) { _captureTimer.Stop(); lblStatus.Text 定时连拍已停止。; }逻辑说明Timer的Tick在 UI 线程上触发直接调拍照逻辑没问题因为拍照本身只是Clone加Save几十毫秒内能完成。但如果你的存盘路径是网络盘IO 可能慢那就得把存盘挪到Task.Run里避免 Tick 堆积。参数说明Interval单位是毫秒最小有效值大概 15 毫秒再小没意义。连拍间隔根据场景定做延时摄影可以设 1000 到 5000做监控存档可以设 60000。5.3 一个验证小技巧写完拍照功能别急着上产线。我一般会做一个「连拍 100 张」的测试按钮跑完之后去任务管理器看 GDI 对象数有没有回落再看内存占用是否稳定。如果 GDI 数一直涨说明有泄漏回去查Dispose。这个习惯帮我提前拦下过好几次问题比上线后半夜被叫起来强。从那以后我每次用 AForge 抓摄像头都会先把Dispose链路和WaitForStop走一遍再交付。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

单片机教学闭环试卷:从寄存器操作到硬件时序验证

单片机教学闭环试卷:从寄存器操作到硬件时序验证

简介:本资源是一份面向高校计算机类专业学生的《单片机原理及应用》与《微机原理及应用》课程配套考试试卷(A卷)及完整参考答案,适用于期末复习、考前自测与知识点查漏补缺。试卷涵盖MCS-51单片机结构、寻址方式、指令系统、堆栈操…

2026/10/18 13:08:47 阅读更多 →
通信施工安全技术交底样本:从编制到落地的全流程指南

通信施工安全技术交底样本:从编制到落地的全流程指南

简介:这份通信施工安全技术交底样本面向通信工程现场施工人员、项目负责人及安全管理人员,用于规范施工前的安全技术交底流程,降低现场作业事故风险。内容围绕基本规定、工具使用安全、开箱操作、立机架、放线缆、作业安全与技术规范等模块展…

2026/10/18 13:08:47 阅读更多 →
AI搜索优化实战:从内容可信度到被引用排名的完整指南

AI搜索优化实战:从内容可信度到被引用排名的完整指南

1. 先搞懂一件事:AI搜索的“排名”到底是什么1.1 从“检索列表”到“生成答案”,底层逻辑变了你花了大半年把网站优化到了某个关键词的第一页,结果用户打开AI搜索引擎问了一句类似的问题,它直接生成了一段完整答案,你的…

2026/10/18 13:08:47 阅读更多 →

最新新闻

第五章 C++20标准的改进(代码说明)

第五章 C++20标准的改进(代码说明)

// cpp20_demo.cpp // 编译: g -stdc20 -pthread cpp20_demo.cpp -o cpp20_demo // 运行: ./cpp20_demo#include <iostream> // 输入输出 #include <format> // C20 格式化库 #include <concepts> // C20 概念 #include <ranges> …

2026/10/18 13:56:12 阅读更多 →
MATLAB语音加噪去噪课设全流程:FIR与IIR滤波器设计对比与评估

MATLAB语音加噪去噪课设全流程:FIR与IIR滤波器设计对比与评估

简介&#xff1a;基于MATLAB的含噪语音信号处理&#xff0c;是数字信号处理课程设计中兼顾理论与工程实现的典型课题。该方案针对带有噪声的语音信号进行时域与频域分析&#xff0c;使用窗函数法完成FIR数字滤波器设计&#xff0c;并采用巴特沃斯、切比雪夫及双线性变换法设计I…

2026/10/18 13:56:12 阅读更多 →
artcraft:像素级用户体验的系统化工程方法

artcraft:像素级用户体验的系统化工程方法

1. 项目概述&#xff1a;从“artcraft”这个词开始&#xff0c;我们到底在做什么&#xff1f;“artcraft”这个词乍看像一个合成词&#xff0c;拆开是 art&#xff08;艺术&#xff09;和 craft&#xff08;工艺&#xff09;&#xff0c;合起来直译就是“艺术工艺”。但放在当下…

2026/10/18 13:56:12 阅读更多 →
用Trae开发大模型智能应用:一位工程师的实战复盘

用Trae开发大模型智能应用:一位工程师的实战复盘

最近后台有朋友问我最多的一句话是&#xff1a;“你说你用Trae写大模型应用&#xff0c;到底是个什么体验&#xff1f;”说实话&#xff0c;半年前我也对AI编程助手半信半疑&#xff0c;总觉得代码还得自己一行行敲才靠谱。直到我把一个智能问答Demo从需求到上线完整跑了一遍&a…

2026/10/18 13:56:12 阅读更多 →
豆瓣图书爬虫实战:Requests+BeautifulSoup+openpyxl 打造干净xlsx数据

豆瓣图书爬虫实战:Requests+BeautifulSoup+openpyxl 打造干净xlsx数据

简介&#xff1a;这份资源收录了豆瓣图书爬虫脚本与11个分类整理好的Excel数据文件&#xff0c;面向正在学习Python网络爬虫和数据分析的初学者&#xff0c;也适合需要图书榜单数据的个人开发者。爬虫部分基于requests与BeautifulSoup编写&#xff0c;完整展示了从构造请求、处…

2026/10/18 13:56:12 阅读更多 →
MATLAB/Simulink准同期并网仿真:三判据建模、导前时间补偿与合闸冲击电流分析

MATLAB/Simulink准同期并网仿真:三判据建模、导前时间补偿与合闸冲击电流分析

简介&#xff1a;这份资源是面向电气工程专业学生与电力系统仿真学习者的毕设论文文档&#xff0c;聚焦同步发电机并网中的准同期并列问题&#xff0c;帮助读者理解均频、均压与合闸单元的工作原理&#xff0c;并掌握用MATLAB/Simulink搭建准同期装置仿真模型的方法。压缩包内共…

2026/10/18 13:55:11 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/18 12:16:16 阅读更多 →