Steinberg VST插件开发包入门:从零构建VST3音频插件
简介Steinberg VST插件开发包VST3 SDK 3.5版面向音频插件开发者、音乐软件工程师及数字音频工作站扩展爱好者提供创建虚拟乐器与音频效果处理器所需的完整开发环境。包内共5764个文件以html文档、png界面素材、h头文件、cpp源码、vcproj与sln工程文件、xcconfig与pbxproj构建配置为主另有md5校验、map映射及少量图片资源压缩包约44.93MB。核心涵盖VST3 API、AudioEffect基类、Event事件处理、IPlugView界面、ParamID参数标识与HostContext宿主上下文并附带均衡器、压缩器、合成器等多类示例工程便于理解插件注册、安装及Windows与Mac OS跨平台兼容要点。目前已有650人学习下载适合希望系统掌握VST3规范、快速上手插件开发与性能优化的中高级开发者参考。1. 从零上手 Steinberg VST 插件开发包一个音频从业者的最小可行路径如果你平时用宿主软件做混音或编曲大概率动过「自己写一个插件」的念头。Steinberg VST 插件开发包就是干这件事的官方 SDK——它定义了宿主和插件之间怎么握手、怎么传音频、怎么暴露参数给自动化。很多人卡在第一步下载完 SDK 打开一看几百个源文件、一堆平台相关的宏不知道从哪下手。我当初也是这么翻车的后来才摸清一条最小路径先跑通一个能出声的增益插件再逐步加参数、加 GUI、加 MIDI 处理。这篇笔记就是按这个顺序拆的适合有 C 基础、想正经做音频插件的开发者也适合想理解宿主内部机制的混音师。读完你能自己编译出第一个 VST3知道每个参数在哪改也清楚哪些坑必须提前绕开。2. 先把 VST3 的骨架拆开为什么不是 VST2以及工程怎么建2.1 VST3 和 VST2 的本质区别决定了你的代码结构VST2 时代插件就是一个processReplacing函数加一堆全局变量简单粗暴。VST3 完全换了模型它基于组件对象模型插件是一个实现了IComponent和IAudioProcessor的类宿主通过接口指针调用你。这个变化带来的直接后果是——你不能再写一个全局的float gain就完事所有状态必须封装在类里参数要通过EditController暴露。为什么 Steinberg 要这么改因为 VST2 在多实例、多线程、参数自动化上经常出玄学问题。VST3 把音频处理、参数编辑、UI 三层拆开宿主可以单独查询参数列表而不触发音频线程。代价是入门门槛高了但一旦理解了这个分层后面加功能反而清晰。常见做法是直接用 SDK 里的public.sdk/samples/vst/again示例作为起点。这个示例实现了一个简单的增益插件代码量不大但覆盖了 VST3 的核心流程。我一般会把它复制出来改个名字然后逐步替换成自己的逻辑。2.2 用 CMake 建一个能编译的 VST3 工程SDK 从 VST 3.6.14 开始推荐用 CMake 构建不再维护旧的工程文件。下面是一个最小CMakeLists.txt假设你把 SDK 放在extern/vst3sdk目录下cmake_minimum_required(VERSION 3.15) project(MyGainPlugin VERSION 1.0.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 指向 SDK 根目录里面要有 public.sdk 和 pluginterfaces set(VST3_SDK_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/extern/vst3sdk) # 引入 SDK 自带的 CMake 模块它会处理平台差异和 VST3 打包 add_subdirectory(${VST3_SDK_ROOT} ${CMAKE_BINARY_DIR}/vst3sdk) # 声明插件目标smtg_add_vst3plugin 是 SDK 提供的宏 smtg_add_vst3plugin(MyGainPlugin source/MyGainProcessor.cpp source/MyGainController.cpp source/MyGainEntry.cpp ) # 链接必要的 SDK 库 target_link_libraries(MyGainPlugin PRIVATE sdk_common sdk_hosting base )这段 CMake 的关键在smtg_add_vst3plugin这个宏它会自动帮你设置输出目录、生成.vst3包结构、处理 Windows 的.def导出和 macOS 的 bundle。参数说明VST3_SDK_ROOT必须指向包含public.sdk和pluginterfaces的目录smtg_add_vst3plugin的第一个参数是目标名后面跟源文件列表。如果你在 Windows 上编译还需要确保 SDK 的cmake/modules在CMAKE_MODULE_PATH里不过add_subdirectory之后通常会自动处理。编译命令cmake -B build -DCMAKE_BUILD_TYPERelease cmake --build build --config Release编译成功后产物在build/VST3/Release/MyGainPlugin.vst3。把它拷贝到系统 VST3 目录Windows 是C:\Program Files\Common Files\VST3macOS 是/Library/Audio/Plug-Ins/VST3打开宿主就能扫到。2.3 入口文件里必须写对的三件事MyGainEntry.cpp是插件的入口它负责告诉宿主「这个 DLL/bundle 里有什么」。VST3 用工厂模式注册组件核心是BEGIN_FACTORY_DEF宏。下面是一个最小入口#include public.sdk/source/main/pluginfactory.h #include MyGainProcessor.h #include MyGainController.h #define kPluginName MyGain #define kPluginVendor MyVendor #define kPluginVersion 1.0.0 BEGIN_FACTORY_DEF(kPluginVendor, https://example.com, mailto:devexample.com) // 注册音频处理器宿主用它来创建处理实例 DEF_CLASS2(INLINE_UID_FROM_FUID(MyGainProcessorUID), PClassInfo::kManyInstances, kVstAudioEffectClass, kPluginName, Vst::kDistributable, Vst::PlugType::kFx, kPluginVersion, kVstVersionString, MyGainProcessor::createInstance) // 注册编辑控制器宿主用它来管理参数和 UI DEF_CLASS2(INLINE_UID_FROM_FUID(MyGainControllerUID), PClassInfo::kManyInstances, kVstComponentControllerClass, kPluginName, Vst::kDistributable, , kPluginVersion, kVstVersionString, MyGainController::createInstance) END_FACTORY_DEF逻辑说明BEGIN_FACTORY_DEF和END_FACTORY_DEF之间必须至少注册一个音频处理器类否则宿主扫到了也加载不了。DEF_CLASS2的第一个参数是类的唯一 ID这个 ID 必须和你在 Processor/Controller 里定义的FUID一致否则宿主创建实例时会返回空。kVstAudioEffectClass表示这是音频效果器kVstComponentControllerClass表示这是配套的控制器。Vst::PlugType::kFx是插件分类宿主会根据这个把插件放到「效果器」列表里。参数说明kPluginVendor和kPluginName会显示在宿主的插件浏览器里建议用英文避免编码问题。kPluginVersion是字符串不是数字宿主用它做版本比较。createInstance是静态函数返回FUnknown*SDK 的基类已经帮你实现了引用计数你只需要在类里声明DEFINE_INTERFACES和IMPLEMENT_FUNKNOWN_METHODS。3. 音频处理核心process 函数怎么写才不出爆音3.1 理解 process 的调用时机和缓冲区布局VST3 的音频处理入口是IAudioProcessor::process宿主每次需要音频时调用它。和 VST2 不同VST3 的ProcessData里包含多个AudioBus每个 bus 有多个通道每个通道是一个Sample32数组。你必须先调用ProcessData::prepare或者手动检查numSamples然后遍历 bus 和 channel。一个常见的翻车点是直接假设输入和输出是同一块内存。VST3 允许宿主传入不同的输入输出缓冲区甚至允许原地处理in-place但你不能依赖这一点。正确做法是分别取inputBus和outputBus的通道指针然后逐样本处理。tresult PLUGIN_API MyGainProcessor::process(ProcessData data) { // 如果宿主传入了参数变化先处理参数 if (data.inputParameterChanges) { int32 numParamsChanged data.inputParameterChanges-getParameterCount(); for (int32 i 0; i numParamsChanged; i) { IParamValueQueue* paramQueue data.inputParameterChanges-getParameterData(i); if (paramQueue) { ParamValue value; int32 sampleOffset; int32 numPoints paramQueue-getPointCount(); // 只取最后一个点简化处理 if (paramQueue-getPoint(numPoints - 1, sampleOffset, value) kResultTrue) { if (paramQueue-getParameterId() kGainId) { gain value; // 更新内部增益值 } } } } } // 获取输入输出 bus假设是立体声 AudioBusBuffers inputBus data.inputs[0]; AudioBusBuffers outputBus data.outputs[0]; // 如果总线是静音状态直接清零输出并返回 if (inputBus.silenceFlags ! 0) { outputBus.silenceFlags inputBus.silenceFlags; return kResultOk; } int32 numChannels inputBus.numChannels; int32 numSamples data.numSamples; for (int32 ch 0; ch numChannels; ch) { Sample32* in inputBus.channelBuffers32[ch]; Sample32* out outputBus.channelBuffers32[ch]; if (in out) { for (int32 s 0; s numSamples; s) { out[s] in[s] * gain; // 核心处理乘以增益 } } } return kResultOk; }逻辑说明先处理参数变化把宿主传来的增益值更新到成员变量gain。然后检查silenceFlags如果输入是静音直接把输出也标记为静音并返回省掉无用的乘法。最后遍历每个通道逐样本乘增益。参数说明data.numSamples是本次处理的样本数通常是 32 到 1024 之间取决于宿主的缓冲区设置。inputBus.channelBuffers32是Sample32**每个元素指向一个通道的浮点数组。gain是ParamValue类型实际是double范围 0.0 到 1.0但你可以映射到 -60dB 到 12dB。3.2 参数怎么暴露给宿主EditController 和 Parameter 的绑定VST3 的参数系统是分离的Processor 负责音频线程读取参数值Controller 负责向宿主暴露参数列表和 UI。两者通过ParameterInfo和ParameterChange通信。你需要在 Controller 的initialize里注册参数在 Processor 的process里读取参数变化。下面是在 Controller 里注册一个增益参数的代码tresult PLUGIN_API MyGainController::initialize(FUnknown* context) { tresult result EditControllerEx1::initialize(context); if (result ! kResultOk) return result; // 注册一个增益参数ID 为 kGainId范围 0.0 到 1.0默认 0.5 parameters.addParameter( USTRING(Gain), // 参数名显示在宿主自动化列表里 USTRING(dB), // 单位 0, // 步进数0 表示连续 0.5, // 默认值 ParameterInfo::kCanAutomate, // 标志可自动化 kGainId // 参数 ID必须和 Processor 里一致 ); return kResultOk; }逻辑说明parameters.addParameter是EditControllerEx1提供的方法它把参数信息注册到内部列表宿主通过getParameterInfo查询时会返回这些信息。参数说明第一个参数是显示名第二个是单位第三个是步进数0 表示连续值第四个是默认值第五个是标志位第六个是参数 ID。kGainId必须是一个唯一的整数通常定义在共享头文件里Processor 和 Controller 都包含它。Processor 端不需要注册参数但需要在process里通过inputParameterChanges读取宿主传来的值。如果你想让参数变化更平滑可以在 Processor 里做线性插值避免阶跃噪声。3.3 编译后加载失败先查这三个地方第一次编译 VST3 后宿主扫不到或者加载崩溃是常态。我踩过的坑按频率排序第一工厂注册的 UID 和类里定义的 UID 不一致宿主创建实例时返回空指针表现为插件加载后无声。第二process里没有检查data.numSamples是否为 0某些宿主在停止播放时会传 0导致除零或越界。第三Windows 上忘记导出InitDll和ExitDllSDK 的pluginfactory已经帮你处理了但如果你手动改了入口文件可能破坏导出表。排查方法用 SDK 自带的validator工具。编译 SDK 时会生成validator可执行文件运行validator MyGainPlugin.vst3它会逐项检查工厂注册、参数、总线布局并给出具体错误码。这个工具比盲猜快得多。4. 避坑与排查VST3 开发中最容易翻车的五个地方4.1 宿主扫不到插件先看目录结构和二进制架构现象把.vst3拷到系统目录后宿主插件列表里没有。原因通常有三个一是目录结构不对VST3 在 Windows 上是一个文件夹里面要有Contents/x86_64-win/MyGainPlugin.vst3而不是直接放一个 DLL。二是架构不匹配64 位宿主只能加载 64 位插件如果你编译了 32 位版本宿主会静默忽略。三是 macOS 上没有对 bundle 做代码签名某些宿主会拒绝加载。解决用cmake --build build --config Release编译后检查build/VST3/Release/下的目录结构。Windows 上应该看到MyGainPlugin.vst3/Contents/x86_64-win/MyGainPlugin.vst3。如果只有 DLL说明smtg_add_vst3plugin没有正确执行检查 CMake 里是否包含了 SDK 的cmake/modules。4.2 音频爆音或咔嗒声参数没做平滑处理现象调整增益参数时音频出现咔嗒声或爆音。原因宿主在音频线程外改变参数值Processor 直接用了新值导致波形不连续。解决在 Processor 里对增益做一阶低通平滑或者用ProcessData里的sampleOffset做分段插值。简单做法是维护一个currentGain和targetGain每个样本向目标值靠近一小步// 在 process 循环里 for (int32 s 0; s numSamples; s) { currentGain (targetGain - currentGain) * 0.001; // 平滑系数 out[s] in[s] * currentGain; }平滑系数越小过渡越慢但太慢会导致参数响应迟钝。我一般用 0.001 到 0.01 之间根据宿主缓冲区大小调整。4.3 参数自动化不生效ID 冲突或标志位写错现象在宿主里画自动化曲线插件没反应。原因Processor 和 Controller 里的参数 ID 不一致或者注册参数时没加ParameterInfo::kCanAutomate标志。解决把参数 ID 定义在一个共享头文件里两边都#include。注册时确认标志位包含kCanAutomate。另外某些宿主需要你在EditController::setParamNormalized里更新内部状态否则 UI 和音频线程会脱节。4.4 多实例崩溃全局变量和静态成员惹的祸现象同时加载两个插件实例其中一个崩溃或互相干扰。原因用了全局变量或静态成员来存状态两个实例共享了同一块内存。解决所有状态必须是类的非静态成员。SDK 的FUnknown引用计数机制保证了每个实例独立但如果你在createInstance里返回了单例就会出问题。检查你的createInstance是否每次都new了一个新对象。4.5 宿主退出时崩溃析构顺序和资源释放现象关闭宿主时插件导致崩溃。原因在析构函数里释放了已经被宿主释放的资源或者音频线程还在运行时就销毁了缓冲区。解决在IAudioProcessor::setActive(false)里停止音频处理在terminate里释放资源。不要在析构函数里做复杂操作SDK 的基类会处理引用计数你只需要确保没有悬空指针。5. 进阶技巧用 SDK 自带的测试工具验证插件以及一个我常用的调试习惯SDK 里有一个被很多人忽略的工具validator。它不仅能检查工厂注册还能模拟宿主调用process验证你的音频处理是否返回正确结果。编译 SDK 后validator在build/bin/Release/下。运行validator MyGainPlugin.vst3它会输出类似这样的结果* VST3 Validator Report * Plugin: MyGain * Result: 0 errors, 0 warnings如果有错误它会指出具体是哪个接口没实现、哪个参数越界。我一般会在每次改完process或参数注册后跑一遍比打开宿主手动测试快得多。另一个习惯是在process开头加一个条件断点当data.numSamples 0且data.inputs[0].channelBuffers32[0]为空时触发。这种情况通常意味着宿主传入了无效缓冲区但你的代码没有检查直接解引用就会崩溃。加上这个断点后我至少避免了三次线上崩溃。还有一个进阶用法用IComponent::getState和setState实现预设保存。宿主保存工程时会把插件状态序列化成二进制流你只需要把参数值写进去加载时读出来。SDK 的MemoryStream可以帮你处理字节序和平台差异。这个功能对用户来说很实用但实现时注意不要在setState里触发音频线程操作否则会死锁。最后说一个血泪教训不要为了省事把参数范围设成 0 到 1 然后直接当增益用。人耳对响度的感知是对数关系线性增益在低音量区几乎听不出变化。我一般会把参数映射成 dB范围 -60 到 12然后在 Processor 里转成线性增益。这样自动化曲线看起来更符合直觉用户也更容易调出想要的效果。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

STM32驱动舵机:PWM时基、定时器映射与舵机协议三重校准

STM32驱动舵机:PWM时基、定时器映射与舵机协议三重校准

简介:本资源是一套基于STM32F10x系列的伺服电机数字控制完整工程源码,面向嵌入式初学者、自动化控制课程实践者及机器人/精密设备开发工程师,解决单片机驱动高精度执行机构的核心问题——即通过PWM生成、PID闭环调节与硬件外设协同实现伺服电…

2026/10/13 20:08:06 阅读更多 →
RAG项目自建AI基础设施的陷阱与务实落地路线图

RAG项目自建AI基础设施的陷阱与务实落地路线图

1. 被“自建平台”绑架的RAG项目过去一年多,我参与过好几个企业级RAG(检索增强生成)项目的评审和落地咨询。一个非常普遍的现象是:项目刚立项,技术团队的第一反应往往是“我们要自建一套AI基础设施平台”。理由听起来也…

2026/10/13 20:08:06 阅读更多 →
知识工作者的数字外挂:插件选型与实践复盘

知识工作者的数字外挂:插件选型与实践复盘

知识工作者的数字外挂:一套插件的选型思路与实践复盘这两年,我越来越觉得“知识工作”这个词被低估了。很多人以为写文档、做分析、查资料就是知识工作,但真正每天泡在这些流程里的人才会懂——最耗时间的从来不是思考本身,而是思…

2026/10/13 20:08:06 阅读更多 →

最新新闻

基于STM32的GPS导航:从NMEA解析到控制量完整实现

基于STM32的GPS导航:从NMEA解析到控制量完整实现

简介:这是一份基于STM32微控制器与UC/GUI图形库的GPS导航系统相关工程源码,面向嵌入式开发者以及正在学习STM32、GPS应用和GUI编程的学生。资源以STM32为主控芯片,通过UART或SPI接口接收GPS模块的NMEA协议数据,完成经纬度解析、定…

2026/10/13 21:09:59 阅读更多 →
Sa-Token过滤器引发CORS误判:OPTIONS预检被拦截的排查与修复

Sa-Token过滤器引发CORS误判:OPTIONS预检被拦截的排查与修复

这个问题的标题里有一个关键词——“误判”。在绝大多数CORS报错的排查复盘里,大家习惯把锅甩给浏览器,觉得它又在发神经。但实际经历过Sa-Token过滤器引发的CORS误判之后,我才意识到,浏览器这个“判官”其实一丝不苟,…

2026/10/13 21:09:59 阅读更多 →
人格障碍诊断系统毕业设计:从判分引擎到报告可视化完整实现

人格障碍诊断系统毕业设计:从判分引擎到报告可视化完整实现

如果你在毕业设计选题里看到“人格障碍诊断系统”这几个字,先别急着把它归类成普通的增删改查项目。这个题目看起来是典型的SpringBootVueMySQL管理信息系统,但真正做起来,它比学生管理系统、图书管理系统多了一层核心业务逻辑——量表测评、…

2026/10/13 21:09:59 阅读更多 →
MLX90614红外测温实战:发射率、SMBus与标定排错全攻略

MLX90614红外测温实战:发射率、SMBus与标定排错全攻略

简介:这是一份基于51单片机与MXL90614红外测温模块的完整工程代码包,适合单片机初学者、嵌入式爱好者以及需要快速实现非接触测温显示的开发者使用。工程直接面向“读取红外传感器—数据处理—LCD1602显示”的完整链路,能帮助读者直接用现成代…

2026/10/13 21:09:59 阅读更多 →
SDR# x86分发包驱动PlutoSDR:从兼容性到采样率与增益调优

SDR# x86分发包驱动PlutoSDR:从兼容性到采样率与增益调优

简介:软件定义无线电(SDR)的核心价值在于用软件替代固定硬件,能灵活适应多种通信协议和频段,近年来受到无线电爱好者、科研人员与工程师的广泛重视。这里提供的是一份针对Pluto SDR设备优化定制的SDR#接收软件包&#…

2026/10/13 21:09:59 阅读更多 →
鸽哒IM源码:生产级私有化即时通讯系统解析

鸽哒IM源码:生产级私有化即时通讯系统解析

简介:这是一套完整的跨平台即时通讯(IM)系统源码,面向中高级Java/Android/iOS开发者及全栈工程师,用于学习和搭建企业级实时通信服务。资源涵盖Web(WebSocketHTML5)、Android(JavaFC…

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

日新闻

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

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

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

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 阅读更多 →