Vue+Three.js实现PLY三维模型展示的完整实践
做前端项目时我经常遇到一种“看起来不难实际要踩不少坑”的需求。去年接了个项目要把一批PLY格式的三维扫描模型直接嵌到Vue后台系统里浏览器打开就能看还要支持鼠标旋转、缩放、平移。这个活儿听起来简单无非是加载模型、丢进场景、渲染出来。但真正落地时文件格式的坑、渲染器的选型、Vue组件生命周期的配合、大点云模型的性能压制每一个环节都藏着细节。这篇文章就把我从零到一跑通“PLY三维模型在vue中的展示”的完整过程拆开讲清楚包括为什么选Three.js、Vue环境怎么搭、核心代码怎么组织、遇到问题怎么排查最后再分享几个我实际工作里觉得非常管用的经验。1. 先理清PLY文件到底在说什么1.1 一次读懂PLY的文件头PLY全称是Polygon File Format也叫Stanford Triangle Format最初是斯坦福大学搞三维扫描研究时定义的格式。它的核心结构非常规矩ASCII或者二进制的文件头后面跟着顶点数据、面数据可能还带有颜色、法线、纹理坐标这些属性。文件头最典型的结构长这样ply format ascii 1.0 element vertex 8 property float x property float y property float z property uchar red property uchar green property uchar blue element face 6 property list uchar int vertex_indices end_header这里每一行都值得看format告诉解析器这是一份ASCII文本文件还是二进制文件版本号是1.0还是2.0element vertex声明了顶点数量下面的property定义了每个顶点有哪些字段。上面这个例子意味着每个顶点除了xyz坐标还有RGB颜色后面还有6个面。解析器真正处理之前会先按文件头构建数据布局因此如果文件头写错一个变量名渲染出来的模型大概率就是错位或者完全空白。1.2 三种容易搞混的PLY变体工作中见到的PLY大致能分成三类处理方式差别很大ASCII文本型直接用记事本打开能看到坐标文本数据量大的时候文件相当大但调试方便格式出问题也好排查。二进制little-endian型文件头里写的是format binary_little_endian 1.0CPU和内存中常见的数据排列方式绝大多数三维扫描仪导出时默认选这个。二进制big-endian型字节序反过来多见于某些老硬件设备前端处理时需要特别注意字节顺序。更值得留意的是PLY不一定都是三角网格。很多测绘或扫描设备导出的是不含面数据的点云PLY文件头里只有element vertex没有element face。加载这类文件时渲染思路要从“画三角形”切换成“画点”否则模型永远显示不出来。判断一个PLY是网格还是点云不用等加载完打开文件头看有没有face元素就一目了然。2. 前端渲染方案取舍为什么我选了Three.js2.1 备选方案的真实体验拿到需求后我第一反应是找现成的“展示PLY”工具确实有几个备选Potree、Three.js、以及自己写WebGL。Potree对超大规模点云优化做得非常好但它本身是一套相对独立的体系整个工程以点云为核心想把它塞进Vue里做深度定制API风格和Three.js不太一样二次开发成本偏高。自己写WebGL更不现实解析PLY看着简单真要手写BufferGeometry上传、处理顶点着色器、相机矩阵、深度测试、抗锯齿没有几周时间根本稳不下来。这时候Three.js的优势就出来了它的PLYLoader直接读PLY文件输出BufferGeometry配合OrbitControls就能实现鼠标交互官方Example里也有现成参考。对绝大多数Vue项目来说Three.js是性价比最高的一条路。2.2 PLYLoader内部做了什么用PLYLoader表面上是“load一个文件”实际上它做了解析、数据转换、生成几何体三件事。它会按照文件头声明逐个读取顶点坐标、颜色、法线等属性把这些原始数据装进BufferAttribute再组成Three.js能识别的BufferGeometry。比如顶点数据会变成一个position数组颜色数据如果是uchar类型PLYLoader会自动做标准化处理把它从0到255的范围换算成0到1的浮点数这个细节直接关系到后面颜色渲染是否正确。也正是因为它帮你处理了这部分使用时必须保持Three.js版本稳定。不同版本的PLYLoader对于法线缺失、颜色缺失、非三角面的处理逻辑略有不同升级Three.js版本后之前能正常显示的PLY可能就变了样。2.3 把渲染逻辑封装成独立Vue组件这个方案里最值得做的一件事是把Three.js相关代码封装成一个独立的Vue组件通常叫PlyViewer.vue。理由很直接Vue组件负责管理DOM容器和生命周期Three.js负责创建WebGL上下文、管理场景和动画循环两者通过onMounted和onUnmounted衔接。如果不做组件隔离把渲染器绑定在页面级写法上页面一旦存在多个模型展示场景代码会迅速失控。组件内部还要负责在切换路由或关闭弹窗时释放WebGL资源这个“释放”动作在原生JS里经常被忽略但放在Vue里如果不处理反复切换页面后浏览器会报“Too many active WebGL contexts”。3. 搭建Vue环境与安装依赖3.1 用Vite快速创建Vue项目当前我推荐Vue 3 Vite组合启动速度快依赖处理也干净。创建项目可以直接用命令行的交互方式npm create vitelatest ply-vue-demo -- --template vue然后按需安装依赖cd ply-vue-demo npm install npm install three不需要额外安装three的typescript类型包因为Three.js本身自带类型声明。很多人会顺手装一个types/three其实从某个版本开始已经没有必要了装了反而可能出现重复声明问题。如果要处理文件上传、解析本地PLY再按需引入后续的库即可。3.2 Vite配置上要留心的点项目跑起来之前我习惯先确认Vite配置里有没有影响.ply文件加载的项。Three.js的loader.load()在开发环境和生产环境走的都是HTTP请求。如果PLY文件放在public/models/目录下开发时直接访问/models/xxx.ply生产环境打包后也会原样输出到根目录。放在src/assets里反而麻烦因为Vite会对资源做指纹重命名而PLYLoader是按URL字符串去加载的文件名一旦被hash处理就很容易出现路径对不上。生产环境如果部署在子目录还要注意base配置不然模型路径会变成从根目录找文件结果是接口通了、场景黑屏模型文件404。3.3 版本一致性的隐性坑Three.js的生态迭代非常快常见的大版本之间API变化不小。OrbitControls早期从three/examples/jsm/controls/OrbitControls.js导入现在的路径基本延续这个规范。PLYLoader同样在three/examples/jsm/loaders/PLYLoader.js。如果你在网上搜代码很可能搜到旧写法比如直接import * as THREE from three再挂载PLYLoader或者从three/examples/js/...导入这些写法在新版本里已经不可靠了。依赖安装完成后先写一句console.log(THREE.REVISION)确认版本再动手能省掉很多不必要的调试时间。4. 在Vue组件中实现PLY加载与渲染4.1 组件模板与生命周期设计组件模板里不需要太多东西一个占满容器的div就够了。Three.js会在这个容器内部创建canvas所以容器高度一定要明确最好是固定高度或者用绝对定位撑满父元素。很多新手写渲染器时场景、相机、渲染器都创建了但容器高度是0模型自然看不见。template div refcontainerRef classply-viewer/div /template组件内部的核心逻辑围绕生命周期展开onMounted初始化Three.js场景、相机、渲染器、轨道控制器然后调用加载方法。onBeforeUnmount停止动画循环释放渲染器移除事件监听销毁几何体和材质。这样设计的原因是WebGL上下文数量有限。如果用户从A页面跳到B页面再跳回来旧组件没有被销毁浏览器会慢慢积累多个WebGL上下文最后页面直接卡死。4.2 初始化场景、相机与渲染器在setup函数里定义一个initThree方法三个核心对象必须正确配置import * as THREE from three import { OrbitControls } from three/examples/jsm/controls/OrbitControls.js import { PLYLoader } from three/examples/jsm/loaders/PLYLoader.js const containerRef ref(null) let scene, camera, renderer, controls, animationId function initThree() { const container containerRef.value const width container.clientWidth const height container.clientHeight scene new THREE.Scene() scene.background new THREE.Color(0x1a1a2e) camera new THREE.PerspectiveCamera(45, width / height, 0.1, 1000) camera.position.set(2, 2, 2) camera.lookAt(0, 0, 0) renderer new THREE.WebGLRenderer({ antialias: true }) renderer.setSize(width, height) renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) container.appendChild(renderer.domElement) controls new OrbitControls(camera, renderer.domElement) controls.enableDamping true }这里面有几个细节setPixelRatio不要直接传window.devicePixelRatio在4K屏上会真的把渲染分辨率拉满性能会不好。限制到2就能兼顾清晰度和流畅度。OrbitControls的enableDamping开启后旋转操作会有很舒服的惯性感但记得要在动画循环里调用controls.update()否则没有效果。4.3 加载PLY并处理网格与点云加载PLY的核心逻辑很简单关键是加载后根据几何体类型选择渲染方式function loadPLY(url) { const loader new PLYLoader() loader.load( url, (geometry) { geometry.computeVertexNormals() if (geometry.hasPositions geometry.index) { const material new THREE.MeshStandardMaterial({ vertexColors: geometry.hasColors, side: THREE.DoubleSide, }) const mesh new THREE.Mesh(geometry, material) scene.add(mesh) } else { const material new THREE.PointsMaterial({ size: 0.02, vertexColors: geometry.hasColors, sizeAttenuation: true }) const points new THREE.Points(geometry, material) scene.add(points) } fitCameraToModel(geometry) }, (xhr) { console.log(加载进度, (xhr.loaded / xhr.total) * 100 %) }, (err) { console.error(加载PLY失败, err) } ) }判断是网格还是点云我习惯看geometry.index是否存在。BufferGeometry有索引说明存在顶点之间的连接关系可以画三角形没有索引大概率是纯点云数据。颜色方面也别忽略如果PLY文件头里声明了red/green/blue那么geometry.hasColors就是true材质要开vertexColors否则颜色丢失模型显示成一整块灰白色看起来像损坏了。4.4 自适应尺寸与动画循环动画循环使用requestAnimationFrame但一定把返回值存下来方便组件卸载时取消function animate() { animationId requestAnimationFrame(animate) controls.update() renderer.render(scene, camera) } function handleResize() { const container containerRef.value const width container.clientWidth const height container.clientHeight camera.aspect width / height camera.updateProjectionMatrix() renderer.setSize(width, height) }组件挂载后还要监听窗口resize事件同时建议用ResizeObserver监听容器尺寸变化。因为在后台管理系统里容器宽度经常受侧边栏折叠、弹窗展开影响只有window的resize事件是不够的。5. 性能优化与大数据量PLY的处理5.1 先算算顶点数量做性能优化前先搞明白自己面对的是多大数量级。普通机械零件的三维扫描PLY顶点数可能几万到几十万WebGL渲染起来压力不大。但激光雷达扫描出来的点云PLY动辄几百万甚至上千万个点直接丢给Three.js渲染旋转起来就会明显掉帧。一个非常粗略的经验是WebGL单帧绘制100万点普通显卡还能勉强保持流畅到500万点以上如果没有做抽稀或分块交互体验基本就崩了。所以在生产环境里不能只靠前端扛必要时后端要先对点云做抽稀或转格式。5.2 实际操作中最管用的几个优化手段点云抽稀如果服务端能控制导出时就把PLY的点间距拉大或者使用MeshLab、CloudCompare这类工具导出简化版模型。前端做抽稀也可以比如每隔几个点保留一个但数据量超大时解析本身就是压力。使用Dithering代替半透明点云渲染经常要调透明度太多半透明点会触发大量深度排序计算可以尝试关闭transparent用尺寸更小的点替换。动态调整sizeAttenuation点云的点大小在sizeAttenuation开启时会随相机距离变化。对超大点云关闭衰减并设定固定点大小反而能减少渲染开销。模型翻模简化网格PLY面数过高时先用工具做网格简化保留特征的前提下把面数降到合理范围前端渲染压力立刻小很多。5.3 组件销毁时的内存回收内存回收是很多人容易忽略的。Three.js创建的对象不会被浏览器自动垃圾回收因为它们之间存在循环引用而且GPU显存里的数据需要手动释放。组件销毁时正确的顺序是function disposeResources() { cancelAnimationFrame(animationId) controls.dispose() scene.traverse((obj) { if (obj.geometry) obj.geometry.dispose() if (obj.material) { const materials Array.isArray(obj.material) ? obj.material : [obj.material] materials.forEach((m) m.dispose()) } }) renderer.dispose() if (containerRef.value renderer.domElement) { containerRef.value.removeChild(renderer.domElement) } }这样做之后反复切换路由、重复加载模型都不会出现WebGL上下文溢出的情况。我测量过一个极端场景不释放资源的组件切换30次之后浏览器打开任务管理器能看到GPU内存持续增长释放资源之后内存基本能回到初始水平。6. 常见问题排查与解决方案下面这张表是实际项目里最常遇到的几类问题按这条路径排查大部分都能快速定位现象可能原因处理方式模型完全不显示控制台也无错误容器高度为0或模型坐标为超大值给容器设置高度用Box3计算包围盒并调整相机位置模型显示为灰白色PLY带颜色但材质没开vertexColors设置vertexColors: true浏览器报CORS或加载404文件放错目录或跨域请求被拦截模型放public目录开发时确认路径生产环境配置静态资源跨域头页面一卡一卡帧率低顶点数太大或devicePixelRatio过高抽稀、限制像素比、关闭transparent切路由后整个页面黑屏WebGL上下文泄漏组件卸载时调用renderer.dispose()和geometry.dispose()模型出现在奇怪位置原始模型的坐标原点不在包围盒中心加载后用geometry.center()居中处理页面提示不能创建WebGL上下文浏览器GPU进程异常或上下文耗尽释放旧上下文刷新页面确认显卡驱动是否正常每个问题展开说都有一堆细节。比如“模型坐标是超大值”这一点三维扫描设备经常把原点设在扫描仪位置上模型离原点可能有几十米远。相机的near/far如果设置的是0.1到1000几十米外的模型完全在可视范围之外。我用Box3计算包围盒然后根据包围盒大小重置相机位置function fitCameraToModel(geometry) { geometry.computeBoundingBox() const box geometry.boundingBox const sphere box.getBoundingSphere() const distance sphere.radius * 2.5 const direction camera.position.clone().normalize() camera.position.copy(direction.multiplyScalar(distance)) camera.near sphere.radius / 10 camera.far sphere.radius * 20 camera.updateProjectionMatrix() controls.target.copy(box.getCenter(new THREE.Vector3())) controls.update() }这段逻辑极大减少了模型“一眼看上去找不到”的情况。7. 我交付这类需求前会做的几件事项目收尾时我习惯把以下几件事做完整这些实战经验能避免不少运维侧的返工第一确认PLY文件和前端环境是同一套编码规则。部分扫描设备导出的PLY文件头里带中文注释或非标准扩展字段解析器可能直接报错我会提前用PLYLoader跑一遍确认文件头字段都合规。第二生产环境尽量让后端提供模型文件的下载接口而不是把静态文件硬编码在前端。这样模型更新不需要重新打包前端发布也能借助后端做权限校验。配合nginx的静态文件缓存加载速度还能再快一截。第三给模型展示页做降级处理。没有WebGL支持的浏览器环境比如某些旧版本的WebView会直接黑屏要提前增加提示别让业务方以为功能坏了。可以用renderer.debug.checkShaderErrors或简单检查WebGLRenderingContext是否存在来判断。第四动画循环的启动需要在模型加载成功后开始。如果一开始就启动场景还是空的用户肉眼看起来只是黑屏容易以为是加载出错。实际项目中我会在load回调成功后再启动动画循环同时显示一个简单的loading状态。如果还要继续扩展这个组件可以继续做模型多视角截图、模型标注、模型测量等功能这些底子都是在Vue里稳定渲染PLY模型之后才能做的。至少对我来说这套流程跑顺之后以后再遇到类似的三维展示需求就不会再被格式解析和生命周期问题绊住了。

相关新闻

WPF ListView不刷新?从通知机制到线程调度的完整排查指南

WPF ListView不刷新?从通知机制到线程调度的完整排查指南

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

2026/10/4 7:43:08 阅读更多 →
hyperframes 实战:HTML 转 MP4 的自动化视频渲染方案

hyperframes 实战:HTML 转 MP4 的自动化视频渲染方案

1. 从 hyperframes 说起:一个被低估的 HTML 转 MP4 思路第一次看到 hyperframes 这个词,是在一个做自动化内容分发的群里。有人丢了个链接,说“这玩意儿能把 HTML 直接变成 MP4,不用开浏览器录屏”。当时我的第一反应是&#xff1…

2026/10/4 7:43:08 阅读更多 →
wechat-cli开发者指南:项目架构、Click命令设计与npm跨平台二进制分发深度剖析

wechat-cli开发者指南:项目架构、Click命令设计与npm跨平台二进制分发深度剖析

wechat-cli开发者指南:项目架构、Click命令设计与npm跨平台二进制分发深度剖析 【免费下载链接】wechat-cli A CLI tool to query your local WeChat data — chat history, contacts, sessions, favorites, and more. Designed for LLM integration. 项目地址: h…

2026/10/4 7:43:08 阅读更多 →

最新新闻

Webots R2023b 版本深度解析:Pose 节点迁移、VacuumGripper 真空吸盘与外置控制器启动器实战

Webots R2023b 版本深度解析:Pose 节点迁移、VacuumGripper 真空吸盘与外置控制器启动器实战

科研自动驾驶物理引擎 【免费下载链接】webots Webots Robot Simulator 项目地址: https://gitcode.com/gh_mirrors/web/webots 点击查看 免费下载 Webots R2023b 是 Cyberbotics 于 2023 年 6 月 28 日发布的重大版本更新,本篇文章以官方发布文档 docs…

2026/10/4 9:04:12 阅读更多 →
国庆七天搭建飞书企微自动化工作流:WorkBuddy实战指南

国庆七天搭建飞书企微自动化工作流:WorkBuddy实战指南

1. 为什么我要在国庆七天折腾这套自动化工作流国庆七天假,说长不长说短不短。出门堵在高速上看车尾灯,不如在家把一直想搭但没时间搭的自动化工作流给落地了。我平时的工作状态是这样的:飞书里堆着各种需求文档和表格,企微里是团队…

2026/10/4 9:04:12 阅读更多 →
VFH避障算法原理与调参实战:从向量场直方图到机器人局部路径规划

VFH避障算法原理与调参实战:从向量场直方图到机器人局部路径规划

做机器人避障和局部路径规划的人,应该没有一个没听说过VFH。VFH算法全称是Vector Field Histogram(向量场直方图),它解决的是移动机器人在未知环境下,如何根据传感器信息实时避开障碍物并朝目标方向运动的问题。市面上…

2026/10/4 9:04:12 阅读更多 →
IT爱学堂-普通人玩转 Vibe Coding 实战课,Vibe Coding全栈开发实战课(完结)

IT爱学堂-普通人玩转 Vibe Coding 实战课,Vibe Coding全栈开发实战课(完结)

一句话生成项目:从自然语言到可运行系统的技术链路 普通人玩转 Vibe Coding,第一步不是让 AI 立刻写代码,而是学会把一句话变成可执行的规格。所谓一句话生成项目,背后并不是魔法,而是一条技术链路:意图澄清…

2026/10/4 9:04:12 阅读更多 →
Hey压测公共服务:压别人家API时的法律红线与礼仪规范

Hey压测公共服务:压别人家API时的法律红线与礼仪规范

Hey压测公共服务:压别人家API时的法律红线与礼仪规范 【免费下载链接】hey HTTP load generator, ApacheBench (ab) replacement 项目地址: https://gitcode.com/GitHub_Trending/he/hey Hey 是一个用 Go 编写的轻量级 HTTP 压测工具(ApacheBench…

2026/10/4 9:04:12 阅读更多 →
宿舍楼局域网组网实战:从拓扑选型到VLAN划分的完整方案

宿舍楼局域网组网实战:从拓扑选型到VLAN划分的完整方案

简介:一份面向网络工程、计算机及相关专业学生的完整课程设计文档,以大学学生宿舍楼为场景,系统梳理局域网从规划到运维的闭环流程。文档主体为1个doc文件,压缩包约2.26MB,内容围绕网络规划、网络设计、网络实施、测试…

2026/10/4 9:03:12 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/2 10:36:31 阅读更多 →
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/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练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/3 9:42:36 阅读更多 →