从零搭建课程手册系统:内容模型、权限分发与版本控制实战
1. 从零搭建一个课程手册系统:为什么我选择用“coursebook”这个思路来做
“coursebook”这个词,乍一看像是一本纸质教材的名字,但在实际项目语境里,它更像是一个代号——代表着一套围绕课程内容组织、分发、管理和迭代的轻量级系统。我最初接触这个方向,是因为身边好几个做培训、做知识付费、做企业内部赋能的朋友都在抱怨同一个问题:课程资料散落在网盘、聊天记录、邮件附件和本地硬盘里,每次开课之前找资料的时间比备课时间还长。讲师拿到的是三个月前的版本,学员手里的是另一个版本,运营那边统计完课率还得手动对表格。这些事听起来琐碎,但叠加起来就是巨大的效率黑洞。
所以我决定动手做一套以“coursebook”为核心概念的工具,目标很明确:让课程内容像一本书一样有清晰的目录结构,但又能像软件一样持续迭代、按权限分发、按进度追踪。它要解决的核心问题有三个:第一,课程内容的单一数据源问题,避免多版本混乱;第二,内容与学员之间的匹配问题,不同班次、不同角色看到的内容应该不一样;第三,内容更新后的同步问题,改了一处,所有关联的地方都要自动生效。
这套东西适合谁来参考?如果你是一个技术负责人,想给团队搭一套内部培训系统;或者你是一个独立讲师,想把自己的课程体系化管理起来;再或者你是一个产品经理,正在规划在线学习平台的内容模块——那这套思路都可以直接拿去用。它不依赖某个特定的商业平台,核心逻辑是通用的,用什么技术栈实现反而在其次。
我下面会从整体设计、核心细节、实操过程、常见问题四个维度,把我在这个项目里踩过的坑、总结的经验、以及可以直接抄作业的配置方案,全部摊开来讲。文章会比较长,因为我不想只给一个骨架,而是想把每一处“为什么这么做”都讲清楚。你可以按需跳读,但建议至少把第二和第三部分看完,那里是实操密度最高的地方。
2. 内容整体设计与思路拆解
2.1 为什么是“手册”而不是“课程平台”
市面上做在线学习的系统很多,从开源的Moodle、Canvas,到SaaS化的各种工具,功能都很全。但我在实际调研之后发现,对于大多数中小规模的课程运营场景,这些系统要么太重,要么太死。重的地方在于,它们往往自带一整套用户体系、考试系统、证书系统,你只想管个课程内容,却被迫要维护一堆用不上的模块。死的地方在于,内容结构一旦定下来,想调整层级或者换一种组织方式,成本非常高。
“coursebook”这个思路的核心差异在于:它把课程内容当作一本可以动态编排的书。书有章、节、页的层级,每一页是最小的内容单元。这个模型足够简单,简单到可以用一张表存下来,但又足够灵活,因为你可以通过调整章节的嵌套关系,表达出几乎任何课程结构。比如一个编程课,章是“基础语法”,节是“变量与类型”,页是“整数与浮点数”;换成一个瑜伽课,章是“体式练习”,节是“站姿系列”,页是“山式详解”。底层模型不变,只是数据不同。
我选择这个模型还有一个现实原因:它和大多数人的心智模型是对齐的。讲师备课的时候,脑子里想的就是“我这门课分几个部分,每个部分讲什么”,而不是“我要创建多少个学习活动、配置多少条完成条件”。降低认知负担,是让系统能被真正用起来的前提。
2.2 内容模型的设计取舍
具体到数据模型,我最终定下来的是四层结构:课程(Course)、章节(Chapter)、小节(Section)、内容块(Block)。课程是最外层,包含元信息如标题、简介、封面、标签、状态(草稿/已发布/已归档)。章节是课程下的第一级分组,通常对应一个大的知识模块。小节是章节下的第二级分组,对应一个具体的知识点或一次课的内容。内容块是小节内的最小单元,可以是文本、图片、视频链接、代码片段、附件、测验题等。
这里有一个关键取舍:内容块要不要支持嵌套?我一开始想支持,因为觉得这样更灵活。但实测下来,嵌套带来的复杂度远超收益。渲染的时候要递归,权限继承要递归,版本对比要递归,光是处理边界情况就写了一大堆代码。后来我改成扁平结构,一个小节下的内容块按顺序排列,需要分组的时候用“分组标题”这种特殊类型的内容块来模拟。这样既保留了视觉上的层次感,又避免了递归带来的各种麻烦。
另一个取舍是关于内容块的类型系统。我见过一些系统把内容块类型做得非常细,光是文本就分标题、段落、引用、代码、公式好几种。这样做的好处是渲染时可以精确控制样式,坏处是讲师在录入的时候要频繁切换类型,体验很割裂。我的做法是只保留少数几种基础类型,文本块内部用Markdown来标记格式。Markdown的语法足够表达常见的排版需求,而且讲师学习成本低,复制粘贴也方便。实测下来,这个决定让内容录入效率提升了至少一倍。
2.3 权限与分发逻辑的考量
课程内容做出来,下一步就是分发给正确的人。这里的核心问题是:同一门课,不同的人应该看到不同的内容。比如内部培训课,新员工看到的是完整版,老员工可能只需要看更新部分;再比如付费课程,试看用户只能看前两节,正式学员能看全部。
我的设计是把“内容”和“访问权限”解耦。内容本身不带权限标记,权限通过“课程-用户组”的关联表来控制。用户组是一个抽象概念,可以对应一个班级、一个角色、一个标签,甚至一个具体的用户。关联表里记录的是“某个用户组对某门课有某种级别的访问权”,级别分为只读、可评论、可编辑、可管理四档。这样做的好处是,当课程内容调整时,不需要动权限配置;当权限策略变化时,也不需要动内容。
分发逻辑上,我做了两个维度的控制:可见性范围和时效性。可见性范围决定谁能看到,时效性决定什么时候能看到。比如一门新课,可以设置成“教研组可编辑,讲师可预览,学员在开课前三天可见”。这些规则都配置在课程元信息里,系统在渲染时动态判断。实测下来,这套机制足够覆盖绝大多数场景,而且配置界面可以做得非常简单,就是几个下拉框和日期选择器的事。
2.4 技术选型的背后逻辑
技术栈方面,我选的是比较保守的组合:后端用Python的FastAPI,数据库用PostgreSQL,前端用React加TypeScript,内容渲染用Markdown解析库。选FastAPI是因为它的开发效率高,类型提示和自动文档对团队协作很友好。选PostgreSQL是因为课程内容天然适合关系型模型,而且PostgreSQL的JSON字段可以灵活存一些非结构化的元信息,比如内容块的扩展属性。
前端选React是因为生态成熟,遇到问题容易找到解决方案。TypeScript是必须的,因为课程内容模型一旦复杂起来,没有类型检查很容易出bug。Markdown解析用的是markdown-it,插件生态丰富,需要扩展语法的时候很方便。
这里我想特别说一下为什么没有选NoSQL。我试过用MongoDB存课程内容,因为觉得文档模型和课程结构很匹配。但实际用下来发现,课程内容之间的关联查询非常多,比如“找出所有引用了某个附件的课程”、“统计某个标签下的课程数量”,这些在关系型数据库里就是一句SQL的事,在MongoDB里要么写聚合管道,要么在应用层做多次查询。而且课程内容的更新往往涉及多个文档,事务支持很重要。所以最终还是回到了PostgreSQL。
3. 核心细节解析与实操要点
3.1 课程内容的数据表设计
先看最核心的几张表。课程表(courses)的字段包括:id、title、description、cover_image_url、tags、status、created_at、updated_at、published_at。其中tags用PostgreSQL的数组类型存,方便做标签筛选。status是一个枚举,取值draft、published、archived。
章节表(chapters)的字段包括:id、course_id、title、sort_order、created_at、updated_at。sort_order是一个整数,用来控制章节在课程内的排列顺序。这里我没有用浮点数来做排序,因为浮点数在频繁插入时容易遇到精度问题,整数排序虽然插入时需要更新后续记录的sort_order,但逻辑简单可靠。实测下来,一个课程的章节数量通常不会超过几十个,批量更新的成本可以忽略。
小节表(sections)的字段包括:id、chapter_id、title、sort_order、created_at、updated_at。结构上和章节表类似,只是外键指向章节。
内容块表(blocks)的字段包括:id、section_id、type、content、sort_order、metadata、created_at、updated_at。type是枚举,取值text、image、video、code、attachment、quiz。content字段存具体内容,对于text类型就是Markdown原文,对于image类型就是图片URL,对于quiz类型就是JSON格式的题目数据。metadata是一个JSONB字段,存一些类型相关的扩展信息,比如视频的时长、附件的文件大小。
注意:content字段我用的是TEXT类型而不是VARCHAR,因为Markdown内容可能很长,VARCHAR的长度限制容易踩坑。PostgreSQL的TEXT类型没有长度限制,性能上也没有明显差异。
3.2 内容块的渲染逻辑
内容块的渲染是前端最核心的逻辑。我的做法是写一个BlockRenderer组件,根据block.type分发到不同的子组件。TextBlock用markdown-it渲染Markdown,ImageBlock用img标签加懒加载,VideoBlock用video标签或者嵌入播放器,CodeBlock用语法高亮库,AttachmentBlock显示文件名和下载链接,QuizBlock渲染题目和选项。
这里有一个细节值得展开:Markdown渲染的安全性。因为课程内容可能由多个讲师协作编辑,不能假设所有输入都是安全的。我在markdown-it的配置里开启了html: false,禁止原始HTML标签,同时用DOMPurify对渲染结果做二次清洗。这样做虽然牺牲了一些灵活性(比如不能直接嵌入iframe),但安全性大大提升。如果确实需要嵌入外部内容,我单独做了一个embed类型的块,对URL做白名单校验。
另一个细节是代码块的高亮。我选的是Prism.js,因为它轻量、支持的语言多、主题丰富。配置的时候要注意,Prism的自动高亮模式在内容量大时会有性能问题,所以我改成了手动指定语言。讲师在写代码块的时候,需要在Markdown的代码围栏后面标注语言,比如```python。这个要求写进了讲师手册,实测下来大家都能接受。
3.3 版本控制与内容同步
课程内容需要迭代,迭代就需要版本控制。我的做法是给每次发布打一个快照,快照存在一个单独的course_snapshots表里,字段包括:id、course_id、version、content_json、created_at、created_by。content_json是整个课程内容树的JSON序列化结果,包含所有章节、小节、内容块的数据。
这样做的好处是,回滚非常简单,就是把某个快照的content_json反序列化回来,覆盖当前内容。对比两个版本的差异也很直接,就是对比两个JSON对象。缺点是存储空间会随着版本数量增长,但课程内容的体积通常不大,一个课程的所有内容序列化后也就几百KB,存几十个版本完全没问题。
内容同步是另一个关键点。当课程内容更新后,所有引用该内容的地方都需要感知到变化。我的做法是在课程表上维护一个content_version字段,每次内容变更就递增。前端在加载课程时,会带上这个版本号,后端在响应里也返回当前版本号。如果版本号不一致,前端就触发重新加载。这是一个简化的乐观并发控制,实测下来足够用,而且实现成本很低。
3.4 搜索与筛选的实现
课程多了之后,搜索和筛选就变得很重要。我的搜索方案分两层:数据库层面的全文搜索和应用层面的标签筛选。全文搜索用的是PostgreSQL的tsvector和tsquery,给courses表的title和description字段建了GIN索引。标签筛选用的是数组包含操作符,比如tags @> ARRAY['python']。
这里有一个性能优化的点:全文搜索的索引更新是异步的,我用了一个触发器在courses表的insert和update操作后自动更新tsvector字段。触发器的逻辑很简单,就是把title和description拼接后转成tsvector。实测下来,几千条课程记录的搜索响应时间在几十毫秒级别,完全满足需求。
标签筛选的优化在于,我给tags字段建了GIN索引,这样数组包含查询可以用到索引。另外,标签的列表我做了缓存,因为标签的变化频率很低,没必要每次查询都从数据库拉。缓存用的是内存缓存,设置了一个合理的过期时间,比如五分钟。
3.5 内容导入与导出的格式设计
课程内容需要支持导入导出,方便备份和迁移。我选的格式是JSON,因为它是结构化的,而且和内部数据模型可以直接映射。导出的JSON包含课程元信息、章节列表、小节列表、内容块列表,以及它们之间的层级关系。导入的时候,系统会校验JSON的结构,然后按层级重建数据。
这里有一个坑:导入时的ID冲突问题。如果直接使用导出文件里的ID,可能会和现有数据冲突。我的做法是导入时忽略原ID,全部重新生成,同时维护一个旧ID到新ID的映射表,用来修正内容块里可能存在的内部引用。比如一个quiz块里引用了另一个内容块的ID作为解析,导入后这个引用需要指向新的ID。
导出格式的版本号也很重要。我在JSON的顶层加了一个schema_version字段,导入时先检查版本号,如果不匹配就给出明确的错误提示,而不是尝试兼容。这样做虽然不够灵活,但避免了因为格式变化导致的静默数据损坏。
4. 实操过程与核心环节实现
4.1 环境准备与项目初始化
先说一下基础环境。我用的Python版本是3.11,PostgreSQL版本是15。这两个版本都比较稳定,而且Python 3.11在性能上有明显提升。Node.js版本是18 LTS,前端构建工具用的是Vite,比Webpack快很多。
项目初始化步骤:
数据库初始化:
提示:密码不要用简单密码,也不要把密码硬编码在代码里。我用的是环境变量加.env文件的方式,.env文件加入.gitignore,避免泄露。
4.2 后端核心接口的实现
后端我按资源类型分了几个路由模块:courses、chapters、sections、blocks、auth。每个模块提供标准的CRUD接口,加上一些业务特定的接口。
以创建课程为例,接口定义如下:
这里用Pydantic做请求体校验,FastAPI会自动生成OpenAPI文档,前端可以直接根据文档来对接。实测下来,这个组合的开发效率非常高,接口定义和文档同步更新,省去了很多沟通成本。
获取课程完整内容的接口需要做嵌套查询,把课程、章节、小节、内容块一次性组装好返回。我的做法是用SQLAlchemy的joinedload做预加载,避免N+1查询问题。具体实现:
注意:joinedload在层级深的时候会产生很大的JOIN查询,如果数据量大可能会有性能问题。我的经验是,三层以内的joinedload没问题,再深就要考虑分开查询然后在应用层组装。
4.3 前端内容渲染的关键代码
前端的BlockRenderer组件是整个渲染逻辑的核心。我把它写成一个纯函数组件,接收block对象作为props,根据type返回对应的渲染结果。
这段代码有几个细节值得说。第一,Markdown渲染后一定要用DOMPurify清洗,即使关闭了html选项,也不能完全信任渲染结果。第二,代码高亮用的是Prism的highlight方法,需要提前加载对应的语言包。我的做法是在应用启动时按需加载常用语言,比如python、javascript、typescript、bash、sql。第三,图片加了loading="lazy",视频加了preload="metadata",这些都是性能优化的基本操作,但效果很明显。
4.4 课程发布流程的实现
课程从草稿到发布,需要经过几个步骤:内容校验、权限检查、快照生成、状态变更。我把这些步骤串成一个发布接口。
内容校验主要检查必填字段是否完整,比如课程标题不能为空,每个章节至少有一个小节,每个小节至少有一个内容块。权限检查确认当前用户有发布权限。快照生成把当前内容序列化成JSON存到快照表。状态变更把课程状态从draft改成published,并记录发布时间。
提示:发布操作应该是幂等的。如果课程已经是published状态,再次调用发布接口应该返回成功而不是报错。我的做法是在状态变更前加一个判断,如果已经是published,就只更新快照和版本号,不重复变更状态。
4.5 数据迁移与备份策略
课程数据是核心资产,备份策略必须可靠。我的方案是每天凌晨做一次全量备份,用pg_dump导出整个数据库,压缩后存到对象存储。同时,每次课程发布时生成的内容快照本身就是一种增量备份,因为快照里包含了完整的课程内容。
数据迁移用的是Alembic,每次数据库结构变更都生成一个迁移脚本。迁移脚本要写清楚upgrade和downgrade两个方向的操作,方便回滚。实测下来,Alembic的自动生成功能可以覆盖大部分场景,但涉及数据转换的迁移还是需要手写。
备份恢复的演练也很重要。我每个季度会做一次恢复演练,从备份文件恢复到一个临时数据库,验证数据完整性。这个习惯是从一次惨痛经历来的:曾经有一次数据库磁盘故障,结果发现备份文件损坏,幸好有另一份更早的备份可用,但丢失了一周的数据。从那以后,备份验证就成了固定流程。
5. 常见问题与排查技巧实录
5.1 内容渲染异常的问题排查
最常见的问题是Markdown渲染出来的样式和预期不符。比如列表缩进不对、代码块没有高亮、链接点击没反应。这类问题通常有三个原因:Markdown语法写错了、渲染配置不对、CSS样式冲突。
排查步骤我总结了一个顺序:先看原始Markdown文本,确认语法正确;然后在浏览器控制台看渲染后的HTML结构,确认解析结果符合预期;最后检查CSS,看是否有样式覆盖。我遇到过好几次是CSS的全局样式影响了Markdown渲染结果,比如某个全局的p标签样式把段落间距改得面目全非。解决办法是给Markdown渲染结果加一个特定的容器类,所有样式都限定在这个容器内。
另一个渲染问题是图片加载失败。原因可能是图片URL失效、跨域限制、或者图片格式不支持。我的做法是在ImageBlock里加一个onError处理,加载失败时显示一个占位图,同时把失败的URL上报到日志,方便后续排查。
5.2 权限配置错误的快速定位
权限问题的表现通常是:用户看不到本该看到的课程,或者看到了不该看到的内容。排查的时候,我会按这个顺序检查:先确认用户的用户组归属,再确认用户组对课程的权限配置,最后确认课程的可见性范围和时效性设置。
我写了一个调试接口,输入用户ID和课程ID,返回权限判断的详细过程,包括每一步的判断结果和依据。这个接口在排查权限问题时非常有用,比手动查数据库快得多。
注意:调试接口不要在生产环境暴露,或者至少要加管理员权限校验。我一般只在开发环境开启,生产环境通过配置项控制。
5.3 内容版本冲突的处理
多人协作编辑课程内容时,版本冲突是难免的。表现是:A编辑了内容并保存,B在A保存之前也编辑了同一部分内容并保存,结果A的修改被覆盖了。
我的解决方案是乐观锁。每个课程有一个content_version字段,前端加载课程时记录这个版本号,保存时带上。后端在保存前检查版本号是否匹配,不匹配就返回409冲突,让前端提示用户刷新后重新编辑。
这个机制虽然简单,但实测下来能解决90%以上的冲突问题。剩下的10%是同一用户在多标签页编辑的情况,这种只能靠前端做本地存储和提示来缓解。
5.4 性能问题的排查与优化
课程内容多了之后,性能问题会逐渐显现。我遇到过的性能瓶颈主要有三个:课程列表查询慢、课程详情加载慢、全文搜索响应慢。
课程列表查询慢,通常是因为没有合适的索引。我在courses表的status、created_at、tags字段上都建了索引。另外,列表查询不要返回完整内容,只返回摘要信息,完整内容在详情接口里返回。
课程详情加载慢,通常是因为N+1查询。用SQLAlchemy的joinedload或者selectinload做预加载可以解决。selectinload比joinedload更适合一对多关系,因为它用单独的IN查询代替JOIN,避免了结果集膨胀。
全文搜索响应慢,通常是索引没建对。PostgreSQL的GIN索引对tsvector字段的搜索加速效果很明显。另外,搜索查询要限制返回数量,不要一次性返回所有匹配结果。
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 课程列表加载超过3秒 | 缺少索引或返回了过多字段 | 用EXPLAIN ANALYZE看查询计划 | 加索引,列表接口只返回摘要字段 |
| 课程详情加载超过5秒 | N+1查询 | 开启SQL日志,看是否有大量重复查询 | 用joinedload或selectinload预加载 |
| 搜索响应超过2秒 | 全文索引缺失或查询未命中索引 | 用EXPLAIN看是否走了GIN索引 | 建GIN索引,优化tsquery写法 |
| 发布课程时超时 | 快照序列化数据量过大 | 看快照JSON的大小 | 压缩快照内容,或异步生成快照 |
5.5 数据一致性的保障技巧
课程内容涉及多张表的关联,数据一致性很重要。我的做法是在数据库层面用外键约束保证引用完整性,在应用层面用事务保证操作的原子性。
比如删除一个章节时,需要同时删除该章节下的所有小节和内容块。这个操作必须在一个事务里完成,否则可能出现章节删了但小节还在的情况。
提示:级联删除在数据库层面也可以配置ON DELETE CASCADE,但我不推荐完全依赖数据库的级联。应用层显式删除的好处是逻辑清晰,而且可以在删除前做一些额外的检查,比如确认没有其他数据引用这些内容。
5.6 讲师使用中的常见困惑
系统做出来是给讲师用的,讲师的使用体验直接决定系统能不能推广开。我在推广过程中收集了一些常见困惑,这里列出来供参考。
第一个困惑是“为什么我改了内容,学员那边没变化”。这是因为课程内容有草稿和发布两个状态,讲师在草稿状态的修改不会自动同步到已发布的版本。需要重新发布才能生效。这个逻辑我在讲师培训时反复强调,但总有人忘记。后来我在编辑界面上加了一个明显的提示条,显示当前是草稿状态,以及上次发布的时间。
第二个困惑是“为什么我看不到某个课程”。这通常是权限问题,可能是用户组没配置对,或者课程的可见时间还没到。我建议讲师在遇到这类问题时,先找管理员确认权限配置,而不是自己反复尝试。
第三个困惑是“内容块怎么调整顺序”。我的设计是每个内容块有一个sort_order字段,调整顺序就是修改这个字段的值。前端提供了上移下移按钮,点击后交换相邻两个块的sort_order。这个操作看起来简单,但实现的时候要注意边界情况,比如第一个块不能上移,最后一个块不能下移。
6. 这套课程手册系统后续可以怎么扩展
这套系统目前已经稳定运行了一段时间,支撑了几十门课程的内容管理。但我知道它还有很多可以改进的地方。比如,可以增加内容块的评论功能,让讲师和学员在具体内容上交流;可以增加学习进度追踪,记录每个学员看到了哪个内容块;可以增加内容推荐,根据学员的标签推荐相关课程。
技术层面,我考虑过把内容块的渲染改成服务端渲染,这样首屏加载会更快,对SEO也更友好。但服务端渲染会引入额外的复杂度,需要评估收益是否值得。另一个方向是引入协同编辑,让多个讲师可以同时编辑同一门课程。这个功能技术上可行,但产品设计上需要仔细考虑,因为协同编辑的冲突处理比简单的乐观锁要复杂得多。
如果你打算基于这套思路做自己的课程手册系统,我的建议是先跑通最小闭环:课程创建、内容编辑、发布、查看。这四个环节打通了,系统就能用起来。然后再根据实际使用中的反馈,逐步增加功能。不要一开始就追求大而全,那样很容易陷入过度设计的泥潭。
我在实际使用中发现,最受欢迎的功能往往不是那些复杂的高级功能,而是最基础的内容编辑体验。讲师能不能快速录入内容、能不能方便地调整结构、能不能直观地预览效果,这些才是决定系统生死的关键。所以如果你要优化,优先优化编辑体验,其他的都可以往后放。
相关推荐
基于Python+Open CV的手势识别算法设计
伴随着人工智能时代的到来,人机交互的领域也逐渐成为研究的一大方向;其中,手势识别是人机交互领域的一项关键技术,自此手势识别也迎来了一波高潮,近几年无论是在消费领域、电子领域、数码领域、家电领域甚至汽车领域上,都能或多或少的见到手势识别的身影。 手势识别在设计智能高效的人机界面方面具有至关重要的作用, 目前手势识别已应用到手语识别、智能监控、到虚拟现实等各个领域,手势识别的原理都是利用各种传感器(例如红外、摄像头等)对手部的形态进行捕捉并进行建模,形成一个模型信息的序列帧,而后将这些信息序列转换为机器能够识别的相对应指令(例如打开、切换菜单、移动等)来完成控制。 采用Python的集成开发环境Pycharm进行本次课程设计,在Pycharm中进行需要库(模块)的下载,调取电脑摄像头,按帧读取摄像头采集到的头像,形态学处理,图像旋转(由于摄像头采集到的图像是镜像,需要用cv2.flip将摄像头采集到的图像进行处理),选取图片中固定位置作为手势输入,用红线画出手势识别框,基于hsv的肤色检测,进行高斯滤波,找出轮廓,求出图像中手势的凹凸点,手指间角度求取。
ROS-CV-ImageConverter:将ROS图像转换为OpenCV(cv :: Mat)类型的图像
ROS-CV图像转换器 依存关系 cv_bridge(ROS软件包)
计算机视觉图像处理-CV2与图像插值算法
用最近邻插值与双线性插值原理完成图像缩放 最近邻插值,是指将目标图像中的点,对应到源图像中后,找到最相邻的整数点,作为插值后的输。缺点:使用最近邻插值算法处理图像放大时,在图象中可能出现明显的块状效应 双线性插值,在两个方向上做三次线性插值。使用双线性插值算法处理图像放大时,在图象中可能出现马赛克 代码 import cv2 img = cv2.imread(’./xiabang.jpg’, cv2.IMREAD_UNCHANGED) #缩放比例 scale_percent = 0.3 width = int(img.shape[1] * scale_percent) height = int
基于水平集CV模型的图像分割(Matlab代码)
基于水平集CV模型的图像分割(Matlab代码),全套代码
导向滤波(guide_filter)的python实现
导向滤波(guide_filter)的python实现,可以结合暗通道;理论实现比较好的图像去噪效果。

