计算机视觉基础与实战应用

学习目标
本章将带你深入理解计算机视觉的核心概念,掌握图像处理、特征提取及主流模型架构。通过 OpenCV、PIL、PyTorch 等工具库的实战演练,你将学会如何构建图像分类、目标检测及语义分割应用,最终完成一个完整的视觉项目。
一、计算机视觉基础
1.1 基本概念与重要性
计算机视觉(Computer Vision)是人工智能的关键分支,旨在让计算机像人类一样理解和解释图像内容。其核心价值在于:
- 图像理解:识别物体、场景和动作
- 目标检测:定位并识别图像中的多个物体
- 图像分类:对整张图像进行标签化
- 语义分割:像素级的图像分析与标记
- 图像生成:创造新的图像内容
1.2 应用场景与挑战
计算机视觉已广泛应用于医疗诊断、自动驾驶、安防监控、电商推荐等领域。但在实际落地中,我们常面临图像噪声、物体多样性、光照变化、数据稀缺及计算资源限制等挑战。
二、图像处理技术
2.1 图像预处理
预处理是后续分析的基础,主要包括读取保存、尺寸调整、色彩平衡及裁剪旋转等操作。
import cv2
import numpy as np
def read_image(image_path):
image = cv2.imread(image_path)
return image
def save_image(image, output_path):
cv2.imwrite(output_path, image)
def resize_image(image, width, height):
resized_image = cv2.resize(image, (width, height))
return resized_image
def adjust_brightness_contrast(image, alpha=1.0, beta=0.0):
adjusted_image = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)
return adjusted_image
def crop_image(image, x, y, width, height):
cropped_image = image[y:y+height, x:x+width]
return cropped_image
def rotate_image(image, angle):
(h, w) = image.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated_image = cv2.warpAffine(image, M, (w, h))
return rotated_image
2.2 图像增强与滤波
为了提升图像质量,我们需要使用直方图均衡化、平滑滤波等技术去除噪声或增强对比度。边缘检测则是提取轮廓信息的重要手段。
import cv2
import numpy as np
def histogram_equalization(image):
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
equalized_image = cv2.equalizeHist(gray_image)
return equalized_image
def mean_filter(image, kernel_size=3):
blurred_image = cv2.blur(image, (kernel_size, kernel_size))
return blurred_image
def gaussian_filter(image, kernel_size=3, sigma=0):
blurred_image = cv2.GaussianBlur(image, (kernel_size, kernel_size), sigma)
return blurred_image
def median_filter(image, kernel_size=3):
blurred_image = cv2.medianBlur(image, kernel_size)
return blurred_image
def sobel_edge_detection(image):
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
sobel_x = cv2.Sobel(gray_image, cv2.CV_64F, 1, 0, ksize=3)
sobel_y = cv2.Sobel(gray_image, cv2.CV_64F, 0, 1, ksize=3)
sobel_combined = np.sqrt(sobel_x**2 + sobel_y**2)
sobel_combined = np.uint8(sobel_combined / np.max(sobel_combined) * 255)
return sobel_combined
def canny_edge_detection(image, threshold1=100, threshold2=200):
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray_image, threshold1, threshold2)
return edges
三、特征提取方法
3.1 HOG、SIFT 与 ORB
传统特征提取算法如 HOG(方向梯度直方图)、SIFT(尺度不变特征变换)和 ORB(快速 BRIEF),在深度学习普及前曾是主流。它们能有效捕捉图像的局部结构信息。
import cv2
import numpy as np
def extract_hog_features(image):
hog = cv2.HOGDescriptor()
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
features = hog.compute(gray_image)
return features
def extract_sift_features(image):
sift = cv2.SIFT_create()
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
keypoints, descriptors = sift.detectAndCompute(gray_image, None)
return keypoints, descriptors
def extract_orb_features(image):
orb = cv2.ORB_create()
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
keypoints, descriptors = orb.detectAndCompute(gray_image, None)
return keypoints, descriptors
四、常用模型与架构
4.1 模型演进
从早期的 LeNet、AlexNet,到 VGG 的小卷积核设计,再到 ResNet 的残差连接解决梯度消失问题,以及 YOLO 系列的高效目标检测,深度学习模型不断迭代优化。
4.2 PyTorch 实战训练
下面是一个基于 ResNet 的完整训练流程示例,包含数据加载、模型微调及训练循环。
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms, models
def train_resnet_model(data_dir, num_classes=2, batch_size=32, num_epochs=10, lr=0.001):
# 数据预处理
data_transforms = {
'train': transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
'val': transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
}
image_datasets = {x: datasets.ImageFolder(f'{data_dir}/{x}', data_transforms[x]) for x in ['train', 'val']}
dataloaders = {x: DataLoader(image_datasets[x], batch_size=batch_size, shuffle=True, num_workers=4) for x in ['train', 'val']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
class_names = image_datasets['train'].classes
# 加载模型
model = models.resnet18(pretrained=True)
num_ftrs = model.fc.in_features
model.fc = nn.Linear(num_ftrs, num_classes)
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=lr, momentum=0.9)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)
# 训练模型
for epoch in range(num_epochs):
print(f'Epoch {epoch + 1}/{num_epochs}')
print('-' * 10)
for phase in ['train', 'val']:
if phase == 'train':
model.train()
else:
model.eval()
running_loss = 0.0
running_corrects = 0
for inputs, labels in dataloaders[phase]:
optimizer.zero_grad()
with torch.set_grad_enabled(phase == 'train'):
outputs = model(inputs)
_, preds = torch.max(outputs, 1)
loss = criterion(outputs, labels)
if phase == 'train':
loss.backward()
optimizer.step()
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
if phase == 'train':
scheduler.step()
epoch_loss = running_loss / dataset_sizes[phase]
epoch_acc = running_corrects.double() / dataset_sizes[phase]
print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}')
print('Training complete')
return model
五、实战项目:计算机视觉应用开发
5.1 系统设计与实现
本项目旨在构建一个支持图像分类与目标检测的桌面应用。采用分层架构,包括用户界面层、业务逻辑层、图像处理层和数据存储层。
环境搭建
确保安装必要的依赖库:
pip install opencv-python pillow torch torchvision
核心功能代码
以下是 Tkinter 界面与核心处理逻辑的整合示例(注:实际项目中建议拆分为独立模块)。
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
from PIL import Image, ImageTk
import cv2
import numpy as np
import torch
from torchvision import transforms, models
class ImageInputFrame(tk.Frame):
def __init__(self, parent, on_image_selected):
super().__init__(parent)
self.parent = parent
self.on_image_selected = on_image_selected
self.create_widgets()
def create_widgets(self):
self.image_label = tk.Label(self)
self.image_label.pack(pady=10, padx=10, fill="both", expand=True)
tk.Button(self, text="选择图像", command=self.select_image).pack(pady=10, padx=10)
def select_image(self):
file_path = filedialog.askopenfilename(filetypes=[("Image Files", "*.png *.jpg *.jpeg *.bmp")])
if file_path:
image = Image.open(file_path)
image = image.resize((400, 300), Image.LANCZOS)
photo = ImageTk.PhotoImage(image)
self.image_label.configure(image=photo)
self.image_label.image = photo
self.on_image_selected(file_path)
class ResultFrame(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.create_widgets()
def create_widgets(self):
self.result_text = scrolledtext.ScrolledText(self, width=60, height=5)
self.result_text.pack(pady=10, padx=10, fill="both", expand=True)
def display_result(self, result):
self.result_text.delete("1.0", tk.END)
self.result_text.insert(tk.END, result)
def classify_image(image_path, model_path, class_names):
data_transforms = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
image = Image.open(image_path)
image = data_transforms(image)
image = image.unsqueeze(0)
model = models.resnet18()
num_ftrs = model.fc.in_features
model.fc = torch.nn.Linear(num_ftrs, len(class_names))
model.load_state_dict(torch.load(model_path, weights_only=False))
model.eval()
with torch.no_grad():
outputs = model(image)
_, preds = torch.max(outputs, 1)
return class_names[preds[0]]
def detect_objects(image_path, model_path, class_names):
image = cv2.imread(image_path)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image_pil = Image.fromarray(image_rgb)
data_transforms = transforms.Compose([
transforms.Resize((416, 416)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
image_tensor = data_transforms(image_pil)
image_tensor = image_tensor.unsqueeze(0)
model = models.detection.fasterrcnn_resnet50_fpn(pretrained=False)
in_features = model.roi_heads.box_predictor.cls_score.in_features
model.roi_heads.box_predictor = models.detection.faster_rcnn.FastRCNNPredictor(in_features, len(class_names))
model.load_state_dict(torch.load(model_path, weights_only=False))
model.eval()
with torch.no_grad():
outputs = model(image_tensor)
boxes = outputs[0]['boxes'].cpu().numpy()
scores = outputs[0]['scores'].cpu().numpy()
labels = outputs[0]['labels'].cpu().numpy()
for i in range(len(boxes)):
if scores[i] > 0.5:
box = boxes[i].astype(int)
label = class_names[labels[i]]
score = scores[i]
cv2.rectangle(image, (box[0], box[1]), (box[2], box[3]), (0, 255, 0), 2)
cv2.putText(image, f"{label}: {score:.2f}", (box[0], box[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
return image
class CVApp:
def __init__(self, root):
self.root = root
self.root.title("计算机视觉应用")
self.class_names = ['猫', '狗']
self.model_path = 'model.pth'
self.create_widgets()
def create_widgets(self):
self.image_input_frame = ImageInputFrame(self.root, self.process_image)
self.image_input_frame.pack(pady=10, padx=10, fill="both", expand=True)
function_frame = tk.LabelFrame(self.root, text="功能选择")
function_frame.pack(pady=10, padx=10, fill="x")
self.function_var = tk.StringVar()
self.function_var.set("图像分类")
tk.Radiobutton(function_frame, text="图像分类", variable=self.function_var, value="图像分类").grid(row=0, column=0, padx=5, pady=5)
tk.Radiobutton(function_frame, text="目标检测", variable=self.function_var, value="目标检测").grid(row=0, column=1, padx=5, pady=5)
self.result_frame = ResultFrame(self.root)
self.result_frame.pack(pady=10, padx=10, fill="both", expand=True)
self.output_image_label = tk.Label(self.root)
self.output_image_label.pack(pady=10, padx=10, fill="both", expand=True)
def process_image(self, image_path):
function = self.function_var.get()
try:
if function == "图像分类":
result = classify_image(image_path, self.model_path, self.class_names)
self.result_frame.display_result(result)
elif function == "目标检测":
result_image = detect_objects(image_path, self.model_path, self.class_names)
result_image = cv2.cvtColor(result_image, cv2.COLOR_BGR2RGB)
result_image_pil = Image.fromarray(result_image)
result_image_pil = result_image_pil.resize((400, 300), Image.LANCZOS)
photo = ImageTk.PhotoImage(result_image_pil)
self.output_image_label.configure(image=photo)
self.output_image_label.image = photo
else:
raise ValueError("未知功能")
except Exception as e:
messagebox.showerror("错误", f"处理失败:{str(e)}")
if __name__ == "__main__":
root = tk.Tk()
app = CVApp(root)
root.mainloop()
5.2 运行与测试
启动程序后,依次执行以下步骤:
- 点击'选择图像'加载图片。
- 在功能选择区切换'图像分类'或'目标检测'。
- 查看结果文本框或可视化图像。
六、总结
计算机视觉作为人工智能的核心分支,正逐步改变我们与数字世界的交互方式。从底层的像素处理到高层的语义理解,掌握图像处理、特征工程及深度学习模型是构建智能应用的关键。通过上述实战案例,希望你能建立起从理论到落地的完整认知,并在实际项目中灵活运用这些技术。

