Qt SQLCipher集成:SQLite数据库加密与驱动改造实践
先从一个让人心疼的场景说起项目里一套内部工具后台用的是 SQLite 存数据跑得好好的结果有台测试机整目录被拷贝出去别人拿文本编辑器直接打开.db文件用户操作记录、配置信息、甚至本地缓存里的敏感字段全都能看懂。从那天起我就把“SQLite 数据库文件必须加密”从可选优化划成了硬性需求。Qt 项目里做这事绕不开 SQLCipher。严格来说SQLCipher 不是独立数据库它是 SQLite 的一个加密分支把数据库页面在落盘之前做 AES 加密读取时再解密。而 Qt 这边最干净的做法不是自己封装一层文件加解密而是在 Qt SQL 的 SQLite 驱动插件里接入 SQLCipher上层代码继续用QSqlDatabase、QSqlQuery不需要改业务 SQL。这篇文章就把我在实际项目里的完整做法写一遍从 SQLCipher 源码怎么编译到 Qt 的QSQLITE驱动插件如何改成QSQLCIPHER驱动再到连接参数、迁移老库、改密码、兼容旧版本库、性能取舍最后是我踩过的几个坑。已经用 SQLCipher 的可以直接跳转看 3、4、5 三个部分还没接入的建议从第 1 部分开始看。1. 为什么 SQLite 裸文件守不住数据以及 SQLCipher 给出的解决方案1.1 明文存储的三个漏洞SQLite 默认把所有数据和表结构完整地写在磁盘文件里一个没有加密的.db文件头部直接就是SQLite format 3这几个 ASCII 字符。后面跟的表数据、索引、B 树页面虽然二进制结构有点复杂但任何能读取二进制文件的工具都可以解析。更直接的问题是字符串。你把.db拖进十六进制编辑器搜索几个字段名附近的字节大概率就是明文内容。SQLite 还有一个容易忽略的点事务日志和 WAL 文件。只要数据库开过 WAL 模式同目录下会多出.db-wal和.db-shm文件WAL 文件里也可能残留未合并的页面。如果你只加密主文件忘了处理这些衍生文件依然会泄露数据。很多刚接触的人会问我把整个数据库文件用 AES 加密一下不就行了吗想法没错但实现上有两个致命问题。第一SQLite 是随机读写的数据库按页面读取数据。如果整个文件加一层密每次读任何一页都得先解密整个文件写入更是灾难文件越大性能越没法看。第二文件不是数据库自带的机制应用层很难正确控制加解锁时机崩溃恢复、日志同步这些功能基本等于废掉。1.2 页面级加密和整文件加密的本质区别SQLCipher 的核心理念是页面级加密。SQLite 把数据库切成固定大小的页面默认是 4096 字节读一条记录可能只需要读一个页面。SQLCipher 在原来的页面管理外面包了一圈写入页面之前对页面内容做 AES 和 HMAC 运算读取页面之后先验证 HMAC 再解密。这个设计带来的结果是数据库头部不再包含SQLite format 3变成一堆随机字节用file命令都识别不出来。没有正确密钥的外部程序打开这个文件时会报告“file is not a database”。每个页面的加密是独立的数据库索引、主键、B 树结构仍然在加密后的页面里正常工作性能损失被控制在一个可以接受的范围。某几个页面被篡改HMAC 校验能感知到而不是等到读到那一页才发现数据变成乱码。用生活类比来解释整文件加密像是把一整套笔记本放进保险柜你要用第一页就得把整本拿出来解密SQLCipher 则是给每一页纸单独盖章封印随取随验随读。后者才是数据库场景真正需要的加密粒度。1.3 在 Qt 眼里SQLCipher 只是另一个 SQLite 后端Qt 的QtSql模块里有 SQLite 驱动底层就是 C APIsqlite3_*函数通过插件形式暴露给上层。SQLCipher 和 SQLite 的 C API 兼容所以 Qt 不需要为加密单独发明一套新的数据库引擎只需要让QSQLITE插件链接的 SQLite 实现从原生版本换成 SQLCipher 版本。这也是“Qt 插件和 SQLCipher 实现加密与解密”这句话的实际含义插件还是原来的插件数据库核心换掉了服务端多了一个密码校验层。上层用QSqlDatabase::addDatabase(QSQLCIPHER)注册驱动然后像操作普通 SQLite 一样操作加密库。2. 把 SQLCipher 编进 Qt SQL 插件源码替换与构建2.1 版本组合怎么选才能少踩编译坑在我实际动手之前最让我头疼的其实是版本配对问题。Qt 的 SQLite 插件源码在不同大版本之间改动不小SQLCipher 也在持续跟随 SQLite 上游版本。经验上你不需要追求最新但一定要满足几个条件Qt 版本确定后找到对应版本的 Qt 源码包里面才有qsql_sqlite.cpp、qsqlite.pro这些插件工程文件。SQLCipher 的合并源码amalgamation也就是sqlite3.c单文件版本最好选择与 Qt 自带 SQLite 主版本差距不超过一个大的版本避免sqlite3_open_v2、sqlite3_column_*等 API 行为差异过大。编译器位数必须一致。32 位编译的 SQLite 库进 64 位 Qt 插件链接阶段就过不去。我用的组合是 Qt 5.15 系 SQLCipher 4.5.x 系列编译环境是 MSVC 和 MinGW 两套都吃过后面会专门说坑。如果你想省事永远记住一个原则先在一个干净目录成功编译一次官方 Qt SQLite 插件再动手替换 SQLCipher这样出了问题至少能判断是环境问题还是 SQLCipher 接入问题。2.2 先编译一份 SQLCipher还是直接用合并后的源码SQLCipher 官方仓库提供 CMake 构建也可以生成合并后的sqlite3.c。我在交叉编译场景下更推荐直接用合并源码因为 Qt 插件构建时可以直接把sqlite3.c作为源文件编进插件里避免动态库的符号冲突和安装路径问题。如果先独立编译 SQLCipher 成动态库再让 Qt 插件去链接它表面上很清晰实际会碰到一个麻烦Qt 的某些构建方式会自带一份 SQLite 符号插件内链接的动态库如果也导出sqlite3_*符号运行时不一定会用你期望的那一份。轻则功能异常重则直接崩溃。所以我的方案很简单插件工程里不再添加原生sqlite3.c而是把 SQLCipher 的sqlite3.c、sqlite3.h、sqlite3ext.h放进来一起编译。如果你确实需要独立生成 SQLCipher 库备用CMake 命令大致是这样cmake -S . -B build \ -DCMAKE_INSTALL_PREFIX/opt/sqlcipher \ -DSQLCIPHER_BUILD_TESTSOFF \ -DCMAKE_BUILD_TYPERelease \ -DSQLCIPHER_CRYPTO_OPENSSLOFF cmake --build build cmake --install buildSQLCIPHER_CRYPTO_OPENSSLOFF是使用 SQLCipher 内置加密算法实现减少 OpenSSL 依赖。如果你因为合规要求必须使用 OpenSSL 的 FIPS 模块再考虑打开它。2.3 改造 qsql_sqlite 插件并生成 qsqlcipher 驱动Qt 源码里 SQLite 插件的位置一般在qtbase/src/plugins/sqldrivers/sqlite这个目录下有qsql_sqlite.cpp、qsql_sqlite.h、sqlite.pro之类的文件。我建议不要把插件直接改在 Qt 源码目录里而是拷贝一份到自己的工程里维护起来更清楚。核心改动有三处。第一处驱动名。qsql_sqlite.cpp里会定义QSQLITE_DRIVER_NAME之类的东西你把它改成QSQLCIPHER。驱动名不同插件运行时注册的数据库驱动才是独立的QSQLCIPHER。如果你不改名继续叫QSQLITE就会和你 Qt 自带的原版 SQLite 驱动冲突QSqlDatabase::drivers()里出现两个同名驱动加载哪个都说不清。第二处编译宏。插件的.pro文件里必须加DEFINES SQLITE_HAS_CODEC这件事没有商量余地。SQLCipher 的加密相关 API 全都由这个宏控制Qt 的qsql_sqlite.cpp里也有针对SQLITE_HAS_CODEC的条件编译片段比如在setPassword()时调用sqlite3_key之类的能力就在这段里面。第三处源码替换。把 SQLCipher 的sqlite3.c、sqlite3.h、sqlite3ext.h放进插件的源码目录.pro的SOURCES里引用 SQLCipher 的sqlite3.c同时删除对原生 SQLite 代码的引用。一个简化后的.pro片段TARGET qsqlcipher include(../../../../src/plugins/sqldrivers/qsqldriverbase.pri) DEFINES SQLITE_HAS_CODEC INCLUDEPATH $$PWD/sqlcipher_amalgamation SOURCES sqlcipher_amalgamation/sqlite3.c构建完之后把生成的qsqlcipher.dll或qsqlcipher.so放到 Qt 的plugins/sqldrivers目录下同时并行保留原来的qsqlite插件也没问题因为它们驱动名不同互不干扰。3. 连接、迁移、改密让加密库真正跑起来的 Qt 代码3.1 连接参数和密码的执行时机编译好了插件接下来看 Qt 代码怎么连。最标准的打开加密数据库方式是这样的QSqlDatabase db QSqlDatabase::addDatabase(QSQLCIPHER, mainConnection); db.setDatabaseName(/srv/data/app.db); db.setPassword(your-strong-passphrase); db.setConnectOptions(QSQLCIPHERaes256cbc); if (!db.open()) { qWarning() open db failed: db.lastError().text(); return false; } QSqlQuery query(db); query.exec(SELECT * FROM user_config);注意几个细节。addDatabase第三参数是连接名同一个连接名不要重复添加否则 Qt 会直接替换掉现有连接对象。setPassword对应的是 SQLCipher 的PRAGMA key执行时机是在db.open()内部也就是数据库文件打开之后、任何业务 SQL 执行之前。这个过程由qsql_sqlite.cpp完成你不需要在业务代码里手工执行PRAGMA key。setConnectOptions(QSQLCIPHERaes256cbc)是选择加密算法名SQLCipher 支持aes256cbc、aes256ctr等算法默认是aes256cbc设置它不是必须的但显式写出来有利于后续升级时明确库的格式。密码不要用太短的字符串SQLCipher 背后有 KDF 生成派生密钥弱密码会显著降低暴力破解成本。还有一个使用习惯问题不要让代码里到处打印db.password()或者把密码拼进日志。密码在 Qt 驱动内部通常不会被记录到错误信息里但你一旦在业务层自己qDebug() db.connectOptions()很可能会把密码一起打印出来。线上事故基本都这么来的。3.2 明文数据库加密迁移的完整过程很多项目不是一开始就加密的老库已经积累了大量数据这时候不可能把数据导成 CSV 再新建。SQLCipher 提供了一个干净利落的迁移方案在同一连接里挂载两个数据库用sqlcipher_export()完成数据迁移。实际操作分三步。第一步先用普通 SQLite 驱动打开明文库执行一次 VACUUM确保 WAL 和 journal 里残留的数据被合入主文件。第二步在同一个连接上执行下面的 SQLATTACH DATABASE new_encrypted.db AS encrypted KEY new-passphrase; SELECT sqlcipher_export(encrypted); DETACH DATABASE encrypted;第三步校验新数据库可以正常打开数据条数和原库一致再把旧库文件移走或者删除。这里我提几个容易踩的点。new_encrypted.db必须是新文件如果目标文件已经存在sqlcipher_export会失败不会帮你覆盖。迁移过程中原库不应该有并发写入最好在业务低峰期做或者先整体停写。迁移后立刻重新打开一次新库检查PRAGMA key是否生效。我见过迁移完没校验第二天才发现导出来的是个坏库数据全毁的案例。如果你用的是 Qt 代码流程就是在普通 SQLite 连接上执行上述 SQL然后关闭旧连接再换成QSQLCIPHER驱动打开新库。3.3 修改密码和解密回明文数据库运行了一段时间密码想换掉怎么办不需要把数据导出来再导进去。SQLCipher 有专门的PRAGMA rekeyPRAGMA rekey new-passphrase;执行的先决条件是当前连接已经用正确密码打开了数据库。Qt 里这样操作if (!db.open()) { return; } QSqlQuery query(db); query.exec(PRAGMA rekey new-passphrase);特别提醒PRAGMA rekey执行期间数据库文件会被重写这个操作在文件大时会比较慢务必在事务或者锁可控的环境里做并且先备份。rekey不是一个即时完成的 IN 内存操作它要重新加密所有页面中途断电是存在损坏风险的。解密回明文也是同一个入口。SQLCipher 支持把rekey设为空字符串让数据库变成不加密状态。实际业务里很少用到这个操作但测试环境经常需要可以用PRAGMA rekey ;它的效果等同于“去掉密码”之后文件就变成普通 SQLite 明文库。注意空密码和 SQLCipher 完全不启用是两回事只要你用的还是 SQLCipher 驱动即使rekey为空驱动仍然会走 SQLCipher 的打开路径只是不设密钥而已。真正完全回到原生 SQLite还是需要走一遍导出迁移。3.4 判断数据库是否已经加密项目里偶尔要写个巡检脚本判断某个.db文件是不是已经被 SQLCipher 加密。最简单的方法是用十六进制查看前 16 个字节。明文 SQLite 头部是53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00也就是SQLite format 3。SQLCipher 加密后的头部没有这个固定标识直接是一段随机数据。你要是只想快速判别直接在命令行下head -c 16 app.db | xxd更严谨的方式还是用sqlite3CLI 或 Qt 打开一次遇到file is not a database再用正确密码尝试。针对加密库做自动化健康检查推荐把密码放到独立的密钥文件或者环境变量里脚本从环境读取而不是写在命令行参数里以免被别人从进程列表里翻出来。4. 版本兼容参数、性能损耗和备份细节往往一起踩雷4.1 SQLCipher 新旧版本默认参数差异如果你只在新项目里用 SQLCipher 最新版可能永远碰不到兼容性问题。但公司内部往往存在多个历史项目数据库是不同时期创建的这时候一个常见报错就出现了打开老库时密码明明正确却还是报错或者读出来的数据全是乱码。原因在 SQLCipher 3 到 4 之间默认参数发生过一批重要变化参数SQLCipher 3 默认值SQLCipher 4 默认值页面大小1024 字节4096 字节KDF 迭代次数64000 次256000 次HMAC 算法HMAC_SHA1HMAC_SHA512KDF 算法PBKDF2_HMAC_SHA1PBKDF2_HMAC_SHA512页面大小影响读写粒度KDF 迭代次数影响连接打开速度并影响派生密钥HMAC 算法直接影响校验值。所以一个新版 SQLCipher 驱动打开旧版加密库哪怕密码完全正确只要默认参数对不上密钥派生出来的结果也不一致。解决方法是打开旧库前设置兼容性 PRAGMAPRAGMA cipher_page_size 1024; PRAGMA kdf_iter 64000; PRAGMA cipher_hmac_algorithm HMAC_SHA1; PRAGMA cipher_kdf_algorithm PBKDF2_HMAC_SHA1;需要强调的是这些 PRAGMA 必须在PRAGMA key生效之前执行。而 Qt 驱动默认在open()时就把密码对应的PRAGMA key发出去了你想在open()之前插入这几个设置往往只能回到插件源码层面把兼容性逻辑写进qsql_sqlite.cpp的构造函数或打开函数里或者在自己的驱动子类里重写打开流程。所以我的建议是如果你维护了多个历史的 SQLCipher 加密库先在插件里做一个按文件路径区分配置的逻辑比每次手工执行更可靠。4.2 性能损耗到底能跑多少怎么缓解SQLCipher 不是没有代价。加密解密需要 CPU 时间HMAC 校验也会增加 I/O 之外的额外开销。以我自己的压测来看普通 SQLite 执行一万条插入在 400 毫秒左右SQCipher 加密后在 600 到 800 毫秒之间具体取决于密码 KDF 参数和 CPU 型号但总体可以接受一般是普通 SQLite 的 1.2 到 2 倍之间。比较值得注意的不是单次 SQL 性能而是两件事。第一连接打开速度。因为 KDF 迭代要跑几十万次每次建立新连接都会产生几十到几百毫秒不等的开销。如果你的代码每次都新建连接然后马上关闭这个损耗会被放大。解决办法是复用连接用连接池保活。Qt 里可以用QSqlDatabase的命名连接配合单例模式避免频繁开关。第二页面大小对随机读的影响。旧参数下页面只有 1024 字节随机读取更频繁性能一般比 4096 页面更差。所以如果历史库允许尽早把它迁移到 SQLCipher 4 的默认参数上长期受益。另外SQLCipher 有一个cipher_memory_security选项默认开启时会在内存中更谨慎地处理密钥材料安全更好但会带来一定开销。仅在你确认内存安全要求没那么高、性能又吃紧的时候可以在打开数据库后执行PRAGMA cipher_memory_security OFF;日常项目我默认不关安全收益通常比那一点点性能重要。4.3 WAL、备份和数据库拷贝的加密边界SQLite 的 WAL 模式在不加密时工作得很好SQLCipher 也支持 WAL 模式而且 WAL 文件本身同样被加密。但有一点要注意WAL 文件是数据文件的一部分备份时不能只拷贝主库把 WAL 文件漏掉恢复出来的库可能就是旧版本甚至损坏的。用 Qt 做在线备份时最稳妥的方式不是直接复制文件而是利用 SQLite 的在线备份 API 或VACUUM INTO语句VACUUM INTO backup_app.db;这个操作会把当前数据库的一致性快照导出为一个新文件新文件仍然遵循当前连接的加密配置。在 SQLCipher 连接下执行备份库也是加密的后续用相同密码打开即可。还有一个容易犯的错误很多人以为数据库加密了整机备份加密就不需要了。实际上 SQLCipher 只保护数据库文件本身程序运行时的内存、日志文件、崩溃转储仍然可能包含明文数据。安全的纵深仍然是“磁盘加密 SQLCipher 应用层的访问控制”不要指望一把钥匙解决所有问题。5. 三个常见问题的排查路径从现象到根因5.1 driver not loaded插件被丢错了目录最常见的现象是运行后打开数据库直接报driver not loaded代码本身看起来完全正常。这时候我一般按下面的顺序排查。第一步在程序启动阶段输出当前可用的驱动列表qDebug() QSqlDatabase::drivers();如果列表里没有QSQLCIPHER说明插件没有被加载。第二步确认插件的实际位置。Qt 运行时是按QCoreApplication::libraryPaths()搜索sqldrivers子目录的不是你编译生成的目录自动生效。发布程序时要把qsqlcipher.dll和它依赖的 Qt 库一起放到诸如plugins/sqldrivers下并在程序里QCoreApplication::addLibraryPath指定 plugins 根路径。第三步检查 ABI 兼容性。qsqlcipher.dll如果是 MSVC 2022 编译的你却在 MinGW 构建的 Qt 里加载多半会失败插件使用的 Qt 版本、编译器和你的主程序必须一致。用Dependency Walker或objdump查看插件依赖就能发现。5.2 file is not a database密码不对和驱动串用这个报错几乎成了 SQLCipher 入门第一课。先说结论看到这个错误先别怀疑数据库文件损坏先检查是不是开了错误引擎或配错密码。可能的情况有三种。第一种你用原生QSQLITE驱动去打开加了密的库。原生驱动不认识加密格式读取头部失败自然报file is not a database。这个属于驱动名选错。第二种你用QSQLCIPHER驱动打开但密码传错了。密码错误时 SQLCipher 不会直接把错误信息写成“密码错误”因为从实现角度看它只是拿错误密钥去解密头部解出来的内容不符合数据库格式于是底层报出同样的提示。你可以在sqlite3CLI 下手动验证sqlite3 encrypted.db PRAGMA key correct-passphrase; .tables如果PRAGMA key后.tables正常返回说明数据库本身和密码都没问题出问题的是应用层的传参。第三种文件本身就是被截断或者头部被覆盖过的真损坏库。这种情况比较少见通常伴随其他日志错误处理方案是先看备份。5.3 打开特别慢KDF 开销与连接复用有同事跑来问我“为什么我程序一启动打开数据库卡了一秒多其他 SQL 却很快” 打开慢十有八九是 KDF 在作怪。SQLCipher 每次打开新连接都要执行几十万次 PBKDF2 迭代这本来就是设计出来的安全强度参数越高越难破解但代价就是一次性开销大。如果这个开销已经影响到用户体验我会依次做三件事把连接改成全局复用不在每个查询函数里重复打开关闭数据库。这个往往能把感知延迟从几百毫秒降到几十毫秒。检查是否真的需要把 KDF 迭代次数设置到 256000。安全要求不高的内部工具可以降低到比如 64000但一定要明确你们能接受这个安全底线。不要把迭代次数降到几千那就等于给破解留后门了。在 UI 线程里不要执行db.open()放到异步任务里。这样即使打开需要 500 毫秒界面也不会卡住。我自己测试时发现SQLCipher 在低功耗 ARM 设备上打开一个 256000 迭代连接可能要 1 秒以上移动端项目如果频繁开库体验会非常明显。真到这一步除了复用连接还可以考虑把数据库常驻内存用内存副本做热点数据但要注意同步和落盘策略。最后说点实在的项目里接入 SQLCipher 不是写完代码就完事我最后总会再花半天做两件事。第一件是写一份密钥管理说明密码到底存在哪里是环境变量、独立配置文件还是密码管理器谁敢把密码硬编码进 Git 仓库就应该被拉去谈话。第二件是给备份系统做一次演练把加密库的备份文件拉回来在新环境里用密码打开确认数据可以完整恢复。另外SQLCipher 有开源和商业授权两个体系如果你的项目是商业闭源产品正式发布前要把授权边界确认清楚尤其是需要在移动端 SDK 里内置数据库的时候更要注意。技术支持群里最常见的悲剧不是技术实现不了而是发布后才发现授权不合规代码都写完了才开始改架构。我的个人建议是小项目直接采用合并源码嵌入 Qt 插件的方式维护成本最低如果团队够大、有专门的数据库工程能力再考虑把 SQLCipher 独立库和 Qt 插件解耦方便后续升级和统一密钥服务。选 Compile-time 还是 Runtime 接入其实不只是一个技术问题更多取决于你们团队能接受多大的长期维护成本。

相关新闻

PowerCenter ETL开发入门:从映射到工作流的完整实践指南

PowerCenter ETL开发入门:从映射到工作流的完整实践指南

1. 老牌ETL工具的定位:PowerCenter解决的是数据搬运的"脏活"1.1 先想清楚:你为什么要用ETL工具第一次接触Informatica ETL开发的人,多半是被项目上那句"所有数据都从这边走"推到这个工具面前的。PowerCenter在数据仓库里…

2026/10/18 10:00:01 阅读更多 →
海龟交易法则Python实现:pandas回测框架全解析

海龟交易法则Python实现:pandas回测框架全解析

简介:基于Python的pandas库实现的海龟交易法则量化策略完整方案,面向量化交易、程序化交易及Python数据分析学习者,解决从经典策略理论到可运行代码的落地难题。压缩包共3个文件,包含1个Python主程序脚本和2个CSV格式数据文件&…

2026/10/17 7:24:09 阅读更多 →
AI辅助开题报告写作:从框架搭建到内容迭代的完整指南

AI辅助开题报告写作:从框架搭建到内容迭代的完整指南

做科研的人都知道,开题报告这东西有多拧巴。它说到底也就几千到上万字,却逼着你把“要研究什么、为什么研究、怎么研究、能不能研究出来”全部想明白。我第一次带学生改开题报告的时候,最头疼的还不是内容写得对不对,而是好多人根…

2026/10/18 9:58:19 阅读更多 →

最新新闻

NLM非局部均值滤波原理与MATLAB图像去噪实现及参数调优

NLM非局部均值滤波原理与MATLAB图像去噪实现及参数调优

简介:非局部均值滤波是图像降噪领域经典算法,这套MATLAB程序包专为学习或实现该算法的开发者与研究生准备,可帮助快速理解原理并完成代码落地。压缩包共十个文件,包含两个程序源文件、七张标准测试图和一个文档说明,整…

2026/10/18 9:59:37 阅读更多 →
LLM Inference 实战:从 prefill 到 decode,用 TaoToken 统一 Key 跑通 KV cache 与 continuous batching 验证

LLM Inference 实战:从 prefill 到 decode,用 TaoToken 统一 Key 跑通 KV cache 与 continuous batching 验证

/* 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 9:59:37 阅读更多 →
无限畅用Cursor编辑器,四步轻松搞定!TaoToken统一Key接入实战

无限畅用Cursor编辑器,四步轻松搞定!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/18 9:59:37 阅读更多 →
纯CSS3 Loading文字加载动效:零JS实现逐字弹出动画原理与实战

纯CSS3 Loading文字加载动效:零JS实现逐字弹出动画原理与实战

简介:一款纯 CSS3 实现的 Loading 文字加载动效源码,面向具备前端基础或网页交互设计经验的开发者,尤其适合在网站初次加载、接口请求等待以及图片懒加载等场景使用。核心效果完全依靠层叠样式表动画属性驱动,无需额外引入大型动画…

2026/10/18 9:59:37 阅读更多 →
RTP.NET:.NET平台实现实时音视频传输的协议封装与实践

RTP.NET:.NET平台实现实时音视频传输的协议封装与实践

简介:RTP.NET 是一个基于 .NET 框架的实时传输协议库,帮助 C# 开发者在 VoIP、视频会议、流媒体等场景中实现音视频等时间敏感数据的网络发送与接收。库内封装了 RTPSession 会话管理、RTPParticipant 与会者标识、数据包化、负载类型识别等功能&#xf…

2026/10/18 9:59:37 阅读更多 →
VGG骨干+轻量时序注意力:视频理解模型设计实战拆解

VGG骨干+轻量时序注意力:视频理解模型设计实战拆解

做视频理解绕不开一个老问题:怎么在空间特征之上合理地建模时间关系。这个“VGG改进”系列我写了几十篇,大部分都集中在图像场景、网络剪枝和重参数化上,直到第23篇我才正式把方向切到视频。今天这篇就用一个实战设计笔记的方式,完…

2026/10/18 9:58:37 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

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

周新闻

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

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

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

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

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

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

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

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

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在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/16 14:23:36 阅读更多 →