计算机视觉入门实战:图像处理与 CNN 图像分类

从零实战计算机视觉:图像数据表示、OpenCV 预处理(缩放/归一化/增强)、CNN 图像分类(PyTorch CIFAR-10)、经典网络(ResNet)、目标检测/分割概览、迁移学习与部署。

引言

计算机视觉(CV)是深度学习落地最广的方向——图像分类、目标检测、分割、OCR。核心管线清晰:图像变成张量 → CNN 提特征 → 任务头输出。本文从图像数据的底层表示讲起,用 OpenCV 讲预处理与增强,再完整跑通 PyTorch 的 CIFAR-10 图像分类,最后概览目标检测/分割和迁移学习,让你对 CV 实战有全景认知。

前置:/ml-deep-learning-advanced/(CNN 结构)、/ml-python-environment-setup/(NumPy/环境)。


目录


1. 图像数据的表示

图像 = 三维张量:H × W × C(高、宽、通道)。

灰度图: 224 × 224 × 1(0-255 亮度)
彩色图: 224 × 224 × 3(RGB 三通道)

NumPy/PyTorch 表示:

import numpy as np
from PIL import Image

# PIL 打开 → numpy 数组 (H, W, C)
img = np.array(Image.open('cat.jpg'))   # (224, 224, 3), 0-255
print(img.shape, img.dtype)

# 归一化到 [0,1](模型输入习惯)
img_norm = img / 255.0

# 通道顺序:PIL/OpenCV 是 HWC,PyTorch 要 CHW
img_chw = img.transpose(2, 0, 1)   # (3, 224, 224)
维度含义注意
H/W像素行列需统一尺寸
C通道数RGB=3/灰度=1
dtypeuint8(0-255)转 float 归一化

关键认知:模型不吃「图像」,吃「数字张量」——加载、缩放、归一化、转 CHW 是固定四步。


2. OpenCV 图像预处理

OpenCV 常用预处理:

import cv2

# 读取与缩放
img = cv2.imread('cat.jpg')              # BGR 顺序!(与 RGB 相反)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)   # 转 RGB
img = cv2.resize(img, (224, 224))        # 统一尺寸

# 灰度/二值化(传统 CV)
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 模糊/边缘
blur = cv2.GaussianBlur(img, (5, 5), 0)
edges = cv2.Canny(gray, 100, 200)

# 仿射变换/裁剪
M = cv2.getRotationMatrix2D((112, 112), 30, 1.0)   # 旋转 30°
rot = cv2.warpAffine(img, M, (224, 224))
预处理用途
resize统一模型输入尺寸
cvtColorBGR↔RGB、灰度
normalize均值为 0、方差为 1
去噪GaussianBlur
边缘检测Canny(传统特征)
透视校正文档扫描

BGR 陷阱:OpenCV 读图默认 BGR,送 PyTorch 前必须转 RGB,否则颜色全错。


3. 数据增强:让模型见多识广

增强 = 用变换制造「更多样本」,专治数据不足与过拟合:

from torchvision import transforms

train_tf = transforms.Compose([
    transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),  # 随机裁剪缩放
    transforms.RandomHorizontalFlip(p=0.5),               # 随机翻转
    transforms.RandomRotation(10),                        # 随机旋转
    transforms.ColorJitter(brightness=0.2, contrast=0.2), # 颜色抖动
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406],           # ImageNet 均值方差
                         [0.229, 0.224, 0.225]),
])

# 验证/测试:只做标准化,不做增强
val_tf = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406],
                         [0.229, 0.224, 0.225]),
])

增强原则:

原则说明
训练集增强、测试集不增测试要「公平」
用真实变换翻转适合自然场景,不适合数字识别
保持标签不变裁剪别裁掉主体
增强强度适度过强反而学不到

记忆:增强 = 免费的更多数据——同预算下常能提 2-5 个点准确率。


4. 经典 CNN 网络演进

看几个里程碑网络(理解设计思路):

网络年份关键创新
AlexNet2012深度 CNN + ReLU + Dropout
VGG2014小核堆叠(3×3)
ResNet2015残差连接(解决梯度消失)
Inception2014多尺度并行卷积
EfficientNet2019缩放法则(深/宽/分辨率)

ResNet 残差连接(最重要的思想):

普通块:  y = F(x)              # 深层难训练
残差块:  y = F(x) + x          # 学"残差",退化网络保底 = 恒等映射
import torch.nn as nn

class BasicBlock(nn.Module):
    def __init__(self, in_c, out_c):
        super().__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(in_c, out_c, 3, padding=1),
            nn.BatchNorm2d(out_c), nn.ReLU(),
            nn.Conv2d(out_c, out_c, 3, padding=1),
            nn.BatchNorm2d(out_c),
        )
        self.shortcut = nn.Identity() if in_c == out_c else \
            nn.Conv2d(in_c, out_c, 1)
        self.relu = nn.ReLU()

    def forward(self, x):
        return self.relu(self.conv(x) + self.shortcut(x))   # 残差相加

残差让网络「加深不退化」——ResNet 至今仍是提取特征的主干标准。


5. PyTorch 实战:CIFAR-10 图像分类

完整流程(数据 → 模型 → 训练 → 评估):

import torch, torch.nn as nn, torchvision
from torchvision import datasets, transforms
import torchvision.models as models

# 1. 数据(增强 + 归一化)
tf = transforms.Compose([
    transforms.RandomHorizontalFlip(),
    transforms.RandomCrop(32, padding=4),
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465),
                         (0.2470, 0.2435, 0.2616)),
])
trainset = datasets.CIFAR10(root='./data', train=True, download=True, transform=tf)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

# 2. 模型(用 torchvision 现成 ResNet18)
model = models.resnet18(num_classes=10)

# 3. 训练
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-4)
criterion = nn.CrossEntropyLoss()

for epoch in range(20):
    model.train()
    for images, labels in trainloader:
        optimizer.zero_grad()
        loss = criterion(model(images), labels)
        loss.backward()
        optimizer.step()
    print(f'epoch {epoch}: loss={loss.item():.3f}')

# 4. 评估
model.eval()
correct = total = 0
with torch.no_grad():
    for images, labels in torch.utils.data.DataLoader(
            datasets.CIFAR10(root='./data', train=False, download=True,
                             transform=transforms.ToTensor()), batch_size=64):
        correct += (model(images).argmax(1) == labels).sum().item()
        total += labels.size(0)
print(f'准确率: {correct/total:.4f}')

CIFAR-10:6 万张 32×32 彩色图、10 类(飞机/汽车/鸟/猫/狗/蛙/马/船/卡车)。ResNet18 从头训约 90%+。


6. 迁移学习与 ResNet 微调

数据少时直接用预训练 ResNet(ImageNet 1000 类权重):

import torchvision.models as models

# 加载 ImageNet 预训练
model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1)

# 冻结主干
for param in model.parameters():
    param.requires_grad = False

# 替换分类头 → 你的 N 类
model.fc = nn.Linear(model.fc.in_features, num_classes)

# 只训分类头(数据 <1k 时);数据多再解冻微调深层
optimizer = torch.optim.AdamW(model.fc.parameters(), lr=1e-3)
数据量冻结策略
<1k冻结全部主干,只训分类头
1k-10k解冻后几层微调
>10k全量微调 + 小学习率

记忆:CV 项目 90% 是「预训练 + 微调」——别从随机权重从头训。


7. 目标检测概览

分类知道「是什么」,检测还要「在哪里」——输出边界框 + 类别:

家族代表思想
两阶段Faster R-CNN先生成候选框再分类
单阶段YOLO / SSD直接回归框+类别(快)
TransformerDETR注意力做检测(端到端)

YOLO 一句话:把图像分网格,每格直接预测「框 + 类别概率」,一次前向出结果——快,适合实时。

# 用 Ultralytics YOLO(极简)
from ultralytics import YOLO

model = YOLO('yolov8n.pt')          # 预训练
results = model('test.jpg')         # 检测
for r in results:
    print(r.boxes.xyxy, r.boxes.cls, r.boxes.conf)

何时用:行人检测、缺陷检测、自动驾驶感知、票据要素定位。


8. 图像分割与 OCR

分割(Segmentation):像素级分类——区分「哪些像素是猫」:

类型输出代表
语义分割每像素类别U-Net、DeepLab
实例分割每个实例独立Mask R-CNN
全景分割语义+实例Panoptic

U-Net(医学图像分割标配):编码-解码 + 跳跃连接(保空间细节)。

OCR(文字识别):

# 经典 OCR 工具链
import pytesseract, cv2
from PIL import Image

img = Image.open('receipt.jpg')
text = pytesseract.image_to_string(img, lang='chi_sim+eng')
print(text)

# 工业级:PaddleOCR(中文强、版式好)
from paddleocr import PaddleOCR
ocr = PaddleOCR(use_angle_cls=True, lang='ch')
result = ocr.ocr('scan.jpg', cls=True)

OCR 流程:检测文本区域 → 识别文字 → 结构化(键值对/表格)。


9. 部署与常见问题

部署要点(完整见 /ml-model-deployment/):

# 导出 ONNX(跨框架部署)
import torch

dummy = torch.randn(1, 3, 224, 224)
torch.onnx.export(model, dummy, 'model.onnx',
                  input_names=['input'], output_names=['output'])

CV 常见问题:

问题解法
过拟合(数据少)增强 + 迁移学习
类别不平衡加权损失/采样
训练不稳定BN + 小学习率
颜色全错检查 BGR→RGB
尺寸不一致统一 resize/crop
小目标检测差多尺度/更高分辨率
推理慢模型量化/剪枝/ONNX

记忆:CV 管线 = 读图转张量 → 预处理增强 → CNN 特征 → 任务头 → 部署;数据少迁移、训练稳靠 BN、部署用 ONNX。


10. 速查表

需求做法
图像分类CNN / ResNet(迁移)
数据少增强 + 预训练微调
目标检测YOLO(快)或 Faster R-CNN(准)
像素级分割U-Net / Mask R-CNN
文字识别PaddleOCR / Tesseract
预处理resize + normalize + RGB
训练稳BatchNorm + AdamW
部署ONNX / TensorRT
实时检测YOLO + 量化

一句话记忆:图像是三维张量,OpenCV 管预处理、增强造数据;分类用 ResNet 迁移、检测用 YOLO、分割用 U-Net、OCR 用 PaddleOCR;部署转 ONNX——CV 一套管线走天下。


延伸阅读

  • /ml-deep-learning-advanced/ — CNN 结构与训练技巧
  • /ml-model-deployment/ — 模型部署与 MLOps
  • /ml-neural-networks-basics/ — 张量与自动求导
  • [[ai-ml]] — CV 算法深度专题
  • [[hpc]] — 推理加速(GPU)

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ml」更多文章

  1. 集成学习实战:Bagging、随机森林、梯度提升与 Stacking
  2. 迁移学习实战:预训练模型、特征提取与微调全流程
  3. 深度学习进阶:CNN、RNN、正则化与训练调参实战