计算机视觉(CV)基础任务介绍

1. 计算机视觉概述

计算机视觉(Computer Vision,简称CV)是人工智能的一个重要分支,旨在使计算机能够理解和处理图像和视频数据。通过模仿人类视觉系统的功能,计算机视觉技术使机器能够识别、检测、分割和理解图像中的内容。本节将介绍计算机视觉的发展历程、基本原理以及主要应用领域。

1.1 计算机视觉的发展历程

计算机视觉的发展经历了以下几个重要阶段:

阶段 时间 技术特点 代表技术
早期发展 1960s-1980s 基于规则和几何的方法 边缘检测、特征提取
统计学习时代 1990s-2000s 基于机器学习的方法 SVM、Adaboost、HOG特征
深度学习时代 2010s至今 基于深度神经网络的方法 CNN、R-CNN系列、YOLO系列
大模型时代 2020s至今 基于大规模预训练模型 ViT、CLIP、SAM

1.2 计算机视觉的基本原理

计算机视觉的基本原理可以概括为以下几个步骤:

  1. 图像获取:通过摄像头、传感器等设备获取图像数据
  2. 预处理:对图像进行去噪、增强、缩放等处理
  3. 特征提取:从图像中提取有意义的特征信息
  4. 特征分析:对提取的特征进行分析和理解
  5. 高层理解:基于特征分析结果,实现对图像内容的理解和推理

1.3 计算机视觉的应用领域

计算机视觉技术广泛应用于以下领域:

  1. 安防监控:人脸识别、行为识别、异常检测
  2. 自动驾驶:车道线检测、目标检测、场景理解
  3. 医疗健康:医学影像分析、疾病诊断、手术辅助
  4. 零售电商:商品识别、视觉搜索、库存管理
  5. 工业制造:缺陷检测、质量控制、机器人视觉
  6. 娱乐媒体:图像编辑、视频特效、AR/VR
  7. 农业:作物监测、病虫害识别、产量预测
  8. 交通管理:车辆识别、交通流量分析、违章检测

2. 基础任务详解

2.1 图像分类(Image Classification)

图像分类是计算机视觉中最基础的任务,目标是将输入图像分配到预定义的类别中。

2.1.1 图像分类的基本原理

图像分类的基本流程:

  1. 数据预处理:调整图像大小、归一化、数据增强等
  2. 特征提取:使用卷积神经网络(CNN)等模型提取图像特征
  3. 分类决策:使用全连接层或分类器对特征进行分类
  4. 模型评估:使用准确率、精确率、召回率等指标评估模型性能

2.1.2 经典图像分类模型

模型 年份 特点 代表网络
早期模型 2012年前 手工特征 + 分类器 SIFT + SVM
AlexNet 2012 深度CNN,ReLU激活函数 8层网络
VGG 2014 更深的网络结构,小卷积核 VGG-16, VGG-19
GoogLeNet 2014 inception模块,多尺度特征 Inception v1-v4
ResNet 2015 残差连接,解决梯度消失问题 ResNet-18, ResNet-50, ResNet-152
DenseNet 2017 密集连接,特征重用 DenseNet-121
EfficientNet 2019 模型缩放,计算效率高 EfficientNet-B0到B7
Vision Transformer 2020 基于自注意力机制 ViT

2.1.3 代码示例:图像分类

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np

# 1. 数据预处理
transform = transforms.Compose([
    transforms.Resize((32, 32)),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

# 2. 加载数据集
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

# 3. 定义模型
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)  # 输入通道3,输出通道6,卷积核5x5
        self.pool = nn.MaxPool2d(2, 2)  # 池化核2x2,步长2
        self.conv2 = nn.Conv2d(6, 16, 5)  # 输入通道6,输出通道16,卷积核5x5
        self.fc1 = nn.Linear(16 * 5 * 5, 120)  # 全连接层
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)  # 输出10类

    def forward(self, x):
        x = self.pool(torch.relu(self.conv1(x)))
        x = self.pool(torch.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)  # 展平
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()

# 4. 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

# 5. 训练模型
for epoch in range(2):  # 只训练2个epoch以节省时间
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        inputs, labels = data
        
        # 清零梯度
        optimizer.zero_grad()
        
        # 前向传播
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        
        # 反向传播和优化
        loss.backward()
        optimizer.step()
        
        # 打印统计信息
        running_loss += loss.item()
        if i % 2000 == 1999:  # 每2000个mini-batch打印一次
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')

# 6. 测试模型
dataiter = iter(testloader)
images, labels = dataiter.next()

# 打印真实标签
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

# 预测
outputs = net(images)
_, predicted = torch.max(outputs, 1)

# 打印预测结果
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))

# 计算整体准确率
correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))

# 计算每个类的准确率
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1

for i in range(10):
    print('Accuracy of %5s : %2d %%' % (classes[i], 100 * class_correct[i] / class_total[i]))

2.2 目标检测(Object Detection)

目标检测是计算机视觉中的核心任务,目标是在图像中定位并识别多个目标对象。

2.2.1 目标检测的基本原理

目标检测的基本流程:

  1. 区域生成:生成可能包含目标的候选区域
  2. 特征提取:对每个候选区域提取特征
  3. 分类与回归:对候选区域进行分类,并回归目标的精确边界框
  4. 后处理:使用非极大值抑制(NMS)等方法过滤重叠检测结果

2.2.2 目标检测算法分类

目标检测算法主要分为以下几类:

  1. 两阶段检测器:先生成候选区域,再进行分类和回归

    • R-CNN系列(R-CNN, Fast R-CNN, Faster R-CNN)
    • Mask R-CNN(同时支持实例分割)
  2. 单阶段检测器:直接预测目标的类别和边界框

    • YOLO系列(YOLOv1-v8)
    • SSD(Single Shot MultiBox Detector)
    • RetinaNet
  3. 锚框-free检测器:不使用预定义的锚框

    • CornerNet
    • CenterNet
    • FCOS(Fully Convolutional One-Stage Object Detection)

2.2.3 代码示例:使用YOLOv5进行目标检测

import torch
from PIL import Image
import cv2
import numpy as np

# 加载YOLOv5模型
model = torch.hub.load('ultralytics/yolov5', 'yolov5s')  # 加载轻量级模型

# 加载图像
img = Image.open('test.jpg')  # 替换为你的测试图像路径

# 进行目标检测
results = model(img)

# 显示检测结果
results.show()

# 打印检测结果
print(results.pandas().xyxy[0])  # 以pandas DataFrame形式显示结果

# 使用OpenCV加载图像并绘制检测结果
img_cv = cv2.imread('test.jpg')
detections = results.pandas().xyxy[0]

for _, detection in detections.iterrows():
    x1, y1, x2, y2, confidence, class_id, class_name = detection.values
    
    # 绘制边界框
    cv2.rectangle(img_cv, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)
    
    # 绘制类别和置信度
    label = f'{class_name}: {confidence:.2f}'
    cv2.putText(img_cv, label, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)

# 保存结果
cv2.imwrite('result.jpg', img_cv)
print('检测结果已保存到result.jpg')

# 实时目标检测(使用摄像头)
cap = cv2.VideoCapture(0)  # 0表示默认摄像头

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 进行目标检测
    results = model(frame)
    
    # 绘制检测结果
    detections = results.pandas().xyxy[0]
    for _, detection in detections.iterrows():
        x1, y1, x2, y2, confidence, class_id, class_name = detection.values
        cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)
        label = f'{class_name}: {confidence:.2f}'
        cv2.putText(frame, label, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
    
    # 显示结果
    cv2.imshow('YOLOv5 Real-time Detection', frame)
    
    # 按'q'退出
    if cv2.waitKey(1) == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

2.3 图像分割(Image Segmentation)

图像分割是将图像分割成不同区域或像素组的任务,根据分割粒度的不同,可分为语义分割、实例分割和全景分割。

2.3.1 语义分割(Semantic Segmentation)

语义分割的目标是为图像中的每个像素分配一个语义类别标签,不区分同一类别的不同实例。

2.3.1.1 语义分割的基本原理

语义分割的基本流程:

  1. 编码器-解码器架构:使用编码器提取特征,解码器恢复空间信息
  2. 跳跃连接:融合不同层次的特征信息
  3. 上采样:使用转置卷积、双线性插值等方法恢复图像分辨率
  4. 像素级分类:对每个像素进行分类
2.3.1.2 经典语义分割模型
  • FCN(Fully Convolutional Networks)
  • U-Net
  • SegNet
  • DeepLab系列(DeepLabv1-v3+)
  • Mask R-CNN(同时支持实例分割)

2.3.2 实例分割(Instance Segmentation)

实例分割的目标是为图像中的每个像素分配一个语义类别标签,并区分同一类别的不同实例。

2.3.2.1 实例分割的基本原理

实例分割的基本流程:

  1. 目标检测:检测图像中的目标实例
  2. 实例掩码生成:为每个检测到的目标生成像素级掩码
  3. 后处理:合并检测结果和掩码
2.3.2.2 经典实例分割模型
  • Mask R-CNN
  • YOLACT
  • SOLO
  • Detectron2系列

2.3.3 全景分割(Panoptic Segmentation)

全景分割是语义分割和实例分割的结合,同时处理"事物"(可数的目标,如人、车)和"东西"(不可数的区域,如天空、道路)。

2.3.4 代码示例:使用U-Net进行语义分割

import torch
import torch.nn as nn
import torch.nn.functional as F
from PIL import Image
import numpy as np
import cv2

# 定义U-Net模型
class UNet(nn.Module):
    def __init__(self, in_channels=3, out_channels=2):
        super(UNet, self).__init__()
        # 编码器
        self.enc1 = self.conv_block(in_channels, 64)
        self.enc2 = self.conv_block(64, 128)
        self.enc3 = self.conv_block(128, 256)
        self.enc4 = self.conv_block(256, 512)
        
        # 瓶颈
        self.bottleneck = self.conv_block(512, 1024)
        
        # 解码器
        self.up4 = nn.ConvTranspose2d(1024, 512, kernel_size=2, stride=2)
        self.dec4 = self.conv_block(1024, 512)  # 512 + 512
        
        self.up3 = nn.ConvTranspose2d(512, 256, kernel_size=2, stride=2)
        self.dec3 = self.conv_block(512, 256)  # 256 + 256
        
        self.up2 = nn.ConvTranspose2d(256, 128, kernel_size=2, stride=2)
        self.dec2 = self.conv_block(256, 128)  # 128 + 128
        
        self.up1 = nn.ConvTranspose2d(128, 64, kernel_size=2, stride=2)
        self.dec1 = self.conv_block(128, 64)  # 64 + 64
        
        # 输出层
        self.out = nn.Conv2d(64, out_channels, kernel_size=1)
    
    def conv_block(self, in_channels, out_channels):
        return nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1),
            nn.ReLU(inplace=True)
        )
    
    def forward(self, x):
        # 编码器
        e1 = self.enc1(x)
        e2 = self.enc2(F.max_pool2d(e1, kernel_size=2, stride=2))
        e3 = self.enc3(F.max_pool2d(e2, kernel_size=2, stride=2))
        e4 = self.enc4(F.max_pool2d(e3, kernel_size=2, stride=2))
        
        # 瓶颈
        b = self.bottleneck(F.max_pool2d(e4, kernel_size=2, stride=2))
        
        # 解码器
        d4 = self.up4(b)
        d4 = torch.cat([d4, e4], dim=1)
        d4 = self.dec4(d4)
        
        d3 = self.up3(d4)
        d3 = torch.cat([d3, e3], dim=1)
        d3 = self.dec3(d3)
        
        d2 = self.up2(d3)
        d2 = torch.cat([d2, e2], dim=1)
        d2 = self.dec2(d2)
        
        d1 = self.up1(d2)
        d1 = torch.cat([d1, e1], dim=1)
        d1 = self.dec1(d1)
        
        # 输出
        out = self.out(d1)
        return out

# 加载模型
model = UNet(in_channels=3, out_channels=2)  # 假设我们分割为2类

# 加载图像
img = Image.open('test.jpg')
img = img.resize((256, 256))  # 调整为256x256
img_tensor = torch.from_numpy(np.array(img)).permute(2, 0, 1).float() / 255.0
img_tensor = img_tensor.unsqueeze(0)  # 添加批次维度

# 进行分割
with torch.no_grad():
    output = model(img_tensor)
    pred = torch.argmax(output, dim=1).squeeze().numpy()

# 可视化分割结果
seg_mask = np.zeros((256, 256, 3), dtype=np.uint8)
seg_mask[pred == 1] = [0, 255, 0]  # 类别1用绿色表示

# 叠加原始图像和分割结果
img_np = np.array(img)
alpha = 0.5
result = cv2.addWeighted(img_np, alpha, seg_mask, 1-alpha, 0)

# 保存结果
cv2.imwrite('segmentation_result.jpg', cv2.cvtColor(result, cv2.COLOR_RGB2BGR))
print('分割结果已保存到segmentation_result.jpg')

# 显示结果
cv2.imshow('Segmentation Result', cv2.cvtColor(result, cv2.COLOR_RGB2BGR))
cv2.waitKey(0)
cv2.destroyAllWindows()

2.4 人脸识别(Face Recognition)

人脸识别是计算机视觉中的一个重要任务,目标是识别图像或视频中的人脸身份。

2.4.1 人脸识别的基本原理

人脸识别的基本流程:

  1. 人脸检测:定位图像中的人脸位置
  2. 人脸对齐:对检测到的人脸进行对齐和规范化
  3. 特征提取:从对齐后的人脸中提取特征
  4. 特征匹配:将提取的特征与数据库中的特征进行匹配
  5. 身份识别:根据匹配结果确定人脸身份

2.4.2 人脸识别的关键技术

  1. 人脸检测

    • Haar特征 + Adaboost
    • HOG + SVM
    • CNN-based方法(如MTCNN, RetinaFace)
  2. 人脸对齐

    • 基于关键点的仿射变换
    • 3D人脸建模
  3. 特征提取

    • 手工特征(LBP, HOG)
    • 深度学习特征(FaceNet, ArcFace, CosFace)
  4. 特征匹配

    • 欧氏距离
    • 余弦相似度
    • 曼哈顿距离

2.4.3 代码示例:使用dlib进行人脸识别

import dlib
import cv2
import numpy as np

# 加载dlib的人脸检测器和特征点检测器
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')  # 需要下载此文件

# 加载人脸识别模型
face_rec_model = dlib.face_recognition_model_v1('dlib_face_recognition_resnet_model_v1.dat')  # 需要下载此文件

# 加载图像
img = cv2.imread('group_photo.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 检测人脸
faces = detector(gray)

print(f'检测到 {len(faces)} 个人脸')

# 对每个人脸进行处理
for i, face in enumerate(faces):
    # 绘制边界框
    x1, y1, x2, y2 = face.left(), face.top(), face.right(), face.bottom()
    cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
    
    # 检测特征点
    landmarks = predictor(gray, face)
    
    # 绘制特征点
    for n in range(0, 68):
        x = landmarks.part(n).x
        y = landmarks.part(n).y
        cv2.circle(img, (x, y), 2, (0, 0, 255), -1)
    
    # 提取人脸特征
    face_descriptor = face_rec_model.compute_face_descriptor(img, landmarks)
    face_descriptor = np.array(face_descriptor)
    
    # 打印特征向量(前10个元素)
    print(f'人脸 {i+1} 的特征向量(前10个元素): {face_descriptor[:10]}')

# 保存结果
cv2.imwrite('face_recognition_result.jpg', img)
print('人脸识别结果已保存到face_recognition_result.jpg')

# 显示结果
cv2.imshow('Face Recognition Result', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 实时人脸识别(使用摄像头)
cap = cv2.VideoCapture(0)  # 0表示默认摄像头

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = detector(gray)
    
    for face in faces:
        x1, y1, x2, y2 = face.left(), face.top(), face.right(), face.bottom()
        cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
        
        landmarks = predictor(gray, face)
        for n in range(0, 68):
            x = landmarks.part(n).x
            y = landmarks.part(n).y
            cv2.circle(frame, (x, y), 1, (0, 0, 255), -1)
    
    cv2.imshow('Real-time Face Detection', frame)
    
    if cv2.waitKey(1) == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

2.5 其他基础任务

2.5.1 图像超分辨率(Image Super-Resolution)

图像超分辨率的目标是从低分辨率图像生成高分辨率图像。

2.5.1.1 图像超分辨率的基本原理

图像超分辨率的基本流程:

  1. 特征提取:从低分辨率图像中提取特征
  2. 特征映射:将低分辨率特征映射到高分辨率特征空间
  3. 图像重建:基于高分辨率特征重建图像
2.5.1.2 经典超分辨率模型
  • SRCNN(Super-Resolution Convolutional Neural Network)
  • EDSR(Enhanced Deep Super-Resolution Network)
  • SRGAN(Super-Resolution Generative Adversarial Network)
  • ESRGAN(Enhanced Super-Resolution Generative Adversarial Network)

2.5.2 图像风格迁移(Style Transfer)

图像风格迁移的目标是将一幅图像的风格应用到另一幅图像上,同时保留内容。

2.5.2.1 图像风格迁移的基本原理

图像风格迁移的基本流程:

  1. 特征提取:使用预训练的CNN提取内容图像和风格图像的特征
  2. 损失计算:计算内容损失和风格损失
  3. 优化:通过优化生成图像的像素值,最小化总损失
2.5.2.2 经典风格迁移方法
  • Gatys等人的方法(基于优化)
  • Fast Style Transfer(基于前向网络)
  • CycleGAN(无配对数据的风格迁移)
  • AdaIN(自适应实例归一化)

3. 技术挑战与发展趋势

3.1 技术挑战

计算机视觉技术面临以下主要挑战:

  1. 数据挑战

    • 数据标注成本高
    • 数据分布不均
    • 低资源场景数据稀缺
  2. 模型挑战

    • 计算资源需求大
    • 模型部署困难
    • 可解释性差
  3. 场景挑战

    • 光照变化
    • 视角变化
    • 遮挡
    • 背景复杂
    • 目标变形
  4. 泛化挑战

    • 跨域泛化能力弱
    • 对未见场景适应性差
    • 鲁棒性不足

3.2 发展趋势

计算机视觉技术的发展趋势包括:

  1. 大模型时代

    • 预训练视觉模型(如ViT, CLIP)
    • 自监督和无监督学习
    • 多任务学习
  2. 多模态融合

    • 视觉-语言融合(如Vision-Language Pre-training)
    • 视觉-语音融合
    • 多模态表示学习
  3. 高效模型设计

    • 轻量级模型
    • 模型压缩和量化
    • 知识蒸馏
    • 神经架构搜索
  4. 自监督学习

    • 对比学习
    • 掩码图像建模
    • 自监督预训练
  5. 3D视觉

    • 单目3D目标检测
    • 点云处理
    • 立体视觉
    • 3D重建
  6. 可解释性

    • 视觉解释方法
    • 透明模型设计
    • 因果推理
  7. 鲁棒性

    • 对抗训练
    • 域适应
    • 开放世界识别
  8. 实时处理

    • 边缘计算
    • 硬件加速
    • 实时算法优化

4. 实践案例

4.1 案例一:使用ResNet进行图像分类

背景:某电商平台需要自动识别用户上传的商品图片,将其分类到正确的商品类别中。

解决方案:使用预训练的ResNet模型进行商品图像分类。

代码示例

import torch
import torchvision.models as models
import torchvision.transforms as transforms
from PIL import Image

# 1. 加载预训练的ResNet模型
model = models.resnet18(pretrained=True)
model.eval()  # 设置为评估模式

# 2. 定义图像预处理
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]),
])

# 3. 加载并预处理图像
img = Image.open('product.jpg')
img_t = preprocess(img)
batch_t = torch.unsqueeze(img_t, 0)

# 4. 进行预测
with torch.no_grad():
    outputs = model(batch_t)
    _, predicted = torch.max(outputs, 1)

# 5. 加载ImageNet类别标签
with open('imagenet_classes.txt') as f:  # 需要下载此文件
    classes = [line.strip() for line in f.readlines()]

# 6. 打印预测结果
print(f'预测类别: {classes[predicted[0]]}')

# 7. 打印前5个预测结果
probabilities = torch.nn.functional.softmax(outputs, dim=1)[0]
top5_prob, top5_catid = torch.topk(probabilities, 5)
for i in range(top5_prob.size(0)):
    print(f'{classes[top5_catid[i]]}: {top5_prob[i].item():.4f}')

4.2 案例二:使用YOLOv5进行目标检测

背景:某安防公司需要开发一个监控系统,能够实时检测监控视频中的人员、车辆等目标。

解决方案:使用YOLOv5模型进行实时目标检测。

代码示例

import torch
import cv2
import numpy as np

# 1. 加载YOLOv5模型
model = torch.hub.load('ultralytics/yolov5', 'yolov5s')

# 2. 打开视频文件
video_path = 'surveillance.mp4'
cap = cv2.VideoCapture(video_path)

# 3. 获取视频属性
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

# 4. 创建输出视频写入器
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi', fourcc, fps, (width, height))

# 5. 处理视频帧
while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    
    # 6. 进行目标检测
    results = model(frame)
    
    # 7. 绘制检测结果
    detections = results.pandas().xyxy[0]
    for _, detection in detections.iterrows():
        x1, y1, x2, y2, confidence, class_id, class_name = detection.values
        
        # 只显示人员和车辆
        if class_name in ['person', 'car', 'truck', 'bus']:
            # 绘制边界框
            cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)
            
            # 绘制类别和置信度
            label = f'{class_name}: {confidence:.2f}'
            cv2.putText(frame, label, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
    
    # 8. 写入输出视频
    out.write(frame)
    
    # 9. 显示结果
    cv2.imshow('Surveillance Detection', frame)
    
    if cv2.waitKey(1) == ord('q'):
        break

# 10. 释放资源
cap.release()
out.release()
cv2.destroyAllWindows()
print('处理完成,结果已保存到output.avi')

4.3 案例三:使用U-Net进行医学图像分割

背景:某医院需要开发一个医学影像分析系统,能够自动分割CT扫描中的病变区域。

解决方案:使用U-Net模型进行医学图像分割。

代码示例

import torch
import torch.nn as nn
import numpy as np
import cv2
from PIL import Image

# 1. 定义U-Net模型(与前面相同)
class UNet(nn.Module):
    def __init__(self, in_channels=1, out_channels=2):
        super(UNet, self).__init__()
        # 编码器
        self.enc1 = self.conv_block(in_channels, 64)
        self.enc2 = self.conv_block(64, 128)
        self.enc3 = self.conv_block(128, 256)
        self.enc4 = self.conv_block(256, 512)
        
        # 瓶颈
        self.bottleneck = self.conv_block(512, 1024)
        
        # 解码器
        self.up4 = nn.ConvTranspose2d(1024, 512, kernel_size=2, stride=2)
        self.dec4 = self.conv_block(1024, 512)  # 512 + 512
        
        self.up3 = nn.ConvTranspose2d(512, 256, kernel_size=2, stride=2)
        self.dec3 = self.conv_block(512, 256)  # 256 + 256
        
        self.up2 = nn.ConvTranspose2d(256, 128, kernel_size=2, stride=2)
        self.dec2 = self.conv_block(256, 128)  # 128 + 128
        
        self.up1 = nn.ConvTranspose2d(128, 64, kernel_size=2, stride=2)
        self.dec1 = self.conv_block(128, 64)  # 64 + 64
        
        # 输出层
        self.out = nn.Conv2d(64, out_channels, kernel_size=1)
    
    def conv_block(self, in_channels, out_channels):
        return nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1),
            nn.ReLU(inplace=True)
        )
    
    def forward(self, x):
        # 编码器
        e1 = self.enc1(x)
        e2 = self.enc2(nn.functional.max_pool2d(e1, kernel_size=2, stride=2))
        e3 = self.enc3(nn.functional.max_pool2d(e2, kernel_size=2, stride=2))
        e4 = self.enc4(nn.functional.max_pool2d(e3, kernel_size=2, stride=2))
        
        # 瓶颈
        b = self.bottleneck(nn.functional.max_pool2d(e4, kernel_size=2, stride=2))
        
        # 解码器
        d4 = self.up4(b)
        d4 = torch.cat([d4, e4], dim=1)
        d4 = self.dec4(d4)
        
        d3 = self.up3(d4)
        d3 = torch.cat([d3, e3], dim=1)
        d3 = self.dec3(d3)
        
        d2 = self.up2(d3)
        d2 = torch.cat([d2, e2], dim=1)
        d2 = self.dec2(d2)
        
        d1 = self.up1(d2)
        d1 = torch.cat([d1, e1], dim=1)
        d1 = self.dec1(d1)
        
        # 输出
        out = self.out(d1)
        return out

# 2. 加载模型
model = UNet(in_channels=1, out_channels=2)  # 输入为灰度图像,输出为2类分割
# 假设我们已经训练好了模型并保存
# model.load_state_dict(torch.load('unet_medical.pth'))
model.eval()

# 3. 加载医学图像
img = Image.open('ct_scan.jpg').convert('L')  # 转换为灰度图像
img = img.resize((256, 256))
img_np = np.array(img)
img_tensor = torch.from_numpy(img_np).unsqueeze(0).unsqueeze(0).float() / 255.0

# 4. 进行分割
with torch.no_grad():
    output = model(img_tensor)
    pred = torch.argmax(output, dim=1).squeeze().numpy()

# 5. 可视化分割结果
# 创建彩色掩码
mask = np.zeros((256, 256, 3), dtype=np.uint8)
mask[pred == 1] = [255, 0, 0]  # 病变区域用红色表示

# 叠加原始图像和分割结果
img_color = cv2.cvtColor(img_np, cv2.COLOR_GRAY2BGR)
alpha = 0.5
result = cv2.addWeighted(img_color, alpha, mask, 1-alpha, 0)

# 6. 计算病变区域面积
lesion_area = np.sum(pred == 1)
total_area = pred.size
lesion_percentage = (lesion_area / total_area) * 100
print(f'病变区域面积: {lesion_area} 像素')
print(f'病变区域占比: {lesion_percentage:.2f}%')

# 7. 保存结果
cv2.imwrite('medical_segmentation_result.jpg', result)
print('分割结果已保存到medical_segmentation_result.jpg')

# 8. 显示结果
cv2.imshow('Original CT Scan', img_color)
cv2.imshow('Lesion Segmentation', mask)
cv2.imshow('Overlay Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

5. 总结与建议

5.1 学习建议

  1. 掌握基础理论:了解计算机视觉的基本概念和原理
  2. 实践项目:通过实际项目练习巩固所学知识
  3. 模型选择:根据具体任务选择合适的模型
  4. 数据处理:学习数据预处理、增强和标注技术
  5. 性能优化:了解模型压缩、量化等优化技术
  6. 关注前沿:跟踪计算机视觉的最新研究和发展

5.2 最佳实践

  1. 任务适配

    • 图像分类:使用ResNet、EfficientNet等模型
    • 目标检测:使用YOLO系列、Faster R-CNN等模型
    • 图像分割:使用U-Net、DeepLab等模型
    • 人脸识别:使用FaceNet、ArcFace等模型
  2. 数据处理

    • 数据增强:随机翻转、缩放、裁剪、颜色变换等
    • 数据标准化:对输入图像进行归一化
    • 数据标注:使用专业标注工具,确保标注质量
  3. 模型训练

    • 迁移学习:使用预训练模型加速训练
    • 学习率调度:使用学习率衰减、warmup等策略
    • 正则化:使用dropout、权重衰减等防止过拟合
    • 批次大小:根据硬件资源选择合适的批次大小
  4. 模型评估

    • 图像分类:准确率、精确率、召回率、F1分数
    • 目标检测:mAP(mean Average Precision)
    • 图像分割:IoU(Intersection over Union)、Dice系数
    • 人脸识别:准确率、FRR(False Rejection Rate)、FAR(False Acceptance Rate)

5.3 未来展望

计算机视觉技术正在快速发展,未来将在以下方向取得突破:

  1. 大模型时代:预训练视觉模型将成为主流,模型能力将进一步提升
  2. 多模态融合:视觉与语言、语音等模态的融合将更加紧密
  3. 高效智能:模型将更加高效,适用于边缘设备和实时场景
  4. 自监督学习:减少对标注数据的依赖,提高模型泛化能力
  5. 3D视觉:从2D到3D的演进,提供更丰富的场景理解
  6. 可解释性:模型决策过程将更加透明,增强用户信任
  7. 鲁棒性:模型将更加鲁棒,能够应对复杂的真实场景

作为人工智能训练师,掌握计算机视觉的基础任务和技术,将有助于我们更好地理解和应用这一强大技术,为各种视觉相关任务开发更有效的解决方案。同时,我们也需要关注计算机视觉的伦理问题,确保技术的负责任使用。

« 上一篇 预训练语言模型(BERT, GPT)概览 下一篇 » 目标检测模型(YOLO, R-CNN)概览