计算机视觉基础
计算机视觉基础
简介
计算机视觉(Computer Vision,CV)是让计算机"看懂"图像和视频的技术。从传统的图像处理到深度学习的端到端方案,CV 已广泛应用于人脸识别、自动驾驶、医学影像、工业检测等领域。本篇介绍 CV 的基础概念和常见任务。
计算机视觉的发展历程可以分为几个阶段:早期(1960s-1980s)以边缘检测、特征提取等手工方法为主;中期(1990s-2010s)以 SIFT、HOG 等手工特征 + 传统分类器(SVM)为主流;深度学习时代(2012 至今)以 CNN、Transformer 等端到端学习方法为主。2012 年 AlexNet 在 ImageNet 上的突破标志着深度学习在 CV 领域的全面崛起,此后 CNN 架构不断演进(VGG、ResNet、EfficientNet),2020 年 Vision Transformer (ViT) 的出现将 Transformer 引入了视觉领域。
从信息处理的角度看,计算机视觉的输入是数字图像(像素矩阵),输出可以是类别标签、边界框、分割掩码、关键点坐标等。核心挑战在于:从高维的像素信号中提取有语义意义的特征,并建立特征与任务目标之间的映射关系。
特点
CV 任务层次
CV 任务按复杂度递增可以分为:
- 图像分类:整张图属于什么类别?(最简单)
- 目标检测:图中有哪些物体?它们在哪里?
- 语义分割:每个像素属于什么类别?
- 实例分割:哪些像素属于同一个物体实例?
- 全景分割:同时做语义分割和实例分割
- 视觉问答:根据图像回答自然语言问题
- 图像生成:根据文本或条件生成图像
图像基础操作
OpenCV 基础
import cv2
import numpy as np
# 读取和显示图像
img = cv2.imread('image.jpg')
print(f"图像形状: {img.shape}") # (H, W, C) BGR格式
# 基本操作
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度化
resized = cv2.resize(img, (224, 224)) # 缩放
blurred = cv2.GaussianBlur(gray, (5, 5), 0) # 高斯模糊
edges = cv2.Canny(gray, 100, 200) # 边缘检测
# 图像保存
cv2.imwrite('output_gray.jpg', gray)
# 绘制标注
cv2.rectangle(img, (100, 100), (300, 300), (0, 255, 0), 2)
cv2.putText(img, 'Object', (100, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)图像数据基础
import numpy as np
def explain_image_fundamentals():
"""数字图像的基本概念
1. 图像表示:
- 灰度图: (H, W) 二维数组,每个值 0-255
- 彩色图: (H, W, C) 三维数组,C=3 (RGB/BGR)
- 像素值: uint8 (0-255) 或 float32 (0.0-1.0)
2. 颜色空间:
- RGB/BGR: 红绿蓝三通道(CV 默认 BGR)
- HSV: 色相、饱和度、明度(颜色检测更直观)
- LAB: 亮度 + 颜色通道(感知均匀)
- 灰度: 单通道(边缘检测、二值化)
3. 图像属性:
- 分辨率: 宽 x 高(如 1920x1080)
- 通道数: 灰度=1, RGB=3, RGBA=4
- 位深: 8bit (0-255), 16bit, float32
- 色彩空间: sRGB, Adobe RGB, RAW
"""
# 创建不同类型的图像
gray_img = np.random.randint(0, 256, (100, 100), dtype=np.uint8)
color_img = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
float_img = np.random.rand(100, 100, 3).astype(np.float32)
print(f"灰度图: shape={gray_img.shape}, dtype={gray_img.dtype}")
print(f"彩色图: shape={color_img.shape}, dtype={color_img.dtype}")
print(f"浮点图: shape={float_img.shape}, dtype={float_img.dtype}")
print(f" 值域: [{float_img.min():.2f}, {float_img.max():.2f}]")
explain_image_fundamentals()图像预处理管线
import torch
from torchvision import transforms
from PIL import Image
def build_preprocessing_pipeline():
"""构建图像预处理管线
训练时和推理时的预处理通常不同:
- 训练时: 包含数据增强(随机裁剪、翻转、颜色抖动等)
- 推理时: 只有确定性变换(缩放、归一化)
预训练模型的归一化参数必须与训练时一致!
ImageNet 的标准归一化:
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
"""
# 训练预处理
train_transform = transforms.Compose([
transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),
transforms.RandomHorizontalFlip(p=0.5),
transforms.RandomRotation(degrees=15),
transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
# 推理预处理
val_transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
print("训练预处理: 随机裁剪 + 翻转 + 旋转 + 颜色抖动 + 归一化")
print("推理预处理: 缩放 + 中心裁剪 + 归一化")
build_preprocessing_pipeline()图像增强
# 数据增强
import albumentations as A
transform = A.Compose([
A.RandomCrop(width=224, height=224),
A.HorizontalFlip(p=0.5),
A.RandomBrightnessContrast(p=0.3),
A.Rotate(limit=15, p=0.5),
A.GaussNoise(p=0.2),
])
# augmented = transform(image=img)['image']高级数据增强策略
import numpy as np
def advanced_augmentation_strategies():
"""高级数据增强策略
1. MixUp: 将两张图片和标签进行加权混合
img = lambda * img1 + (1-lambda) * img2
label = lambda * label1 + (1-lambda) * label2
2. CutMix: 将一张图片的随机区域替换为另一张图片
结合两个标签按面积比例混合
3. RandAugment: 自动搜索最优增强策略组合
N 个操作 + M 个强度等级
4. AutoAugment: 从搜索空间中学习增强策略
ImageNet 上的最佳策略组合
5. Copy-Paste: 实例级别的数据增强
将一个物体的实例复制粘贴到另一张图中
"""
# MixUp 示例
def mixup_data(x, y, alpha=0.2):
if alpha > 0:
lam = np.random.beta(alpha, alpha)
else:
lam = 1.0
batch_size = x.size(0)
index = torch.randperm(batch_size)
mixed_x = lam * x + (1 - lam) * x[index]
y_a, y_b = y, y[index]
return mixed_x, y_a, y_b, lam
print("高级增强策略:")
print(" MixUp: 两张图加权混合,通常提升 1-2% Top-1")
print(" CutMix: 区域替换,对小目标检测有效")
print(" RandAugment: 自动搜索策略,简单高效")
print(" Copy-Paste: 实例级增强,适合检测/分割")
advanced_augmentation_strategies()图像分类
使用预训练模型
import torch
from torchvision import models, transforms
from PIL import Image
# 加载预训练 ResNet
model = models.resnet50(pretrained=True)
model.eval()
# 图像预处理
preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
# 推理
image = Image.open('cat.jpg')
input_tensor = preprocess(image).unsqueeze(0)
with torch.no_grad():
output = model(input_tensor)
_, predicted = output.max(1)
print(f"预测类别: {predicted.item()}")
# 加载 ImageNet 类别标签
with open('imagenet_classes.txt') as f:
classes = [line.strip() for line in f.readlines()]
print(f"类别: {classes[predicted.item()]}")迁移学习实战
import torch
import torch.nn as nn
from torchvision import models, datasets, transforms
from torch.utils.data import DataLoader
def transfer_learning_example():
"""迁移学习的三种模式
1. 特征提取:冻结预训练模型,只训练新的分类头
- 适合数据量少(< 1000 张)
- 训练快,效果通常不错
2. 微调:解冻部分层进行微调
- 适合数据量中等(1000 - 10000 张)
- 比特征提取效果更好
3. 全量微调:所有层都参与训练
- 适合数据量大(> 10000 张)
- 需要较小的学习率
微调的关键技巧:
- 使用差异学习率:底层用小 lr,顶层用大 lr
- 使用余弦退火学习率调度器
- 使用混合精度训练加速
"""
# 加载预训练模型并修改分类头
model = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
# 冻结所有参数
for param in model.parameters():
param.requires_grad = False
# 替换分类头
num_classes = 10 # 自定义类别数
model.fc = nn.Linear(model.fc.in_features, num_classes)
# 只有新分类头需要训练
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
total_params = sum(p.numel() for p in model.parameters())
print(f"可训练参数: {trainable_params:,} / 总参数: {total_params:,}")
print(f"冻结比例: {1 - trainable_params/total_params:.2%}")
transfer_learning_example()目标检测
YOLO 目标检测
# 使用 ultralytics YOLOv8
from ultralytics import YOLO
# 加载预训练模型
model = YOLO('yolov8n.pt')
# 检测
results = model('street.jpg', conf=0.5)
# 解析结果
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
confidence = box.conf[0].item()
class_id = box.cls[0].item()
print(f"检测到: class={class_id}, conf={confidence:.2f}, "
f"bbox=[{x1:.0f}, {y1:.0f}, {x2:.0f}, {y2:.0f}]")
# 保存标注后的图像
results[0].save('detected.jpg')自定义训练
# 训练自定义检测器
model = YOLO('yolov8n.pt')
# 训练(需要准备 YOLO 格式数据集)
# results = model.train(
# data='dataset.yaml', # 数据集配置
# epochs=50,
# imgsz=640,
# batch=16,
# device='0', # GPU
# project='runs/detect',
# name='custom_model'
# )目标检测评估指标
def detection_metrics_explained():
"""目标检测评估指标
1. IoU (Intersection over Union): 预测框与真实框的交并比
IoU > 0.5 通常认为检测正确
IoU > 0.7 为严格匹配
2. Precision (精确率): TP / (TP + FP)
预测为正的中有多少是正确的
3. Recall (召回率): TP / (TP + FN)
真实为正的有多少被检测到了
4. AP (Average Precision): Precision-Recall 曲线下面积
每个类别的 AP 取不同 IoU 阈值的平均
5. mAP (mean AP): 所有类别 AP 的平均值
mAP@0.5: IoU 阈值为 0.5 的 mAP
mAP@0.5:0.95: IoU 从 0.5 到 0.95 的 mAP 平均(COCO 标准)
6. FPS (Frames Per Second): 每秒处理帧数
衡量推理速度
"""
print("目标检测核心指标:")
print(" mAP@0.5: COCO 标准,IoU>0.5 的平均精度")
print(" mAP@0.5:0.95: COCO 严格标准")
print(" FPS: 推理速度")
print(" 小目标 mAP: 单独评估小目标的检测性能")
detection_metrics_explained()图像分割
语义分割
# 使用 segmentation_models_pytorch
import segmentation_models_pytorch as smp
# U-Net 分割模型
model = smp.Unet(
encoder_name='resnet34',
encoder_weights='imagenet',
in_channels=3,
classes=4 # 分割类别数
)
# 推理
# output = model(input_tensor) # 输出 shape: (B, num_classes, H, W)
# mask = output.argmax(dim=1) # 每个像素的类别Vision Transformer 简介
def explain_vit():
"""Vision Transformer (ViT) 简介
ViT 将 Transformer 架构引入计算机视觉:
1. 将图像分割为固定大小的 patch(如 16x16)
2. 每个 patch 线性投影为向量
3. 添加位置编码
4. 输入标准 Transformer Encoder
5. 用 [CLS] token 的输出做分类
ViT 的优势:
- 全局感受野(不像 CNN 需要堆叠才能获得)
- 更少的归纳偏置,适合大规模数据
- 架构统一(NLP 和 CV 共用 Transformer)
ViT 的缺点:
- 需要大量预训练数据(ImageNet-21K 或 JFT)
- 从头训练效果不如 CNN(数据量小时)
- 计算复杂度 O(n^2)(n = patch 数量)
现代 ViT 变体:
- DeiT: 数据高效的 ViT 训练
- Swin Transformer: 窗口注意力,线性复杂度
- ConvNeXt: 借鉴 ViT 设计的纯 CNN
"""
print("Vision Transformer 要点:")
print(" 图像 -> Patch 序列 -> Transformer -> 分类")
print(" 大数据优势明显,小数据不如 CNN")
print(" Swin ViT 通过窗口注意力降低复杂度")
explain_vit()CV 任务对比
| 任务 | 输出 | 典型模型 | 应用场景 |
|---|---|---|---|
| 图像分类 | 类别标签 | ResNet, ViT | 商品分类、医疗诊断 |
| 目标检测 | 边界框+类别 | YOLO, Faster R-CNN | 自动驾驶、安防 |
| 语义分割 | 像素级类别 | U-Net, DeepLab | 医学影像、遥感 |
| 实例分割 | 像素级实例 | Mask R-CNN | 机器人视觉 |
| 姿态估计 | 关键点坐标 | OpenPose, MediaPipe | 运动分析 |
| OCR | 文本内容 | PaddleOCR, TrOCR | 文档识别 |
优点
缺点
总结
计算机视觉核心任务:分类(整体类别)、检测(位置+类别)、分割(像素级分类)。OpenCV 处理基础图像操作(读写、变换、滤波)。图像分类用 ResNet 等预训练模型 + 迁移学习。目标检测用 YOLOv8 快速上手,支持自定义训练。分割用 U-Net/DeepLab。实际项目建议从预训练模型微调开始,而非从零训练。
在实际项目中,数据质量和标注一致性往往比模型选择更重要。一个用 ResNet-50 + 高质量数据微调的模型,可能比用最先进的 ViT + 噪声数据训练的模型效果更好。
关键知识点
- 图像预处理(归一化、缩放)必须与预训练时保持一致。
- 迁移学习是 CV 项目最实用的手段——在 ImageNet 预训练权重上微调。
- YOLO 系列是目标检测的首选,兼顾速度和精度。
- 评估指标(mAP、IoU、FPS)需要根据业务场景选择重点关注。
- 数据增强对 CV 效果提升显著,值得系统设计。
项目落地视角
- 明确任务类型:分类、检测、分割还是其他。
- 评估部署约束:推理速度、输入分辨率、硬件算力。
- 选择合适的预训练模型和微调策略。
- 建立完善的数据标注和质量控制流程。
模型部署考虑
def deployment_considerations():
"""CV 模型部署的关键考虑
1. 推理框架选择:
- ONNX Runtime: 跨平台,通用
- TensorRT: NVIDIA GPU 专用,最快
- OpenVINO: Intel CPU/GPU 专用
- CoreML: Apple 设备专用
- NCNN/TNN: 移动端专用
2. 模型优化:
- 量化: FP32 -> FP16/INT8
- 剪枝: 去除不重要的通道/层
- 蒸馏: 大模型 -> 小模型
- 融合: 合并 BatchNorm 等操作
3. 输入预处理:
- 归一化参数必须与训练一致
- 输入尺寸和颜色空间顺序
- 批处理大小和内存管理
4. 后处理:
- NMS (非极大值抑制): 去除重叠检测框
- 阈值调整: 根据业务场景调整置信度阈值
- 结果格式化: 输出 JSON/XML 等结构化结果
"""
print("部署检查清单:")
print(" [ ] 预处理参数与训练一致")
print(" [ ] 选择合适的推理框架")
print(" [ ] 模型量化降低延迟")
print(" [ ] 后处理逻辑正确(NMS、阈值)")
print(" [ ] 不同光照/角度下验证鲁棒性")
deployment_considerations()常见误区
- 忽略预处理一致性——归一化参数或输入尺寸不一致会导致效果大幅下降。
- 不验证不同光照/角度/分辨率下的鲁棒性。
- 在小数据集上从零训练——迁移学习几乎总是更好的选择。
- 忽略推理速度需求——选择了过大的模型导致无法实时运行。
进阶路线
- 深入学习 CNN 架构演进(ResNet -> EfficientNet -> ConvNeXt)。
- 学习 Vision Transformer 和多模态模型(CLIP、BLIP)。
- 掌握模型部署工具链(ONNX、TensorRT、OpenVINO)。
- 探索自监督学习(DINO、MAE)在 CV 中的应用。
适用场景
- 工业缺陷检测、医学影像分析、自动驾驶感知。
- 安防监控、人脸识别、OCR 文档识别。
- 卫星遥感、农业病虫害检测、零售商品识别。
落地建议
- 先从预训练模型微调开始,验证基线效果。
- 系统设计数据增强策略,模拟真实场景的变化。
- 上线前在不同条件下做充分的鲁棒性测试。
排错清单
- 检查预处理是否与训练一致(归一化、尺寸、颜色空间)。
- 检查模型是否处于 eval 模式(BatchNorm 行为不同)。
- 检查输入数据的类型和值域是否正确。
复盘问题
- 当前任务的精度和速度要求是什么?
- 预训练模型在你的场景中表现如何?
- 部署环境的具体约束是什么?
