ObjectARX扩展记录Xrecord详解:从ResBuf链表到扩展字典的完整实践
1. 为什么扩展记录值得单独写一篇笔记做ObjectARX开发的人绕不开一个现实问题图纸里的自定义数据往哪儿放。你可能已经用过扩展数据XData往实体上挂几个字符串、几个坐标点简单直接。但用着用着就会发现XData有它的天花板——容量有限、类型受限、组织方式也比较扁平。当你需要存储一组结构化的配置信息、一棵有层级关系的参数树或者一段需要跟图纸一起走的业务数据时XData就开始捉襟见肘了。扩展记录Xrecord就是在这个场景下进入视野的。它本质上是一种可以挂在任意数据库对象上的容器内部用结果缓冲区ResBuf链表来组织数据容量比XData大得多结构也更灵活。你可以把它理解成一个“随图纸携带的小型数据仓库”——不依赖外部文件不破坏DWG格式的兼容性保存即持久化打开即读取。这篇笔记适合两类人一是已经写过基础ObjectARX代码、对AcDbObject体系有初步认识的开发者二是正在为“自定义数据存哪里”这个问题纠结、想找一个比XData更靠谱方案的技术人员。我会从Xrecord的底层结构讲起把创建、写入、读取、挂载、释放的完整链路拆开再补上几个实际项目中踩过的坑和验证过的处理方式。代码基于ObjectARX的通用接口不绑定特定版本你可以直接对照自己的工程调整。2. Xrecord的底层结构结果缓冲区链表到底怎么组织数据2.1 从AcDbObject继承而来的“数据容器”本质Xrecord在类继承体系里是AcDbObject的子类这意味着它具备所有数据库常驻对象的基本能力有对象ID、可以被拥有者引用、参与DWG的保存与加载。但它和普通实体AcDbEntity不同它没有图形表现不会在屏幕上画出来纯粹是一个数据载体。一个Xrecord对象内部维护的是一个AcDbResBuf链表。这个链表是单向的每个节点是一个结果缓冲区里面包含数据类型码restype和数据值resval。数据类型码决定了这个节点存的是什么——字符串、整数、实数、坐标点、对象ID甚至是嵌套的链表。这种设计的好处是你可以在一个Xrecord里塞进任意组合的数据结构只要能用ResBuf表达出来。举个例子假设你要存一个“设备参数”的记录包含设备名称字符串、编号整数、安装坐标三维点、关联图块ID对象ID。用Xrecord的话就是往链表里依次追加四个ResBuf节点每个节点的restype分别是AcDb::kDxfXTextString、AcDb::kDxfXInt16、AcDb::kDxfXCoord、AcDb::kDxfHardPointerId。读取的时候按顺序遍历逐个还原。注意Xrecord内部链表的数据类型码用的是Dxf组码的扩展范围1000以上不要直接套用普通实体的组码否则在保存和读取时可能出现意料之外的映射问题。2.2 与XData的关键差异容量、层级和所有权很多人会问既然XData也能挂数据为什么还要用Xrecord我把两者的核心差异整理成了一张表方便你快速判断该用哪个。对比维度XDataXrecord单对象容量每个实体最多16KB理论无硬性上限受DWG文件大小约束数据类型受限于Dxf组码不支持嵌套支持任意ResBuf组合可嵌套链表组织结构扁平列表链表可表达层级关系所有权直接挂在实体上通过扩展字典挂载有独立对象ID访问方式随实体一起读写需要先获取扩展字典再查找条目适用场景少量简单标记结构化配置、参数树、业务数据从表里能看出来Xrecord的核心优势在于“结构化”和“大容量”。当你需要存储的数据超过几十个字段或者字段之间有层级关系比如一个配置项下面还有子项Xrecord是更合适的选择。2.3 扩展字典Xrecord的“挂载点”Xrecord本身不会凭空挂在实体上它需要一个“入口”——这就是扩展字典Extension Dictionary。每个AcDbObject都可以通过createExtensionDictionary()方法创建一个扩展字典这个字典是一个AcDbDictionary对象里面可以存放多个条目每个条目有一个名称和一个对象ID。Xrecord就是作为字典中的一个条目存在的。整个挂载链路是这样的实体 → 扩展字典 → 字典条目 → Xrecord → ResBuf链表。读取的时候反向操作先拿到实体的扩展字典再按名称查找条目得到Xrecord的对象ID打开后遍历链表。这个结构听起来多了一层但好处很明显一个实体可以挂多个Xrecord用不同的名称区分Xrecord可以独立于实体被引用和修改删除时也可以按条目精确清理不会影响其他数据。3. 创建与写入从零构建一个Xrecord的完整链路3.1 打开扩展字典并准备写入环境在写入Xrecord之前必须先确保目标对象有扩展字典。如果对象还没有扩展字典需要先创建。下面这段代码展示了标准的准备流程// 假设pEnt是已经打开用于写入的实体指针 AcDbObjectId extDictId; if (pEnt-extensionDictionary() AcDbObjectId::kNull) { // 没有扩展字典创建一个 pEnt-createExtensionDictionary(); } extDictId pEnt-extensionDictionary(); // 以写入模式打开扩展字典 AcDbDictionary* pExtDict nullptr; acdbOpenObject(pExtDict, extDictId, AcDb::kForWrite); if (pExtDict nullptr) { // 错误处理字典打开失败 return; }这里有几个细节值得注意。第一createExtensionDictionary()只需要调用一次重复调用不会报错但也没有意义。第二打开字典时一定要用kForWrite模式因为接下来要往字典里添加条目。第三如果实体本身是以kForRead模式打开的createExtensionDictionary()会失败所以确保实体已经以写入模式打开。3.2 构建ResBuf链表数据组织的核心步骤ResBuf链表的构建是Xrecord写入的核心。每个节点用acutNewRb()分配设置restype和resval然后用rb-rbnext指向下一个节点。下面是一个构建示例存储设备名称、编号和坐标// 构建ResBuf链表 struct resbuf* head acutNewRb(AcDb::kDxfXTextString); head-resval.rstring _tcsdup(_T(泵组A)); struct resbuf* node2 acutNewRb(AcDb::kDxfXInt16); node2-resval.rint 1001; head-rbnext node2; struct resbuf* node3 acutNewRb(AcDb::kDxfXCoord); node3-resval.rpoint[0] 100.0; node3-resval.rpoint[1] 200.0; node3-resval.rpoint[2] 0.0; node2-rbnext node3; node3-rbnext nullptr; // 链表结束构建链表时最容易出错的地方是内存管理。acutNewRb()分配的内存需要手动释放字符串类型的rstring也需要用free()释放。如果中间某个节点分配失败已经分配的节点也要清理否则就是内存泄漏。我通常会把构建过程封装成一个函数在函数内部用try-catch或者错误码来保证异常情况下的资源回收。提示如果数据量较大建议先用std::vector或自定义结构体组织好数据再统一转换为ResBuf链表。这样代码更清晰也便于调试。3.3 创建Xrecord并写入字典链表构建完成后就可以创建Xrecord对象并写入扩展字典了// 创建Xrecord对象 AcDbXrecord* pXrec new AcDbXrecord(); // 将ResBuf链表设置到Xrecord中 pXrec-setFromRbChain(*head); // 将Xrecord添加到扩展字典指定条目名称 AcDbObjectId xrecId; pExtDict-setAt(_T(DeviceParams), pXrec, xrecId); // 关闭字典 pExtDict-close(); // 释放ResBuf链表内存 acutRelRb(head);setFromRbChain()方法会复制链表中的数据所以调用之后就可以安全地释放原始链表了。setAt()方法将Xrecord以指定名称写入字典并返回它的对象ID。这个对象ID在后续读取时可以用来直接打开Xrecord也可以每次都通过名称从字典中查找。这里有一个经验性的建议条目名称尽量用有意义的英文或拼音避免特殊字符和空格。虽然AutoCAD的字典名称支持中文但在跨版本、跨平台的数据交换中中文名称偶尔会出现编码问题。用英文名称是最稳妥的做法。4. 读取与解析把链表数据还原成业务结构4.1 从实体反查扩展字典并定位Xrecord读取的流程和写入正好相反。假设你有一个实体知道它上面挂了一个名为“DeviceParams”的Xrecord读取代码如下// 获取实体的扩展字典ID AcDbObjectId extDictId pEnt-extensionDictionary(); if (extDictId AcDbObjectId::kNull) { // 没有扩展字典说明没有挂载任何Xrecord return; } // 打开扩展字典 AcDbDictionary* pExtDict nullptr; acdbOpenObject(pExtDict, extDictId, AcDb::kForRead); // 查找指定名称的条目 AcDbObjectId xrecId; if (pExtDict-getAt(_T(DeviceParams), xrecId) ! Acad::eOk) { pExtDict-close(); return; // 条目不存在 } pExtDict-close(); // 打开Xrecord AcDbXrecord* pXrec nullptr; acdbOpenObject(pXrec, xrecId, AcDb::kForRead);这段代码里getAt()的返回值一定要检查。如果字典里没有对应名称的条目getAt()会返回错误码xrecId保持无效值。直接拿无效ID去acdbOpenObject()会返回空指针后续操作就会崩溃。4.2 遍历ResBuf链表并提取数据打开Xrecord之后用rbChain()方法获取内部链表的副本然后逐个节点解析struct resbuf* pRb nullptr; pXrec-rbChain(pRb); struct resbuf* pNode pRb; while (pNode ! nullptr) { switch (pNode-restype) { case AcDb::kDxfXTextString: // 处理字符串 CString strVal(pNode-resval.rstring); break; case AcDb::kDxfXInt16: // 处理整数 int nVal pNode-resval.rint; break; case AcDb::kDxfXCoord: // 处理坐标点 double x pNode-resval.rpoint[0]; double y pNode-resval.rpoint[1]; double z pNode-resval.rpoint[2]; break; default: break; } pNode pNode-rbnext; } // 释放链表内存 acutRelRb(pRb); pXrec-close();rbChain()返回的链表是内部数据的副本所以用完之后必须用acutRelRb()释放。这一点和setFromRbChain()是对称的写入时复制进去读取时复制出来双方各自管理自己的内存。解析时的关键点是“按顺序对应”。因为ResBuf链表是有序的写入时第一个节点是名称、第二个是编号、第三个是坐标读取时就要按同样的顺序解析。如果写入和读取的代码不是同一个人维护建议在链表头部加一个版本号或类型标识节点方便后续扩展时做兼容处理。4.3 处理嵌套链表和可选字段Xrecord支持嵌套链表也就是说某个ResBuf节点的restype如果是AcDb::kDxfXListBegin后面跟着的就是一个子链表直到遇到AcDb::kDxfXListEnd。这种结构适合表达“一个配置项下面有多个子项”的场景。处理嵌套链表时遍历逻辑需要改成递归或者用栈来管理层级。我个人的做法是写一个递归函数遇到kDxfXListBegin就进入下一层遇到kDxfXListEnd就返回上一层。这样代码结构清晰也便于处理任意深度的嵌套。对于可选字段建议在链表末尾追加一个“结束标记”节点restype用一个自定义的值比如AcDb::kDxfXInt32且值为-1。读取时如果提前遇到结束标记就知道后面的字段不存在用默认值填充即可。这样比靠节点数量判断更可靠也更容易向后兼容。5. 实际项目中的几个坑与处理方式5.1 对象打开模式不匹配导致的写入失败这是最常见的问题之一。很多人在写入Xrecord时实体是以kForRead模式打开的然后调用createExtensionDictionary()或者setAt()结果返回错误码或者直接崩溃。原因很简单修改实体的扩展字典本质上是在修改实体本身必须以kForWrite模式打开实体。正确的做法是在写入之前确保实体已经以kForWrite模式打开。如果实体是从选择集或者遍历中获取的默认可能是kForRead模式需要先upgradeOpen()或者关闭后重新以kForWrite打开。upgradeOpen()在大多数情况下是可行的但如果实体正在被其他操作引用可能会失败这时候就需要关闭再重开。注意upgradeOpen()之后如果后续操作失败需要回退记得调用downgradeOpen()恢复原来的打开模式否则会影响其他逻辑。5.2 字典条目名称冲突与覆盖策略扩展字典是一个名称到对象ID的映射同一个名称只能对应一个条目。如果你用setAt()写入一个已经存在的名称AutoCAD会怎么处理答案是旧条目会被自动擦除新条目取而代之。这个行为在大多数情况下是符合预期的但如果你不希望覆盖旧数据就需要先用getAt()检查名称是否已存在。我在一个项目中遇到过这样的情况多个模块往同一个实体的扩展字典里写Xrecord用了相同的名称结果后写入的模块把先写入的数据覆盖了导致先写入的模块读取时拿到的是错误的数据。后来我们约定了一套命名规范每个模块用自己的前缀比如“MODA_Config”、“MODB_Params”彻底避免了冲突。5.3 Xrecord的释放与图纸清理Xrecord作为数据库常驻对象会随着DWG文件一起保存。如果不再需要某个Xrecord应该从扩展字典中移除它并确保没有其他引用否则它会一直留在图纸里增加文件大小。移除Xrecord的正确方式是打开扩展字典kForWrite调用remove()方法删除指定名称的条目。remove()会将条目的引用计数减一如果计数归零对象会被标记为删除在下一次保存时从数据库中清除。需要注意的是如果Xrecord被其他对象通过硬指针引用比如某个实体的某个字段存了它的对象ID单纯从字典中remove()并不会真正删除它因为引用计数没有归零。这种情况下需要先清理所有引用再remove()。实际项目中我建议Xrecord只通过扩展字典引用不要在其他地方存它的对象ID这样生命周期管理最简单。5.4 跨版本兼容性与数据迁移不同版本的ObjectARX在ResBuf类型码的支持上可能有细微差异。比如某些高版本新增的类型码在低版本中不存在如果图纸在高版本中保存了包含这些类型码的Xrecord在低版本中打开时可能会读取失败。为了避免这个问题我在定义Xrecord数据结构时遵循两个原则第一只使用基础类型码字符串、整数、实数、坐标点、对象ID这些在所有版本中都支持第二在链表头部加一个版本号节点读取时先检查版本号如果当前代码不支持这个版本就跳过或者给出提示而不是强行解析导致崩溃。如果确实需要迁移数据可以写一个转换函数读取旧版本的Xrecord按照旧的结构解析然后按照新的结构重新构建链表写入新的Xrecord最后删除旧的。这个过程最好在图纸打开时自动执行一次并记录一个标记避免重复迁移。6. 几个值得记住的实操心得Xrecord的写入和读取本身不复杂但细节决定成败。我把自己反复验证过的几条经验整理出来供你参考。第一ResBuf链表的内存管理要成对出现。acutNewRb()对应acutRelRb()setFromRbChain()对应rbChain()写入时复制进去读取时复制出来双方各自释放自己的副本。不要试图共享同一块内存否则很容易出现双重释放或者悬空指针。第二扩展字典的创建时机要把握好。如果实体还没有扩展字典createExtensionDictionary()必须在实体以kForWrite模式打开时调用。如果实体已经有关联的扩展字典重复调用不会报错但也没有必要。第三条目名称用英文加下划线避免空格和特殊字符。虽然AutoCAD内部支持Unicode名称但在数据交换和日志输出时英文名称更省心。第四读取时永远检查返回值。getAt()可能失败acdbOpenObject()可能返回空指针rbChain()可能返回空链表。每一步都做防御性检查代码才能在实际图纸环境中稳定运行。第五如果Xrecord的数据结构可能变化在链表头部加版本号。这个小小的节点会在后续维护中省下大量排查时间。第六测试时不要只测正常流程。构造一些边界情况空链表、只有部分字段、嵌套链表深度很大、名称包含特殊字符看看代码是否都能正确处理。我在实际项目中遇到的崩溃大多数都是在这些边界情况下暴露出来的。Xrecord这个机制用熟了之后会发现它比XData顺手得多。它给了你足够的自由度去组织数据同时又保持了与DWG格式的紧密集成。掌握它的关键在于理解ResBuf链表的组织方式和扩展字典的挂载逻辑剩下的就是按部就班地写代码和做防御性检查。希望这篇笔记能帮你在下一个ObjectARX项目中少走一些弯路。

相关新闻

蓝桥杯兰顿蚂蚁:DFS递归与迭代解法详解及性能对比

蓝桥杯兰顿蚂蚁:DFS递归与迭代解法详解及性能对比

1. 从一道蓝桥杯老题说起:兰顿蚂蚁到底在考什么兰顿蚂蚁(Langtons Ant)是蓝桥杯历届试题里一道非常经典的模拟类题目,出现在多个年份的省赛和国赛题库中。题目本身描述并不复杂:在一个方格矩阵上,蚂蚁按照固…

2026/10/13 13:12:23 阅读更多 →
Trae Fast Apply 实战:用 TaoToken 统一 Key 打通 AI 代码合并与回滚链路

Trae Fast Apply 实战:用 TaoToken 统一 Key 打通 AI 代码合并与回滚链路

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

2026/10/13 18:05:10 阅读更多 →
【Openclaw】Codex 认证失败排查:把 auth.json 改到 TaoToken 的完整配置

【Openclaw】Codex 认证失败排查:把 auth.json 改到 TaoToken 的完整配置

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

2026/10/13 18:17:10 阅读更多 →

最新新闻

imgsTools图片合并实战:基于Python和Pillow的拼接与批处理

imgsTools图片合并实战:基于Python和Pillow的拼接与批处理

简介:这是一份面向游戏开发者的图片合并工具 imgsTools 完整工程,基于 VALib 图像库与 MFC、GDI 技术实现,用于把多张图片合并为透明 PNG、GIF、JPEG,并生成 Cocos2d 可识别的 .plis 资源清单。PNG 适合精细界面元素与图标&#x…

2026/10/13 21:29:24 阅读更多 →
litellm 实战:统一多模型调用接口与代理层架构设计

litellm 实战:统一多模型调用接口与代理层架构设计

1. 项目概述与核心定位1.1 这个项目到底解决什么问题第一次接触 litellm 是在一个多模型混用的项目里。当时团队同时接入了好几家不同厂商的大模型服务,每家的接口格式、鉴权方式、返回结构都不一样,代码里到处是 if-else 分支,维护起来非常头…

2026/10/13 21:29:24 阅读更多 →
遗传算法优化SVM参数:Python实战与调参避坑指南

遗传算法优化SVM参数:Python实战与调参避坑指南

简介:这份资源面向具备一定Python与机器学习基础、希望掌握超参数调优实战的学习者,聚焦用遗传算法(GA)自动搜索支持向量机(SVM)的最优参数组合,解决人工调参效率低、易陷入局部最优的问题。压缩…

2026/10/13 21:29:24 阅读更多 →
多智能体协作系统实战:用AI Agent搭建虚拟开发团队

多智能体协作系统实战:用AI Agent搭建虚拟开发团队

我花了两周时间,把一个需要四个人并行开发的内部项目,硬生生塞给了一组 AI Agent。说出来你可能不信,最后跑通的产出质量,比我团队里两位中级开发交出来的第一版都要干净。这个想法不是拍脑袋冒出来的。当时我手里压着两个并行需求…

2026/10/13 21:29:24 阅读更多 →
大数据题库拆解:从Hadoop到HBase的备考框架

大数据题库拆解:从Hadoop到HBase的备考框架

简介:这份测试题围绕林子雨《大数据技术原理应用》课程内容整理,适合高校相关专业学生、考研复习者及自学大数据的入门读者巩固理论、考前自测。压缩包内共包含1个Word文档(docx),约58页,整体体积仅76KB&am…

2026/10/13 21:29:24 阅读更多 →
分布式光伏集中监控系统的架构设计:协议适配与告警治理实践

分布式光伏集中监控系统的架构设计:协议适配与告警治理实践

做分布式光伏管理系统之前,我一直觉得“集中监控”四个字不过是把各电站的逆变器数据拉到一个页面上。真到自己动手做的时候才发现,这个页面背后的数据链路、协议适配、告警治理,每一项都是坑。尤其当电站分布在多个省份,屋顶、地…

2026/10/13 21:28:23 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 21:05:09 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →