简介这份PPT面向计算机、人工智能方向的初学者与进阶学习者系统梳理图像处理与计算机视觉的基础概念、发展脉络与技术构成帮助读者建立从二维图像分析到三维场景理解的完整知识框架。资源包内含1个PPT文件约1.55MB以图文并茂的幻灯片形式呈现便于课堂讲解与自学翻阅。内容涵盖图像与数字图像的定义、像素与灰度概念、低中高级处理的划分以及图像处理与计算机视觉、机器视觉之间的区别与联系同时按时间线回顾从20世纪20年代图像编码重构、60年代航天图像处理到70年代遥感与医学图像、80年代末多媒体技术兴起的演进过程。此外还介绍了图像采集传感器、高速采集系统及数码相机、扫描仪等输入输出设备的系统构成并结合工业机器视觉的缺陷检测、尺寸测量等应用场景展开说明。目前已有89人学习适合作为课程复习、技术入门与知识体系搭建的参考材料。1. 图像处理与计算机视觉技术综述从一份 PPT 大纲到能跑通的最小闭环很多人第一次接触图像处理与计算机视觉技术综述是在一份 PPT 里看到满屏的卷积核、特征金字塔和检测框翻完觉得什么都讲了合上又什么都做不出来。这份 PPT 真正的价值不在页面本身而在于它能不能被拆成一条可执行的路径先跑通读图、灰度、滤波、边缘再进到特征提取、分类、检测最后落到一个能复现指标的小项目。它适合两类人刚转方向、需要一张完整地图的新手以及做过单点任务、但说不清各模块怎么串起来的熟手。下面我按自己带人做项目的顺序把这份综述拆成能照着敲、能验证、能踩坑的版本。2. 图像处理与计算机视觉技术综述的骨架从像素到语义的四层拆解一份能落地的综述不该按算法名字罗列而该按数据形态的演进分层。像素层处理的是数值矩阵特征层处理的是可描述的局部结构语义层处理的是类别与位置工程层处理的是速度、内存和部署。这四层对应不同的库、不同的评价指标也对应不同的翻车方式。把层次分清后面选型就不会拿检测模型去做去噪也不会拿传统算子去硬扛语义分割。2.1 像素层读图、色彩空间与滤波的选型理由像素层是所有后续步骤的地基。常见做法是先用 OpenCV 或 Pillow 读入图像统一转成 numpy 数组再决定是否转灰度。转灰度不是习惯动作而是因为多数传统算子Sobel、Canny、Harris定义在单通道上三通道直接算会得到三份互相干扰的梯度。色彩空间的选择也有讲究HSV 适合做颜色阈值分割因为色调和亮度解耦Lab 适合做色差计算因为欧氏距离更接近人眼感知。滤波的选型取决于噪声类型。高斯噪声用高斯滤波椒盐噪声用中值滤波而保边去噪用双边滤波或导向滤波。很多人一上来就用均值滤波结果边缘被糊掉后续 Canny 检出的轮廓全是断的。我一般会先看噪声分布再选核核大小从 3 起步超过 7 就要警惕细节丢失。import cv2 import numpy as np # 读入图像统一转成 BGR 三通道避免后续通道数不一致 img cv2.imread(input.jpg, cv2.IMREAD_COLOR) if img is None: raise FileNotFoundError(图像路径错误或格式不支持) # 转灰度传统算子需要单通道输入 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯滤波ksize 必须是奇数sigmaX 设为 0 表示由 ksize 自动推导 blur cv2.GaussianBlur(gray, (5, 5), 0) # 中值滤波对椒盐噪声更稳ksize 同样取奇数 median cv2.medianBlur(gray, 5) # 保存中间结果方便肉眼比对滤波效果 cv2.imwrite(gray.jpg, gray) cv2.imwrite(blur.jpg, blur) cv2.imwrite(median.jpg, median)这段代码的关键参数是ksize和sigmaX。ksize越大平滑越强但边缘保留越差sigmaX为 0 时 OpenCV 按0.3*((ksize-1)*0.5-1)0.8推导一般不用手改。中值滤波的ksize超过 7 后计算量明显上升实时场景要谨慎。保存中间结果这一步别省后面调参时能快速定位是哪一层出的问题。2.2 特征层边缘、角点与局部描述子的适用边界特征层解决的是“图像里有什么结构”。边缘检测用 Canny角点检测用 Harris 或 Shi-Tomasi局部描述子用 SIFT、ORB 或 SURF。Canny 的双阈值是玄学重灾区高阈值太高会丢弱边缘太低会引入噪声。我一般按梯度幅值的 70% 和 30% 分位先设一版再根据检出图微调。ORB 适合实时匹配因为它快且免费SIFT 精度高但计算重专利已过期现在可以放心用。这里有个常见误用拿 SIFT 去做实时视频拼接。SIFT 单帧特征提取在普通 CPU 上可能几十毫秒加上匹配和 RANSAC帧率直接掉到个位数。实时场景优先 ORB精度不够再考虑 SIFT 加降采样。# Canny 边缘检测双阈值按梯度分位数自适应 grad_x cv2.Sobel(blur, cv2.CV_32F, 1, 0, ksize3) grad_y cv2.Sobel(blur, cv2.CV_32F, 0, 1, ksize3) mag np.sqrt(grad_x**2 grad_y**2) high np.percentile(mag, 70) low high * 0.4 edges cv2.Canny(blur, low, high) # ORB 特征nfeatures 控制上限scaleFactor 控制金字塔层间缩放 orb cv2.ORB_create(nfeatures1000, scaleFactor1.2, nlevels8) keypoints, descriptors orb.detectAndCompute(gray, None) # 画出特征点检查分布是否集中在纹理丰富区域 vis cv2.drawKeypoints(img, keypoints, None, color(0, 255, 0)) cv2.imwrite(orb_keypoints.jpg, vis)nfeatures设太大不会更准只会更慢因为弱特征点匹配时容易被误配。scaleFactor取 1.2 是精度和速度的折中取 1.1 更细但金字塔层数变多。nlevels决定能覆盖多大的尺度变化8 层大约覆盖 2.1 倍尺度。画特征点这步是排查“为什么匹配不上”的第一现场如果点全挤在角落说明纹理分布不均得换区域或换描述子。2.3 语义层分类、检测与分割的任务边界语义层开始出现“类别”和“位置”。分类只回答是什么检测回答是什么和在哪分割回答每个像素属于什么。选型时先问业务需不需要位置只需要判断缺陷有无分类就够需要框出缺陷区域上检测需要精确到像素边界才上分割。很多项目一上来就上分割标注成本翻十倍指标还未必比检测加后处理好。检测里又分两阶段和单阶段。两阶段如 Faster R-CNN 系列精度高但慢单阶段如 YOLO 系列、SSD快但小目标弱。分割里分语义分割和实例分割前者不区分同类个体后者区分。选型时把“同类个体要不要分开数”问清楚能省掉大量返工。2.4 工程层延迟、内存与部署形态的取舍工程层决定方案能不能上线。延迟分预处理、推理、后处理三段很多人只盯推理结果预处理里的 resize 和归一化占了大头。内存分模型权重和中间激活移动端要关注峰值内存。部署形态分服务端 GPU、边缘 NPU、浏览器 WASM不同形态对算子支持差异很大训练时用的自定义算子部署时可能直接不支持。我一般会在训练前就确认部署形态再反推模型结构。比如边缘 NPU 对动态 shape 支持差就固定输入分辨率浏览器 WASM 对 int8 量化支持好就提前规划量化感知训练。这一步提前做比训练完再改结构省事得多。3. 用 OpenCV 和 PyTorch 搭一条可复现的基线从读图到指标输出骨架清楚后下一步是搭一条能跑通、能出指标的基线。基线不追求 SOTA追求每一步可解释、可替换。我习惯用 OpenCV 做预处理和可视化用 PyTorch 做模型和训练中间用 numpy 衔接。这样调预处理时不用动模型换模型时不用动预处理。3.1 环境与依赖版本锁定与最小依赖集环境翻车多数来自版本漂移。OpenCV 4.x 和 3.x 的 API 有差异PyTorch 1.x 和 2.x 的torchvision接口也有变化。我一般用 conda 建独立环境把版本写进requirements.txt避免“在我机器上能跑”。conda create -n cv-baseline python3.10 -y conda activate cv-baseline pip install opencv-python4.9.0.80 pip install torch2.2.0 torchvision0.17.0 --index-url https://download.pytorch.org/whl/cpu pip install numpy1.26.4 matplotlib3.8.3CPU 版本够跑基线有 GPU 再把 index-url 换成对应 CUDA 版本。opencv-python和opencv-contrib-python不要同时装会冲突。numpy锁 1.26 是因为部分旧版 PyTorch 对 numpy 2.x 兼容不好这是血泪经验。3.2 数据管线自定义 Dataset 与增强的四个必调参数数据管线决定模型看到什么。自定义 Dataset 要保证__getitem__返回的 image 和 label 严格对齐增强时同步变换。常见翻车是只对图像做随机裁剪标签没跟着裁训练时 loss 不降反升。import torch from torch.utils.data import Dataset, DataLoader import cv2 import numpy as np import os class ImageDataset(Dataset): def __init__(self, root, splittrain, img_size224): self.root root self.split split self.img_size img_size # 假设目录结构为 root/split/class_name/xxx.jpg self.samples [] for cls_id, cls_name in enumerate(sorted(os.listdir(os.path.join(root, split)))): cls_dir os.path.join(root, split, cls_name) for fname in os.listdir(cls_dir): self.samples.append((os.path.join(cls_dir, fname), cls_id)) def __len__(self): return len(self.samples) def __getitem__(self, idx): path, label self.samples[idx] img cv2.imread(path, cv2.IMREAD_COLOR) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # resize 到固定尺寸保证 batch 内 shape 一致 img cv2.resize(img, (self.img_size, self.img_size)) # 归一化到 [0,1]再按 ImageNet 均值方差标准化 img img.astype(np.float32) / 255.0 mean np.array([0.485, 0.456, 0.406], dtypenp.float32) std np.array([0.229, 0.224, 0.225], dtypenp.float32) img (img - mean) / std # 转成 CHWPyTorch 卷积要求通道在前 img np.transpose(img, (2, 0, 1)) return torch.from_numpy(img), label loader DataLoader(ImageDataset(data, train), batch_size32, shuffleTrue, num_workers4)四个必调参数img_size决定输入分辨率和计算量224 是分类常用值batch_size受显存限制32 是起点num_workers影响数据加载速度设成 CPU 核数的 70% 左右shuffle训练集必须开验证集必须关。归一化的均值和方差要和预训练模型一致否则微调时收敛慢。3.3 模型与训练迁移学习的最小改动基线用预训练 ResNet 做迁移学习只改最后一层全连接。这样小数据集也能收敛不用从零训。import torch.nn as nn from torchvision import models def build_model(num_classes10): model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) # 冻结主干只训分类头小数据集防过拟合 for param in model.parameters(): param.requires_grad False in_features model.fc.in_features model.fc nn.Linear(in_features, num_classes) return model model build_model(num_classes10) criterion nn.CrossEntropyLoss() # 只优化分类头参数学习率可以设大一点 optimizer torch.optim.Adam(model.fc.parameters(), lr1e-3) for epoch in range(10): model.train() for imgs, labels in loader: optimizer.zero_grad() outputs model(imgs) loss criterion(outputs, labels) loss.backward() optimizer.step() print(fepoch {epoch}, loss {loss.item():.4f})冻结主干是防过拟合的关键数据量上来后再解冻部分层做微调。学习率 1e-3 只适用于分类头解冻主干后要降到 1e-4 量级。CrossEntropyLoss内部带 softmax模型输出不要再加 softmax否则梯度会出问题。3.4 指标与可视化混淆矩阵和 PR 曲线的读法训练完不能只看 loss要看混淆矩阵和 PR 曲线。混淆矩阵能看出哪两类互相误判PR 曲线能看出阈值变化对精度和召回的影响。from sklearn.metrics import confusion_matrix, precision_recall_curve import matplotlib.pyplot as plt import seaborn as sns model.eval() all_preds, all_labels, all_probs [], [], [] with torch.no_grad(): for imgs, labels in loader: outputs model(imgs) probs torch.softmax(outputs, dim1) preds torch.argmax(probs, dim1) all_preds.extend(preds.numpy()) all_labels.extend(labels.numpy()) all_probs.extend(probs.numpy()) cm confusion_matrix(all_labels, all_preds) sns.heatmap(cm, annotTrue, fmtd, cmapBlues) plt.savefig(confusion_matrix.png) # 二分类 PR 曲线多分类需按类展开 prec, rec, _ precision_recall_curve(all_labels, [p[1] for p in all_probs]) plt.plot(rec, prec) plt.xlabel(recall) plt.ylabel(precision) plt.savefig(pr_curve.png)混淆矩阵对角线越深越好非对角线出现明显亮块说明这两类特征区分度不够要么加数据要么换更强的 backbone。PR 曲线看曲线下面积面积越大越好如果曲线在某个召回区间骤降说明该区间阈值下误检激增业务上要避开这个阈值。4. 避坑与排查图像处理与计算机视觉项目里最常见的五类翻车这一章按“现象 → 原因 → 解决”写都是我在实际项目里反复遇到的。4.1 现象训练 loss 震荡不降验证集准确率随机跳原因通常是学习率过大或 batch 内样本顺序有强相关。数据管线里shuffle没开或者数据集本身按类别排序模型会学到顺序偏差。解决确认shuffleTrue学习率降一个量级加 warmup。如果还震荡检查归一化均值方差是否和预训练模型一致不一致会让浅层梯度异常。4.2 现象Canny 边缘断裂严重轮廓连不起来原因是双阈值设得过高弱边缘被抑制。解决按梯度幅值分位数自适应设阈值先取 70% 和 30% 分位再根据检出图微调。如果噪声多先做中值滤波再 Canny不要用高斯滤波高斯会把弱边缘一起抹掉。4.3 现象ORB 匹配点大量误配RANSAC 后内点很少原因是nfeatures设太大弱特征点被纳入匹配。解决降到 500 到 1000加交叉验证过滤crossCheckTrue再用比率测试Lowes ratio筛一遍。如果场景纹理重复ORB 本身就不适合换 SIFT 或加语义约束。4.4 现象模型在本地跑得好部署到边缘设备精度掉一截原因是预处理不一致。训练时用 OpenCV 读图是 BGR部署时用其他库读图是 RGB通道顺序反了。解决统一读图库和通道顺序把预处理写成独立函数训练和部署共用同一份代码。另一个常见原因是 resize 插值方式不同训练用双线性部署用最近邻小目标上差异明显。4.5 现象显存溢出batch 调小后训练变慢且指标下降原因是 batch 变小后 BatchNorm 统计量不准梯度噪声变大。解决改用梯度累积累积步数等于原 batch 除以现 batch等效放大 batch。或者换 GroupNorm 替代 BatchNorm小 batch 下更稳。显存溢出也可能是中间激活没释放检查有没有在循环里保留计算图。5. 从综述到落地把 PPT 变成可复用模板的三个技巧综述类 PPT 最大的问题是信息密度高但不可执行。我的做法是把它拆成三个可复用模板预处理模板、模型模板、评估模板。预处理模板固定读图、色彩空间、归一化三步换任务只改参数模型模板固定 backbone、head、loss 三段换任务只改 head 和 loss评估模板固定混淆矩阵、PR 曲线、延迟测量三块换任务只改类别数。延迟测量有个容易忽略的点要测端到端不是只测推理。端到端包括读图、预处理、推理、后处理。我一般用time.perf_counter()在循环里打点跑 100 次取中位数避免首次加载的冷启动干扰。import time def benchmark(model, img_path, runs100): img cv2.imread(img_path) img cv2.resize(img, (224, 224)) img img.astype(np.float32) / 255.0 img np.transpose(img, (2, 0, 1)) tensor torch.from_numpy(img).unsqueeze(0) model.eval() times [] with torch.no_grad(): for _ in range(runs): start time.perf_counter() _ model(tensor) times.append(time.perf_counter() - start) times.sort() return times[len(times) // 2] # 中位数抗离群值 latency benchmark(model, input.jpg) print(fmedian latency: {latency*1000:.2f} ms)中位数比均值稳因为首次推理和系统调度会产生离群值。如果中位数和均值差很多说明有偶发卡顿要查内存交换或线程竞争。最后一个技巧是版本化。预处理参数、模型权重、评估脚本都要打版本号写进配置文件和日志。我吃过亏调了一版预处理参数指标涨了两个点结果忘了记后面复现不出来只能重跑。现在习惯把配置存成 yaml和权重放同一目录跑之前先打印一遍。这套流程不复杂难的是每步都留下可复现的记录。希望帮到你。本文还有配套的精品资源点击获取