news 2026/10/9 1:46:26

AI for Beginners 第 6 课:计算机视觉入门 —— 用 OpenCV 做图像预处理、帧差运动检测与光流分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI for Beginners 第 6 课:计算机视觉入门 —— 用 OpenCV 做图像预处理、帧差运动检测与光流分析
  • 教程
  • 人工智能
  • 机器学习
  • 深度学习

【免费下载链接】AI-For-Beginners

12 Weeks, 24 Lessons, AI for All!

项目地址:https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners
点击查看免费下载

导读

计算机视觉(Computer Vision)是让计算机对数字图像获得“高层理解”的技术领域,是 AI for Beginners 课程中“第 4 周:计算机视觉”的第一课(第 6 课)。本文围绕课程配套文档 Introduction to Computer Vision 展开:先讲清楚计算机视觉的任务谱系与四大 Python 图像库的定位,再结合仓库中可运行的 OpenCV.ipynb 演示三个完整实战案例——盲文(Braille)图像预处理切分、基于帧差(frame difference)的视频运动检测、基于密集光流(dense optical flow)的运动方向分析,最后介绍配套实验室作业(用光流提取手势的上下左右方向)。读完后,你将能独立使用 OpenCV 完成图像加载、色彩空间转换、阈值化、透视变换与光流计算等经典算法流程。

计算机视觉的任务谱系

按照课程文档的定义,计算机视觉的目标是让计算机对数字图像获得高层理解。由于“理解”可以包含多种含义,这一领域的任务范围相当宽:

  • 图像分类(image classification):最简单的任务,判断整张图属于哪一类;
  • 目标检测(object detection):在图中找到具体对象的位置;
  • 事件检测(event detection):理解画面中正在发生什么;
  • 图像描述:用自然语言描述一张图片;
  • 3D 场景重建:从图像重建三维场景;
  • 针对人形的专项任务:年龄与情绪估计、人脸检测与识别、3D 姿态估计等。

课程指出,计算机视觉通常被视为 AI 的一个分支,如今绝大多数视觉任务都靠神经网络解决——课程接下来的 卷积神经网络(CNN)一课 就会专门讲解视觉领域专用的网络结构。但本节的重点是另一半:在把图像交给神经网络之前,往往先用算法化的技术(即“经典计算机视觉”)对图像做增强和预处理。这正是 OpenCV 用武之地。

四大 Python 图像处理库的定位

课程文档给出了选型参考,四者各有侧重:

库语言/实现定位与典型用途
imageioPython读写各种图像格式,支持 ffmpeg,适合把视频帧转成图像
Pillow(PIL)Python功能更强一些,支持变形(morphing)、调色板调整等图像操作
OpenCVC++ 内核 + Python 接口事实上的图像处理标准库(de facto standard),算法丰富
dlibC++ + Python 接口实现多种机器学习算法,适合人脸与面部关键点检测等较难任务

本节聚焦 OpenCV。仓库中 第 5 周(NLP)的 PyTorch 需求文件 中固定了opencv-python==4.5.1.48、numpy==1.22.0、Pillow==12.2.0、matplotlib,这些版本组合可以作为本课实验环境的安装参考。

OpenCV 基础:图像即 NumPy 数组

加载图像与色彩空间

OpenCV 的核心思想是:图像在 Python 中就是一个 NumPy 数组。以 320x200 像素为例:

  • 灰度图存放在200x320的二维数组中;
  • 彩色图为200x320x3的三维数组,第三个维度对应 3 个颜色通道。

加载与显示图像的最小代码(与 OpenCV.ipynb 的加载流程一致,仓库配套素材为盲文照片 data/braille.jpeg):

import cv2 import matplotlib.pyplot as plt im = cv2.imread('data/braille.jpeg') # 返回 NumPy 数组 print(im.shape) # (高, 宽, 3) plt.imshow(im)

这里有一个新手最容易踩的坑:OpenCV 出于历史原因使用 BGR(蓝-绿-红)通道顺序,而 matplotlib 等其他 Python 工具使用更常见的 RGB。直接用plt.imshow显示 OpenCV 读入的彩色图,颜色会发“青”。修正方式有两种——在 NumPy 数组层面交换维度,或调用 OpenCV 的cvtColor:

im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)

同一个cvtColor函数还能完成其他色彩空间变换,例如转灰度(cv2.COLOR_BGR2GRAY)或转到 HSV(色相-饱和度-明度)空间。在 OpenCV.ipynb 中,由于对盲文样本不关心颜色,第一步就是:

bw_im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) # 转灰度,shape 变为 (高, 宽)

OpenCV 同样可以逐帧加载视频(cv2.VideoCapture+ 循环vid.read()),OpenCV.ipynb 中就演示了对 data/motionvideo.mp4 的逐帧读取。

图像预处理工具箱

在把图像喂给神经网络之前,通常要做若干预处理步骤。课程文档列出的 OpenCV 能力清单如下,每项都给出可直接使用的调用形式:

  • 缩放(Resize):im = cv2.resize(im, (320,200), interpolation=cv2.INTER_LANCZOS)
  • 模糊(Blur):中值模糊im = cv2.medianBlur(im,3);高斯模糊im = cv2.GaussianBlur(im, (3,3), 0)
  • 亮度/对比度调整:通过 NumPy 数组运算实现(课程文档引用了 StackOverflow 上的经典做法)
  • 阈值化(Thresholding):调用cv2.threshold/cv2.adaptiveThreshold,课程认为这通常优于直接调亮度对比度
  • 几何变换:
    • 仿射变换(Affine transformation):当你知道图中三个点的源/目标位置时,可组合旋转、缩放与错切;其特点是保持平行线平行;
    • 透视变换(Perspective transformation):当你知道四个点的源/目标位置时可用,典型场景是用手机斜拍一张矩形文档、然后把文档“拉正”成矩形;
  • 光流(Optical flow):理解图像内部像素的运动。

实战案例一:盲文照片的预处理与字符切分

OpenCV.ipynb 的第一个完整案例是:从一张盲文书籍照片出发,纯靠传统计算机视觉手段把一个个盲文符号切分出来,供后续神经网络分类。这正是课程反复强调的理念——如果视觉技术能让神经网络的工作变简单,就应该做,因为它可以用更少的训练数据解决问题。

第一步:多级阈值化增强

盲文点阵在照片中光照不均,notebook 采用的是一组“模糊 + 自适应阈值 + Otsu 全局阈值”交替执行的增强流水线:

im = cv2.blur(bw_im, (3,3)) im = cv2.adaptiveThreshold(im, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 5, 4) im = cv2.medianBlur(im, 3) _, im = cv2.threshold(im, 0, 255, cv2.THRESH_OTSU) im = cv2.GaussianBlur(im, (3,3), 0) _, im = cv2.threshold(im, 0, 255, cv2.THRESH_OTSU)

这里同时出现了课程文档提到的两类阈值函数:cv2.adaptiveThreshold(按局部均值自适应决定阈值,适合光照不均的图)和cv2.threshold(..., cv2.THRESH_OTSU)(Otsu 算法自动选取全局最优阈值)。两轮“模糊—阈值”交替可以显著压掉噪声。

第二步:用 ORB 提取特征点定位点阵

要处理盲文,需要先“提取”每个凸点,即把图像变成一组点坐标。notebook 使用 ORB 特征检测(SIFT、SURF、ORB 一类的特征提取技术):

orb = cv2.ORB_create(5000) f, d = orb.detectAndCompute(im, None) pts = [x.pt for x in f] # 所有特征点坐标

第三步:透视变换把倾斜的文本“拉正”

从所有特征点的 min/max 坐标可以算出整段文本的包围盒。由于照片拍摄角度问题,文本可能是倾斜的,因此对包围盒四角(外加 5 像素偏移off=5)做透视变换,映射到一个尺寸等比例的矩形目标区域:

off = 5 src_pts = np.array([(min_x-off, min_y-off), (min_x-off, max_y+off), (max_x+off, min_y-off), (max_x+off, max_y+off)]) w = int(max_x - min_x + off*2) h = int(max_y - min_y + off*2) dst_pts = np.array([(0,0), (0,h), (w,0), (w,h)]) ho, m = cv2.findHomography(src_pts, dst_pts) trim = cv2.warpPerspective(im, ho, (w, h))

这正是课程文档中“透视变换”要点的落地:已知四个源点和目标点位置,用findHomography求单应矩阵,再warpPerspective完成变换。

第四步:网格切片得到单个符号

对齐之后,切分就变得简单了——按固定的字符格(高 36、宽 24 像素)遍历,跳过全空的格子,逐块yield:

char_h = 36 char_w = 24 def slice(img): dy, dx = img.shape y = 0 while y + char_h < dy: x = 0 while x + char_w < dx: # 跳过空行/空格 if np.max(img[y:y+char_h, x:x+char_w]) > 0: yield img[y:y+char_h, x:x+char_w] x += char_w y += char_h

切分结果与流水线各阶段效果如下图所示:左为预处理后的二值图,右为切出的单个盲文符号,形态上已经接近可直接送进分类网络的样本。

实战案例二:帧差法视频运动检测

第二个案例解决一个非常高频的工程问题:监控摄像头前是否发生了变化(有变化时再用神经网络去理解画面,远比逐帧跑网络便宜)。

核心思想(课程文档与 notebook 中一致):如果摄像头是固定的,连续两帧应该非常相似。帧就是数组,直接把相邻两帧做数组减法就能得到逐像素差异——静态场景差异很小,一旦出现明显运动,差值就会显著升高。

读帧、转灰度、计算差异范数

vid = cv2.VideoCapture('data/motionvideo.mp4') c = 0 frames = [] while vid.isOpened(): ret, frame = vid.read() if not ret: break frames.append(frame) c += 1 vid.release() print(f"Total frames: {c}") # 运动检测不关心颜色,全部转灰度后做帧差 bwframes = [cv2.cvtColor(x, cv2.COLOR_BGR2GRAY) for x in frames] diffs = [(p2 - p1) for p1, p2 in zip(bwframes[:-1], bwframes[1:])] diff_amps = np.array([np.linalg.norm(x) for x in diffs]) # 每帧变化的“幅度”

diff_amps曲线直观反映了画面中“活动的量”:

用滑动平均 + 阈值定位“事件”区间

为了生成“事件发生时抓拍中间帧”的报告,需要找到事件的起止帧。notebook 用滑动平均(np.convolve实现)平滑曲线、设定阈值 13000,再用np.where找出超过阈值的帧,最后提取一段长度大于 30 帧的连续区间:

def moving_average(x, w): return np.convolve(x, np.ones(w), 'valid') / w threshold = 13000 active_frames = np.where(diff_amps > threshold)[0] def subsequence(seq, min_length=30): ss = [] for i, x in enumerate(seq[:-1]): ss.append(x) if x + 1 != seq[i+1]: if len(ss) > min_length: return ss ss.clear() sub = subsequence(active_frames) # 展示事件中间的帧 plt.imshow(cv2.cvtColor(frames[(sub[0]+sub[-1])//2], cv2.COLOR_BGR2RGB))

注意 notebook 特意演示了同一个帧在不转 RGB 与转 RGB 之后的显示差异,再次印证了开头讲的 BGR/RGB 通道顺序问题。

实战案例三:密集光流分析运动方向

帧差只能告诉你“变化有多大”,却不能告诉你什么在动、往哪里动。这就需要光流(optical flow)。课程文档区分了两种类型:

  • 密集光流(Dense Optical Flow):为画面中每个像素计算一个运动向量,得到完整的向量场;
  • 稀疏光流(Sparse Optical Flow):选取图中显著特征(如边缘),逐帧追踪它们的轨迹。

计算密集光流

OpenCV.ipynb 使用 Farneback 算法对每对相邻灰度帧求光流:

flows = [cv2.calcOpticalFlowFarneback(f1, f2, None, 0.5, 3, 15, 3, 5, 1.2, 0) for f1, f2 in zip(bwframes[:-1], bwframes[1:])] flows[0].shape # (高, 宽, 2):两个通道分别是 x、y 方向分量

每帧光流的形状与画面相同、带 2 个通道,恰好对应运动向量在 x、y 方向的分量。

用 HSV 颜色编码可视化向量场

二维向量场不好直接画,notebook 用了一个巧妙的技巧:把光流转到极坐标得到每个像素的“方向 + 强度”,然后构造 HSV 图像——色相(H)表示方向,明度(V)表示强度,饱和度固定 255:

def flow_to_hsv(flow): hsvImg = np.zeros((flow.shape[0], flow.shape[1], 3), dtype=np.uint8) mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1]) hsvImg[..., 0] = 0.5 * ang * 180 / np.pi hsvImg[..., 1] = 255 hsvImg[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX) return cv2.cvtColor(hsvImg, cv2.COLOR_HSV2BGR)

得到的彩色帧中,不同颜色直接对应不同运动方向——在 notebook 的示例中,偏绿表示向左移动,蓝色表示向右移动。从源码逻辑看,光流还有一个实用推论:如果一帧内几乎所有像素都朝大致同一方向移动,说明是相机本身在移动,此时可以对画面做运动补偿。

实验室作业:用手势光流判断上/下/左/右

课程的配套作业见 lab/README.md:给定一段手掌在静止背景前左右上下移动的素材 palm-movement.mp4(截图如下),你的目标是用光流判断视频的哪些片段分别包含上、下、左、右运动;进阶目标是用肤色检测实际追踪手掌/手指。

MovementDetection.ipynb 给出了分步提示:

  1. 按课上方法读取视频帧;
  2. 计算密集光流,并转到极坐标;
  3. 对每帧光流构建方向直方图(统计有多少向量落在各个角度 bin 中)——建议先把幅度低于阈值的向量清零,以滤掉眼球、头部等细小抖动;
  4. 观察直方图后,选取对应上/下/左/右的角度 bin,并设一个阈值判定运动方向。

这条作业路径恰好把本课三个概念串了起来:视频读帧、密集光流、极坐标 + 直方图统计。

小结与学习路径

课程文档的结论是:有些相对复杂的任务(运动检测、指尖检测等)可以完全用经典计算机视觉技术解决,不需要神经网络。因此掌握这些基础技术以及 OpenCV 的能力边界非常有价值——它既能独立解决问题,也能在需要神经网络时做高质量的前端预处理(如盲文切分案例)。

在本课程的计算机视觉章节中,本课时承上启下:接下来会进入 卷积神经网络(07-ConvNets),学习真正用网络解决分类与检测问题的方法。想进一步加深光流理解,可以运行 OpenCV.ipynb 中“Extract Motion using Optical Flow”一节的完整代码,配合 data/motionvideo.mp4 观察光流 HSV 可视化效果,再动手完成 MovementDetection.ipynb 的方向直方图作业。

相关仓库文件索引:

  • 本课讲解文档(英文)
  • OpenCV 演示 notebook
  • 实验室作业说明 与 作业 notebook
  • 配套素材:盲文原图、运动视频
  • 计算机视觉章节总览
  • 教程
  • 人工智能
  • 机器学习
  • 深度学习

【免费下载链接】AI-For-Beginners

12 Weeks, 24 Lessons, AI for All!

项目地址:https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners
点击查看免费下载

相关推荐

上一篇:Homebridge终极指南:轻松将非HomeKit设备接入Apple智能家居生态
下一篇:Talos Linux自动化运维终极指南:Ansible Playbook开发实战

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 1:41:55

AI编程第四天:Claude Code本地部署与Landing page实战指南

1. 从零上手&#xff1a;AI编程第四天到底在折腾什么很多人学AI编程&#xff0c;前三天都在跟聊天窗口较劲——问它写个函数、改个bug、解释一段报错。到了第四天&#xff0c;你会发现光在网页里复制粘贴已经不够用了&#xff0c;真正想让它帮你干活&#xff0c;得把它请进你的…

作者头像 李华
网站建设 2026/10/9 1:41:32

养成记录好习惯(1)——nfs离线包,适用于Ubuntu22.4(amd64架构)

今天要在公司内网机器上连接nfs&#xff0c;在网上找了很多的离线包都会出现依赖相关问题&#xff0c;今天更新一下nfs的离线包https://gitee.com/yzw139831/nfs_server。 操作步骤&#xff1a; 1.将nfs_server.zip下载之后拷贝至离线机器&#xff0c;并解压。 2.上传至Ubun…

作者头像 李华
网站建设 2026/10/9 1:40:06

dinero.js 中 halfAwayFromZero:远离零的四舍五入模式全解析

金融科技 【免费下载链接】dinero.js Create, calculate, and format money in JavaScript and TypeScript 项目地址&#xff1a; https://gitcode.com/gh_mirrors/di/dinero.js 点击查看 免费下载 导读 本文聚焦 dinero.js 中提供的一种关键舍入模式 —— halfAwayFromZero&…

作者头像 李华