简介:面向高校计算机专业学生的计算机视觉与人工智能开源教学项目,以OpenCV、Python、TensorFlow、Keras为技术栈,基于CASIS数据集实现多模态图像处理与目标检测算法,帮助学习者把深度学习理论与工程代码相结合。压缩包共220个文件,大小41.23MB,包含55个py源码、4个ipynb交互式教程、54张jpg与48张png图像样本、38个xml标注/配置文件,以及avi/mp4演示视频和pdf/txt说明文档,覆盖数据预处理、模型训练到检测演示的完整链路。目前已有60人学习下载。提供三个Session的ipynb教程,结合演示视频,可直观展示从环境配置到目标检测结果的可视化过程;PDF与TXT文档补充了安装指南、项目背景与使用说明,旨在降低上手门槛。整体目录按课程模块组织,既适合课堂演示,也适合学生课后对照源码进行实验和二次开发,可作为课程设计与自学项目参考。
1. 打开压缩包看到三个 Notebook 和四个 AVI,先别急着关掉
这个课程包的命名很长,但真正决定它是否能跑的,是_run_winpack_demo_python27.cmd和三个CASIS-OpenCV-Course_Session*.ipynb。四个视频不是摆设,768x576.avi、tree.avi、Megamind.avi、Megamind_bugy.avi分别对应不同的测试场景,后面的 SVM 模型和字母数据则把传统机器学习和深度学习串在一条线上。它解决的是高校计算机专业学生「只懂 API、不懂任务链路」的问题——从读视频帧、构造多通道特征,到用 SVM 分类、再用 TensorFlow/Keras 搭建卷积网络,最后落到目标检测,每一步都有可跑的实验。想快速补项目经验的学生、要在课堂上带实验的老师、以及从传统 CV 转向深度学习的工程人员,都能从这个包里抽出可复用的代码框架。不过要提醒一句:脚本是为 Python 2.7 写的,如果直接双击.cmd,大概率会踩环境坑,本文就从迁移环境开始。
2. 环境准备:OpenCV、TensorFlow 与 Keras 的版本兼容和安装验证
因为_run_winpack_demo_python27.cmd明确提到 python27,而现在的系统基本都是 python3,所以第一步不是直接跑项目,而是构造一个能同时容纳 OpenCV、Scikit-learn、TensorFlow 和 Keras 的虚拟环境。你会发现,只要版本不匹配,import cv2都能报错,更不用说后面的tf.keras了。下面给出一套我常用的组合,按「先图像库、再机器学习库、最后深度学习库」的顺序安装,避免依赖互相覆盖。
2.1 为什么要从 Python 2.7 迁到 Python 3
原项目的演示脚本带python27字样,这在教学包里很常见:OpenCV 课程发布时,很多学校还在用 Python 2.7。但如今 OpenCV 4.x 的 Python 绑定和 TensorFlow 2.x 早已放弃 Python 2.7,直接沿用旧脚本会碰到print语法、xrange不存在、cv2.xxx接口变更等问题。迁移不是把print加括号那么简单,更关键的是 OpenCV 的 API 名称和默认参数变了。例如旧代码常用cv2.cv.Scalar,新版本需要改为cv2.Scalar或直接用元组;cv2.CreateHOGDescriptor变成了cv2.HOGDescriptor。因此,先在 Python 3 下面重建环境,再逐个修改导入和调用,是成本最低的方式。
2.2 用虚拟环境安装 OpenCV 和深度学习框架
我一般会先建一个干净的虚拟环境,避免把系统 Python 搞乱。下面的命令在 Windows/Linux 上都适用,只要先准备好 Python 3.9 或 3.10。TensorFlow 2.13 在 Python 3.11 上也很稳定,但如果你用的是 3.12,需要留意包是否全部发布。
# 创建并激活虚拟环境,假设项目解压在 D:/CASIS python -m venv D:/CASIS/.venv # Windows: .venv\Scripts\activate Linux/macOS: source .venv/bin/activate # 安装依赖。opencv-contrib-python 用于读取视频和额外模块,二者不可重复安装。 pip install opencv-python opencv-contrib-python==4.8.1.78 # scikit-learn 用于 SVM 与数据划分,numpy/scipy 会自动带上 pip install scikit-learn==1.3.2 # TensorFlow 2.x 自带 keras,但单独装 keras 可以固定版本 pip install tensorflow==2.13.0 keras==2.13.1 # Notebook 环境与可视化 pip install jupyter matplotlib这段命令刻意把opencv-contrib-python固定为 4.8.1.78,是因为它的视频后端和 SIFT 等模块比较稳定,不用频繁跟随新版变化。scikit-learn锁定 1.3.2 是与 Python 3.9/3.10 兼容较好的版本;如果装得更新,某些旧代码里的train_test_split行为会略有不同。TensorFlow 与 Keras 必须保持主版本一致,例如 TensorFlow 2.13 配 Keras 2.13,否则会在from tensorflow import keras时出现AttributeError。Jupyter 只是用来打开课程提供的.ipynb,不是运行所必需。
安装完之后,用下面这段验证代码确认所有库都能被正确导入:
import cv2 import numpy as np import sklearn import tensorflow as tf from tensorflow import keras print("OpenCV:", cv2.__version__) print("NumPy:", np.__version__) print("scikit-learn:", sklearn.__version__) print("TensorFlow:", tf.__version__) print("Keras:", keras.__version__) # 测试 VideoCapture,没有摄像头时对象创建后 isOpened 为 False cap = cv2.VideoCapture(0) if not cap.isOpened(): print("摄像头不可用,但 OpenCV 安装正常")这段代码有两个目的:一是打印版本号,确认依赖没有互相覆盖;二是通过VideoCapture的创建行为检查 OpenCV 的视频后台是否正常。如果cap.isOpened()返回 False,不代表安装失败,只是没有可用摄像头或当前环境没有视频输入设备,之后读 AVI 文件时走文件路径即可。cv2.__version__是最直观的检查点,OpenCV 4.8.x 的输出会类似4.8.1,如果你看到 3.x,请重新安装。
2.3 项目文件与 Notebook 的对应关系
解压后你会看到一堆扩展名不同的文件,它们并不是散乱的素材。下表是我在复跑时整理出来的对应关系。
| 文件/目录 | 类型 | 在教学项目中的用途 |
|---|---|---|
CASIS-OpenCV-Course_Session1.ipynb | Notebook | 图像读取、灰度化、颜色空间转换等基础操作 |
CASIS-OpenCV-Course_Session2.ipynb | Notebook | 特征提取、SVM 分类、OCR 与字母识别 |
CASIS-OpenCV-Course_Session3.ipynb | Notebook | 神经网络、TensorFlow/Keras 图像分类与目标检测 |
768x576.avi | 视频文件 | 课程主测试视频,分辨率 768x576,用于帧处理演示 |
tree.avi | 视频文件 | 户外/树木场景,适合测试背景建模和运动分割 |
Megamind.avi | 视频文件 | 动画片段,目标运动明显,用于检测结果可视化 |
Megamind_bugy.avi | 视频文件 | 可能是故意制造 bug 的版本,用于调试训练 |
digits_svm.dat | 模型文件 | OpenCV 预训练的手写数字 SVM 模型,可被cv2.ml.SVM_load读取 |
letter-recognition.data | 特征数据 | UCI 字母识别数据集,16 维特征,用于传统机器学习实验 |
_run_winpack_demo_python27.cmd | 脚本 | Windows 上的一键演示脚本,设置环境并启动 Session 代码 |
这张表帮助你快速定位:Session1里讲的往往是视频帧和颜色通道,Session2会把digits_svm.dat和letter-recognition.data用到极致,Session3才开始让神经网络登场。Megamind_bugy.avi这个带bugy的名字,在课程里经常是故意截断或换编码的版本,用来训练学生识别“视频读取失败”和“模型跑飞”的差异。实际操作时,最好按 Session1→Session3 顺序执行,不要先跑深度学习,因为后面会用到前面构造的特征函数。
2.4 安装层最常见的三个坑
第一个坑是ModuleNotFoundError: No module named 'opencv'。容易看错:正确包名是opencv-python,导入时用import cv2,不是import opencv。第二个坑是 TensorFlow 与 NumPy 的 ABI 不兼容,会出现Segmentation fault或core dumped。解决办法是把numpy降到 TensorFlow 要求的范围内,比如 TensorFlow 2.13 要求numpy<1.24的某个版本,直接执行pip install "numpy<1.24"。第三个坑是_run_winpack_demo_python27.cmd里写死了 Python 27 路径,双击后要么闪退,要么提示找不到python.exe。
提示:在迁移阶段,不要双击
.cmd运行整个项目,而是用jupyter notebook逐个打开 Notebook,先跑通过一个单元格再继续。这样可以第一时间看到哪个 API 不兼容。
等你把环境跑通,下一步才是真正进入图像处理,而不是花半天时间解决环境报错。这里我固定了版本,是想让你少走弯路。
3. 多模态图像处理在 OpenCV 中的落地:AVI 视频读取与多通道特征拼接
这一章要处理 CASIS 包里的四个视频。很多教程只教你cv2.imread读一张图,但教学项目把视频当作图像序列来喂给模型,所以必须先理解怎么从 AVI 中持续拿到帧,再把一帧图像拆成多个模态特征。
3.1 什么叫“多模态”,以及如何用普通摄像头模拟
多模态图像处理的本意是融合来自不同传感器的信息,比如可见光与红外、RGB 与深度图。在这个教学包里并没有真正的红外相机,但可以用同一帧图像的不同表示来模拟多模态:BGR 是原始模态,HSV 是色相模态,灰度是亮度模态,Canny 边缘是结构模态。把它们沿通道方向拼接起来,就得到多通道特征张量,后面的分类器或检测器可以从不同「视角」学习目标外观。这也是为什么不建议直接省略这一步——很多目标检测算法在特征图上做融合,与这里的多通道拼接思想一致。
下面代码演示了从768x576.avi中读取一帧,并拼出多通道特征:
import cv2 import numpy as np cap = cv2.VideoCapture("768x576.avi") if not cap.isOpened(): raise IOError("无法打开 768x576.avi,请检查视频编码或文件路径") # 读取第 30 帧作为样例,跳过前面的无用帧 for _ in range(30): ret, frame = cap.read() if not ret: break # 如果视频是 BGR 顺序,转换成备用表示 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) edges = cv2.Canny(gray, 50, 150) # 手动把 3 通道 BGR、3 通道 HSV、单通道 grey、单通道 edges 拼成 8 通道张量 # dstack 沿第三维堆叠,等价于 np.concatenate([...], axis=2) multi_modal = np.dstack([frame, hsv, gray[..., np.newaxis], edges[..., np.newaxis]]) print("拼接后的张量形状:", multi_modal.shape) cap.release()逻辑说明:ret是VideoCapture.read()返回的布尔值,表示这一帧是否解码成功;在文件路径错误或编码不支持时,ret会一直为False。cvtColor把 BGR 转成灰度或 HSV,供后面的特征提取使用。Canny用两个阈值50和150检测边缘:低于 50 的梯度被认为是噪声,高于 150 的一定是边缘,介于两者之间则看是否与已检测到的边缘相连。np.dstack是沿第三维堆叠,这样multi_modal的形状就是(高, 宽, 8),其中第 0-2 通道来自 BGR,第 3-5 通道来自 HSV,第 6 通道是灰度,第 7 通道是边缘。如果你把三张单通道图直接传给分类器,很容易丢失空间关联,而拼接成多通道后,卷积网络可以直接把这 8 个通道当输入。
3.2 视频参数与帧率控制
768x576.avi这个文件名直接点明了视频尺寸:宽 768 像素、高 576 像素。处理视频时,不能假设所有视频都同尺寸,tree.avi和Megamind.avi的长宽比不同。用CAP_PROP_*可以读到真实参数,并在训练前统一缩放到一个固定大小。下面的代码打印了常用参数:
fps = cap.get(cv2.CAP_PROP_FPS) width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) total = cap.get(cv2.CAP_PROP_FRAME_COUNT) print(f"FPS={fps}, 分辨率={width}x{height}, 总帧数={total}") # 跳转到第 100 帧,再读取 cap.set(cv2.CAP_PROP_POS_FRAMES, 100) ret, frame = cap.read()参数说明:CAP_PROP_FPS得到的是浮点数,别在计算帧间隔时直接用整型;CAP_PROP_FRAME_WIDTH和CAP_PROP_FRAME_HEIGHT对应视频宽高;CAP_PROP_FRAME_COUNT是文件内总帧数,对不稳定的网络流或某些压缩格式可能不准确。cap.set(cv2.CAP_PROP_POS_FRAMES, 100)是随机跳转,但不是所有编码器都支持精确跳帧,如果发现跳到奇怪的位置,可以用连续读取替代。
3.3 用背景建模处理 tree.avi 和 Megamind.avi
多模态特征拼接后,下一步是检测视频中的运动目标。tree.avi这种场景,摄像机不动,只有树在风中摇晃,适合用背景差分把前景分离出来;Megamind.avi是动画,目标轮廓清晰,背景建模速度也会快很多。OpenCV 提供了createBackgroundSubtractorMOG2,不需要每帧都手工差分。
back_sub = cv2.createBackgroundSubtractorMOG2( history=500, varThreshold=16, detectShadows=True ) cap = cv2.VideoCapture("tree.avi") while cap.isOpened(): ret, frame = cap.read() if not ret: break # 多模态:用灰度作为输入,也可以换成 3.1 拼接后的多通道数据 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) fg_mask = back_sub.apply(gray) # 去除噪声 fg_mask = cv2.medianBlur(fg_mask, 5) cv2.imshow("foreground", fg_mask) if cv2.waitKey(30) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()这里history=500表示用于背景建模的帧数,值越大,对光线变化越不敏感,但初始适应越慢;varThreshold=16是像素值变化的阈值,值越小,对前景越敏感,容易把阴影也当成前景。detectShadows=True会生成灰色阴影标记,后续可以通过只保留白色像素来过滤阴影。注意apply可以直接传入彩色帧,内部会转灰度;这里手动转是为了保持与前面多模态代码一致。waitKey(30)控制每帧显示 30 毫秒,约 33fps,如果你的屏幕卡顿,可以调大到 50 或 100。
3.4 不同视频文件的差异决定了参数必须调整
tree.avi的叶子晃动属于小幅度运动,varThreshold默认的 16 可能会产生大量噪点;而Megamind.avi角色移动幅度大,同样的阈值就能得到干净的前景。遇到Megamind_bugy.avi时,如果视频解码出现绿屏或花屏,问题大概率不在算法,而在编码格式。我的处理方式是:先用 OpenCV 读参数确认fourcc编码,再统一用cv2.VideoCapture解码,如果解码失败,用 FFmpeg 转成 MP4 再继续,不纠结于原文件格式。下表总结了调整方向:
| 视频特征 | 建议设置 |
|---|---|
| 树叶/水波等细碎运动 | varThreshold调大到 25~40,medianBlur核调大到 7 |
| 明显的大目标移动 | varThreshold保持 16,history减小到 300 以加快适应 |
| 带阴影的室内场景 | 开启detectShadows,后续用fg_mask == 255过滤灰色 |
| 分辨率不是 768x576 | cv2.resize统一到 224x224 或 512x512,再进网络 |
完成背景建模后,可以用cv2.findContours拿到每个前景目标的边框,这样就把多模态特征和运动目标检测接在了一起。而这正为下一章的分类和检测任务提供数据来源。
4. 从 SVM 到深度学习:CASIS 数据集上的分类与目标检测实验
这一章是整个项目的重头戏。CASIS 包里同时提供了传统机器学习模型文件和深度学习 Notebook,正好用来对比「特征工程 + 轻量模型」和「端到端神经网络」两条路线。先看 SVM,再转 Keras,最后把它们组装到一个简单的目标检测流程里。
4.1 用 OpenCV 的 SVM 模型识别手写数字
digits_svm.dat是 OpenCV 自带的预训练模型,可以直接加载并预测 20x20 的手写数字图像。它属于传统的支持向量机,输入是 HOG 或原始像素特征,输出是 0-9 的分类。很多学生上来就写cv2.SVM(),但在 OpenCV 4 中 SVM 类在cv2.ml命名空间下,必须用cv2.ml.SVM_load加载。下面是一段可运行的预测代码,假设你有一张 28x28 的灰度手写数字digit.png:
import cv2 import numpy as np # 加载项目提供的模型 svm_model = cv2.ml.SVM_load("digits_svm.dat") # 读取并预处理输入图像 img = cv2.imread("digit.png", cv2.IMREAD_GRAYSCALE) img = cv2.resize(img, (20, 20)) # OpenCV 官方训练用 20x20 img = img.reshape(1, 400).astype(np.float32) # 拉平为一行特征 img = img / 255.0 # 归一化到 [0,1] _, result = svm_model.predict(img) print("预测结果:", int(result[0][0]))逻辑很简单:SVM_load从.dat文件恢复模型参数,之后predict会返回一个二维数组,第一维是样本数,第二维是预测类别。这里把图像缩放到20x20是因为模型训练时用的就是 400 维特征,如果尺寸不匹配,会出现特征维度错误。归一化用img / 255.0而不是img - 255,因为 SVM 对特征尺度比较敏感,保持 [0,1] 能避免某些样本距离计算被大数主导。如果你拿到的是letter-recognition.data,可以通过numpy.loadtxt读取,最后一列是字母标签,前 16 列是特征,训练一个线性 SVM 也能达到 90% 左右准确率,这一步留给 Notebook 里的练习去验证。
4.2 用 TensorFlow/Keras 构造 CNN 分类模型
SVM 需要人工设计特征,而 TensorFlow 和 Keras 能自动从原始像素中学习特征。下面的代码定义了一个轻量卷积网络,适配 CASIS 视频中裁剪出来的目标区域。Keras 默认张量顺序是(batch, height, width, channels),所以这里把单通道灰度图也显式保留为最后一维。
from tensorflow import keras from tensorflow.keras import layers model = keras.Sequential([ layers.Input(shape=(32, 32, 1)), layers.Conv2D(32, kernel_size=(3, 3), activation="relu"), layers.MaxPooling2D(pool_size=(2, 2)), layers.Conv2D(64, kernel_size=(3, 3), activation="relu"), layers.MaxPooling2D(pool_size=(2, 2)), layers.Flatten(), layers.Dense(64, activation="relu"), layers.Dense(10, activation="softmax") # 10 类数字或目标类别 ]) model.compile( optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"] ) model.summary()这个网络包含两个卷积层,第一个 32 个卷积核捕捉简单边缘,第二个 64 个卷积核捕捉更复杂的纹理。MaxPooling2D把特征图缩小一半,减少参数并增加平移不变性。Flatten把二维特征图拉成一维,Dense(64)做非线性组合,最后输出 10 类概率。训练时要用整数标签,所以损失函数用sparse_categorical_crossentropy;如果标签是 one-hot,就要换回categorical_crossentropy。model.summary()打印参数量,你可以看到结构是否合理,例如输入 32x32 单通道,经过两层池化后,特征图尺寸会变成 8x8,如果你觉得分类结果差,可以增加卷积核数量。
训练的数据可以直接从视频帧中裁剪 ROI,也可以使用 sklearn 的train_test_split对letter-recognition.data划分。建议先跑通上面的结构,再把学习率从默认的 0.001 调低到 0.0001,防止在小型数据集上过拟合。
4.3 目标检测的最小可用流程:背景差分 + 剪裁 + CNN
由于 CASIS 课程不是让每个学生从零训练目标检测网络,而是希望你把前面的模块组合起来,所以这里给出一个工程上最常用的 "检测 + 分类" 级联方案:先用背景建模从Megamind.avi中找出运动区域,再对每个区域做resize和 CNN 分类。这个流程在算力受限的教学环境里跑得动,也方便你验证算法效果。
def detect_and_classify(video_path, svm_model, cnn_model): cap = cv2.VideoCapture(video_path) back_sub = cv2.createBackgroundSubtractorMOG2(history=200, varThreshold=25) while cap.isOpened(): ret, frame = cap.read() if not ret: break fg = back_sub.apply(frame) # 找到所有运动区域的外包矩形 contours, _ = cv2.findContours(fg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: if cv2.contourArea(cnt) < 500: # 滤除小噪点 continue x, y, w, h = cv2.boundingRect(cnt) roi = frame[y:y+h, x:x+w] roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) roi = cv2.resize(roi, (32, 32)) # 三维数组:batch_size=1, height=32, width=32, channels=1 roi_tensor = roi.reshape(1, 32, 32, 1).astype("float32") / 255.0 score = cnn_model.predict(roi_tensor, verbose=0) label = int(score.argmax(axis=1)[0]) cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.putText(frame, str(label), (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) cv2.imshow("detection", frame) if cv2.waitKey(30) == ord("q"): break cap.release() cv2.destroyAllWindows()这段代码把前面三章的知识全部串了起来。findContours在 OpenCV 4 中返回两个值,和 3.x 的三返回值不同,所以这里写contours, _。cv2.contourArea(cnt) < 500表示只保留面积大于 500 像素的连通域,这个阈值要根据视频分辨率调整,768x576 下 500 像素相当于极小目标,如果你处理的是 1280x720,建议改成 1500。roi = frame[y:y+h, x:x+w]是 Python 的切片索引,注意行列顺序是[y, x],翻转时会出错。CNN 的predict每次只处理一张图,速度较慢,实际项目中可以用model.predict_batch或改用 TensorFlow Serving,但教学场景这样写最直观。
4.4 对比:SVM 和 CNN 在 CASIS 数据上的取舍
传统 SVM 模型digits_svm.dat的大小只有几百 KB,推理极快,即使没有 GPU 也能在视频上实时跑;而 Keras CNN 虽然准确率更高,但模型参数多、推理慢,尤其在纯 CPU 环境下,每帧预测会卡顿。CASIS 教学项目同时保留两者,目的就是让你体会:当训练数据少、类别固定时,SVM 往往够用;当数据量增大、场景多变时,CNN 的特征泛化能力更强。这也是我建议在实验报告中同时给出两种模型的 FPS 和准确率的原因。
在转移到下一环节前,务必保存训练好的模型:
svm_model.save("mysvm.dat") # cv2.ml.SVM 类型可直接保存 cnn_model.save("cnn_model.keras") # Keras 3 推荐的格式 # 也可以用 HDF5 格式兼容旧代码 # cnn_model.save("cnn_model.h5")保存模型时,OpenCV 的 SVM 和 Keras 使用两种完全不同的序列化方式,不要混用。.dat是 OpenCV 专有,.keras或.h5是 Keras 的格式。如果你要把模型传给同学,最好附上模型结构、输入尺寸和归一化方式,否则对方不知道输入是 32x32 还是 20x20。
5. 迁移与验证:把 Python 2.7 的 Windows 演示脚本改成现代流程
最后一章落到一个很实际的技巧:如何让_run_winpack_demo_python27.cmd在你的电脑上重新变得可执行,以及怎样快速判断迁移是否成功。这个脚本的核心功能大概率是设置工作目录、把 Notebook 临时转成.py文件,然后调用 Python 执行它。既然我们已经在虚拟环境里,完全可以用一个简化脚本替代它。
5.1 用等价的 bash 脚本替代 cmd
下面是 Linux/macOS 下的自验证脚本,Windows 用户可以把它改写成run_demo.bat,逻辑一致:
#!/bin/bash # 切换到项目根目录,防止相对路径失效 cd "$(dirname "$0")" # 激活虚拟环境 source .venv/bin/activate # 用 nbconvert 把三个 Session 转成可执行脚本,再逐个执行 jupyter nbconvert --to script --execute \ --ExecutePreprocessor.timeout=120 \ CASIS-OpenCV-Course_Session1.ipynb \ CASIS-OpenCV-Course_Session2.ipynb \ CASIS-OpenCV-Course_Session3.ipynb echo "如果以上输出没有抛异常,说明环境迁移成功。"关键参数是--ExecutePreprocessor.timeout=120,它指定每个单元格最长执行 120 秒。CASIS 项目中如果包含 GPU 训练,120 秒可能不够,可以改成600或直接None。nbconvert --to script会把.ipynb转为同名.py,但生成的代码会包含 Jupyter 的魔术命令,比如%matplotlib inline会报错,所以更稳妥的做法是删掉脚本中的魔术命令,或者直接使用jupyter nbconvert --execute保持 Notebook 环境。
5.2 验证视频和模型是否真正可用
迁移后不要直接开始写新代码,先跑一组快速检查:
import cv2 test_videos = ["768x576.avi", "tree.avi", "Megamind.avi", "Megamind_bugy.avi"] for name in test_videos: cap = cv2.VideoCapture(name) if not cap.isOpened(): print(f"[FAIL] {name} 无法打开") else: ret, frame = cap.read() print(f"[OK] {name}, shape={frame.shape if ret else 'decode error'}") cap.release()这段代码用isOpened确认视频文件可以被 OpenCV 解码,再用read取一帧验证实际数据是否能完整读到。frame.shape会输出(576, 768, 3)表示高、宽、通道数,顺序和很多同学的直觉相反,注意别在之后编写代码时把宽高写反。
5.3 把传统视频帧输出为 MP4 的技巧
最终课程作业经常需要提交结果视频,旧脚本的 AVI 格式在新的浏览器里可能无法直接播放。用 OpenCV 写 MP4 时要注意编码器匹配:
out = cv2.VideoWriter( "result.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps=30, frameSize=(768, 576) ) # 循环中写入处理后的帧 out.write(frame_with_box) out.release()fourcc必须用四个字符的编码标签,*"mp4v"解包成四个字母。如果播放器读不出文件,请检查frameSize是否与写入的帧尺寸完全一致,不一致时VideoWriter不会报错,但生成的文件会损坏或黑屏。此外,cv2.waitKey(30)是毫秒数,写视频时不要把waitKey和VideoWriter的帧率混搞。
至此,CASIS 这个课程包就不再是一个“下载完就吃灰”的压缩包,而是一套可以继续扩展的基线代码。你可以在同一份视频数据上尝试更换不同的 CNN 结构、调整 SVM 的核函数,或者把背景建模换成更贴合目标的检测器,逐步形成自己的计算机视觉项目。最后记得把视频统一转为 MP4 编码,用cv2.VideoWriter_fourcc(*"mp4v")输出,便于在作业或博客中直接展示。
本文还有配套的精品资源,点击获取