计算机视觉课程资料:六次实验与OpenCV实战避坑指南

发布时间:2026/10/13 4:55:46
计算机视觉课程资料:六次实验与OpenCV实战避坑指南 简介这份资源是浙江大学2020-2021秋冬学期计算机视觉课程的配套资料合集面向正在学习计算机视觉、需要动手完成课程实验的高校学生与自学者。内容覆盖从基础图像处理到深度学习的完整链路包含六次实验制作无声小短片视频、圆形与直线检测、Harris角点检测、Eigenface人脸检测以及CNN实现并附有复习资料与OpenCV学习材料适合按课程节奏逐项实践或作为期末复习参考。压缩包共1319个文件以436个jpg图像、419个txt文本、400个pgm灰度图为主另含14个pdf讲义、7个py脚本、7个png图示及若干模型checkpoint与json配置整体约72.75MB目录按实验与资料分块组织便于检索。目前已有330人学习下载。读者可借此获得六次实验的完整素材与代码框架理解角点检测、人脸识别与卷积网络等算法的实现细节并借助OpenCV资料与复习文档查漏补缺适合作为课程作业与考前梳理的实用参考。1. 从六份实验报告说起这套计算机视觉资料到底能帮你省下多少试错时间如果你正在自学计算机视觉或者带本科毕设、准备课程大作业大概率经历过这样的场景OpenCV 的 API 翻来覆去就那几个但真到写实验报告时连一个像样的图像滤波对比图都凑不齐想复现一个特征匹配流程结果卡在cv2.findHomography的参数上一调就是一下午。这套来自某高校 2020-2021 秋冬学期的计算机视觉课程资料包含 6 次完整实验、配套复习资料和一份 OpenCV 学习资料恰好能填上这个缺口。它不是那种只给结论的课件而是把每次实验的代码、数据、报告模板都摆出来了适合想按部就班走一遍经典 CV 流程的人。我翻完这六次实验的目录后第一反应是如果当年我学 SIFT 的时候有这套东西至少能少熬三个晚上。2. 六次实验的骨架拆解从像素操作到特征匹配的递进路线2.1 实验一与实验二图像基础操作与空域滤波前两次实验是整个课程的入口重点落在像素级操作和空域滤波上。实验一通常要求读取图像、分离通道、做灰度化和直方图均衡化实验二则进入卷积核的世界要求手写均值滤波、高斯滤波和中值滤波并与 OpenCV 内置函数做对比。这套资料里最值钱的部分是它给出了完整的对比脚本而不是只丢一个cv2.GaussianBlur就完事。我一般会建议按这个顺序跑一遍import cv2 import numpy as np import matplotlib.pyplot as plt # 读取图像并转为灰度图 img cv2.imread(lena.jpg, cv2.IMREAD_GRAYSCALE) # 手动实现均值滤波3x3 kernel_mean np.ones((3, 3), np.float32) / 9 manual_mean cv2.filter2D(img, -1, kernel_mean) # OpenCV 内置均值滤波 builtin_mean cv2.blur(img, (3, 3)) # 计算两种方式的差异 diff cv2.absdiff(manual_mean, builtin_mean) print(f最大差异值: {diff.max()}) # 正常应为 0 或极小值 # 并排显示 plt.subplot(131), plt.imshow(img, cmapgray), plt.title(Original) plt.subplot(132), plt.imshow(manual_mean, cmapgray), plt.title(Manual Mean) plt.subplot(133), plt.imshow(builtin_mean, cmapgray), plt.title(Builtin Mean) plt.show()这段代码的逻辑很直白用filter2D手动卷积一个 3x3 的均值核再和cv2.blur的结果做逐像素差值。如果差值不为零说明边界处理方式不同——OpenCV 默认对边界做反射填充而手动卷积如果不指定borderType默认是常量填充。这个坑我在第一次跑的时候就踩了输出图像边缘有一圈暗带后来把borderTypecv2.BORDER_REFLECT加上才对齐。参数上ddepth-1表示输出图像深度与输入一致如果输入是uint8卷积结果会被截断到 0-255做对比时最好先转成float32再算差值。实验二还会涉及高斯核的生成。资料里给了一个手动计算高斯核的函数用np.exp和归一化实现和cv2.getGaussianKernel的结果做对比。这里的关键参数是sigma它控制核的权重分布sigma 越大中心像素的权重被摊得越平滤波效果越接近均值滤波。我通常会把 sigma 设成 1.0 到 2.0 之间再大就糊得看不清细节了。2.2 实验三与实验四频域变换与边缘检测实验三进入频域要求实现傅里叶变换、频谱图绘制和低通/高通滤波。实验四则是边缘检测包括 Sobel、Laplacian 和 Canny 算子。这两次实验的难度明显上一个台阶因为频域的概念对初学者不太友好而 Canny 的双阈值又特别依赖经验。频域实验的核心代码通常长这样import cv2 import numpy as np img cv2.imread(lena.jpg, cv2.IMREAD_GRAYSCALE) f np.fft.fft2(img) fshift np.fft.fftshift(f) # 构建理想低通滤波器 rows, cols img.shape crow, ccol rows // 2, cols // 2 mask np.zeros((rows, cols), np.uint8) D0 30 # 截止频率 for i in range(rows): for j in range(cols): if np.sqrt((i - crow)**2 (j - ccol)**2) D0: mask[i, j] 1 fshift_filtered fshift * mask f_ishift np.fft.ifftshift(fshift_filtered) img_back np.fft.ifft2(f_ishift) img_back np.abs(img_back) # 显示频谱和滤波结果 magnitude_spectrum 20 * np.log(np.abs(fshift) 1)这里有几个参数需要留意。D0是截止频率设得太小图像会糊成一片设得太大等于没滤波。我一般从 30 开始试根据图像尺寸调整。另外np.log里的1是为了避免对零取对数这个细节资料里没写但实际跑的时候不加会报 warning。频域滤波的坑在于理想低通滤波器会产生振铃效应因为它的频率响应是矩形窗对应的空域是 sinc 函数。如果想减轻振铃可以换成巴特沃斯或高斯滤波器资料里也给了对应的实现。边缘检测部分Canny 的双阈值是重灾区。资料里建议的低高阈值比是 1:2 或 1:3但实际图像千差万别。我通常先用cv2.Canny的默认阈值跑一遍看边缘图的效果再根据漏检和误检情况调整。如果边缘断断续续说明低阈值太高如果背景噪声全被检出来说明高阈值太低。Sobel 算子则要注意ksize参数取 3 时是标准的 3x3 核取 -1 时用 Scharr 核后者对细边缘更敏感。2.3 实验五与实验六特征提取与图像拼接最后两次实验是整套资料的高潮涉及 Harris 角点检测、SIFT 特征提取、特征匹配和单应性矩阵估计。实验五通常要求实现 Harris 角点检测实验六则是用 SIFT FLANN 做图像拼接。这部分代码量最大也最容易出问题。一个典型的 SIFT 匹配和拼接流程如下import cv2 import numpy as np # 读取两张待拼接图像 img1 cv2.imread(left.jpg) img2 cv2.imread(right.jpg) # 创建 SIFT 检测器 sift cv2.SIFT_create() # 检测关键点和计算描述子 kp1, des1 sift.detectAndCompute(img1, None) kp2, des2 sift.detectAndCompute(img2, None) # FLANN 匹配器参数 FLANN_INDEX_KDTREE 1 index_params dict(algorithmFLANN_INDEX_KDTREE, trees5) search_params dict(checks50) flann cv2.FlannBasedMatcher(index_params, search_params) # KNN 匹配 matches flann.knnMatch(des1, des2, k2) # Lowes ratio test 筛选优质匹配 good_matches [] for m, n in matches: if m.distance 0.7 * n.distance: good_matches.append(m) # 至少需要 4 对匹配点计算单应性矩阵 if len(good_matches) 4: src_pts np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2) dst_pts np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2) H, mask cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) # 用 H 矩阵做透视变换 result cv2.warpPerspective(img1, H, (img1.shape[1] img2.shape[1], img1.shape[0])) result[0:img2.shape[0], 0:img2.shape[1]] img2这段代码里ratio test的 0.7 是个经验值资料里也是这么写的。如果匹配点太少可以放宽到 0.75 或 0.8但误匹配会增多。findHomography的 RANSAC 阈值设为 5.0单位是像素意思是重投影误差超过 5 个像素的点会被当作外点。这个值不能太小否则内点不够矩阵估计不稳定也不能太大否则外点混进来拼接结果会扭曲。我踩过的坑是两张图像如果重叠区域太小SIFT 匹配点不够findHomography会返回 None后面warpPerspective直接报错。所以实际跑之前最好先检查len(good_matches)是否大于 10。3. OpenCV 学习资料怎么用别把它当字典翻3.1 资料的组织逻辑与检索方式这份 OpenCV 学习资料不是那种按 API 字母顺序排列的手册而是按功能模块组织的图像读写、颜色空间转换、几何变换、形态学操作、直方图、轮廓、视频处理等。每个模块下有示例代码和参数说明。我一般不会从头读到尾而是把它当“配方集”用——遇到具体任务时先翻到对应模块看示例代码能不能直接改。比如要做形态学开运算去噪资料里会给出cv2.morphologyEx的完整调用和结构元素的创建方式。但要注意资料里的结构元素是用cv2.getStructuringElement生成的如果你手写一个np.ones((3,3))传进去效果可能不一样因为getStructuringElement生成的矩形核默认锚点在中心而手写的核在filter2D里锚点也是中心但在morphologyEx里行为有差异。这个细节资料里没展开但实际写代码时如果发现腐蚀膨胀结果偏移大概率是锚点问题。3.2 把学习资料和六次实验对照着看单独看 OpenCV 学习资料容易忘因为缺乏任务驱动。我的习惯是每做一次实验就翻到学习资料里对应的章节把示例代码和实验代码做 diff。比如实验二做高斯滤波学习资料里可能只给了cv2.GaussianBlur的调用但实验里要求手动实现这时候对照着看就能理解sigma参数在内部是怎么影响核权重的。另外学习资料里的视频处理部分和六次实验没有直接重叠但如果你想把实验扩展到视频流比如做实时边缘检测那部分代码可以直接拿来改。我试过把 Canny 检测套到摄像头帧上延迟大概在 30ms 左右对于 640x480 的分辨率来说够用了。参数上视频处理要注意cv2.VideoCapture的缓冲区和帧率设置默认的CAP_PROP_BUFFERSIZE是 0如果处理速度跟不上帧会堆积导致延迟越来越大。4. 避坑与排查六次实验里最容易翻车的五个地方4.1 现象SIFT 创建失败报错AttributeError: module cv2 has no attribute SIFT_create原因OpenCV 版本问题。从 4.4.0 开始SIFT 专利到期被移回主模块但如果你装的是更早的版本或者装的是opencv-python-headless可能没有这个函数。另外某些国内镜像源里的包版本较旧也会导致这个问题。解决先查版本print(cv2.__version__)如果低于 4.4.0升级到 4.5 以上。如果用的是opencv-contrib-python确认安装的是完整版而不是 headless 版。升级命令pip install --upgrade opencv-python opencv-contrib-python。4.2 现象频域滤波后图像全黑或全白原因傅里叶变换后的直流分量在fftshift之后位于中心如果滤波器把中心也滤掉了图像均值就没了结果要么全黑要么全白。另外np.fft.ifft2的结果是复数直接imshow会显示异常。解决检查滤波器的中心位置是否保留。低通滤波器的中心应该是 1高通滤波器的中心应该是 0。显示之前用np.abs()取模再归一化到 0-255。如果还是全黑检查D0是不是设得太小把大部分频率都滤掉了。4.3 现象Canny 边缘检测结果断断续续或者背景噪声全被检出来原因双阈值设置不当。低阈值太高弱边缘被丢弃高阈值太低噪声被当成边缘。另外如果输入图像没有做高斯模糊噪声会直接进入梯度计算导致大量伪边缘。解决先用cv2.GaussianBlur做 3x3 或 5x5 的平滑再跑 Canny。阈值方面我一般用np.median算图像梯度中值然后设低阈值为中值的 0.66 倍高阈值为中值的 1.33 倍。这个比例不是固定的但比瞎猜靠谱。4.4 现象findHomography返回 None或者拼接结果严重扭曲原因匹配点太少或者匹配点里外点太多。SIFT 在纹理贫乏的区域比如白墙、天空提取不到足够的关键点导致good_matches数量不够。另外如果两张图像有重复纹理比如窗户、瓷砖FLANN 会匹配到错误的位置RANSAC 也救不回来。解决先打印len(good_matches)如果小于 10考虑换用 ORB 或 AKAZE 特征它们对纹理要求低一些。如果匹配点够但结果扭曲把 RANSAC 阈值从 5.0 降到 3.0或者用cv2.findHomography的methodcv2.LMEDS替代 RANSAC后者对高斯噪声更鲁棒。4.5 现象手动卷积和 OpenCV 内置函数结果不一致原因边界填充方式不同。filter2D默认的borderType是BORDER_DEFAULT实际是反射填充而手动实现时如果没指定默认是常量填充补零。另外如果输入是uint8卷积结果会截断导致负值变 0正值超过 255 变 255。解决在filter2D里显式指定borderTypecv2.BORDER_REFLECT并且把输入转成float32再做卷积。对比时用cv2.absdiff算差值如果最大差值在 1e-3 量级说明只是浮点误差可以接受。5. 进阶技巧用这套资料做一次完整的图像拼接验证5.1 从六次实验里抽出一个可复现的验证流程如果你想验证自己是否真的吃透了这套资料我建议做一次端到端的图像拼接从实验一读取图像开始到实验六的 SIFT 匹配和单应性变换结束中间穿插实验三的频域滤波做预处理。具体步骤是先对两张待拼接图像做傅里叶变换用高通滤波增强边缘再跑 SIFT 匹配最后用warpPerspective拼接。这个流程能同时检验频域、特征和几何变换三个模块的理解。验证时我一般会记录几个指标匹配点数量、RANSAC 内点率、拼接后的重叠区域宽度。匹配点数量在 50 到 200 之间比较理想内点率高于 60% 说明匹配质量不错重叠区域宽度至少要有图像宽度的 20%否则拼接缝会很明显。如果内点率低于 40%我会回头检查频域滤波是不是把太多细节滤掉了或者 SIFT 的contrastThreshold设得太高。5.2 参数调优的边界与经验值这套资料里的参数大多是默认值但实际用的时候需要根据图像调整。我整理了一个常用参数的边界表参数常用范围作用调参方向SIFT contrastThreshold0.01 - 0.04过滤低对比度关键点值越大关键点越少Lowes ratio0.6 - 0.8筛选优质匹配值越小匹配越严格RANSAC 重投影阈值3.0 - 8.0判断内点值越小内点要求越高Canny 低阈值50 - 150弱边缘检测值越低边缘越多高斯核 sigma0.8 - 2.0平滑程度值越大越模糊这张表不是金科玉律但能帮你快速定位调参方向。比如拼接结果扭曲先看 RANSAC 阈值是不是太大匹配点太少先看 ratio 是不是太严。5.3 一个我反复踩过的坑最后说一个血泪经验这套资料里的实验图像是固定的但如果你换成自己的照片SIFT 匹配可能会因为 EXIF 方向信息而失败。手机拍的照片通常带旋转标记OpenCV 读取时不会自动旋转导致两张图的方向不一致匹配点全乱。我后来养成了一个习惯读图之后先跑一遍cv2.rotate根据 EXIF 校正或者直接用PIL的ImageOps.exif_transpose处理后再转成 OpenCV 格式。从那以后我每次做图像拼接之前都强制走一遍方向检查再也没出现过匹配点对不上的玄学问题。希望这套资料能帮你把计算机视觉的基础流程跑通少走点我当年走过的弯路。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询