找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2797

积分

0

好友

361

主题
发表于 10 小时前 | 查看: 5| 回复: 0

昨天我们学习了计算机视觉的理论基础——知道了图像在计算机眼中是数字矩阵,理解了卷积如何提取特征,也看到了CNN如何一步步学会“看”。但问题来了:在实际项目中,你怎么把一张照片读进Python?怎么把彩色图转成灰度?怎么检测人脸?

今天,我们学习 OpenCV ——计算机视觉领域最基础、最强大、最普及的工具库。

OpenCV是什么?

OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉库,包含2500+优化算法,涵盖图像处理、视频分析、目标检测、机器学习等几乎所有视觉任务。

它就像视觉工程师的“瑞士军刀”——你遇到的大多数图像处理问题,OpenCV都有现成函数。

OpenCV的安装与核心理念

安装OpenCV

# 标准安装(包含主要模块)
pip install opencv-python

# 如果需要额外模块(如跟踪算法)
pip install opencv-contrib-python

# 验证安装
import cv2
print(f"OpenCV版本: {cv2.__version__}")
# 应输出 4.5.x 或更高版本

OpenCV的核心理念:一切皆矩阵

OpenCV读取的图像,本质上就是 NumPy数组——这意味着你可以用NumPy的所有功能操作图像!

import cv2
import numpy as np

# 读取图像
img = cv2.imread('photo.jpg')
print(type(img))  # <class 'numpy.ndarray'>
print(img.shape)  # (高度, 宽度, 通道数) 例如 (480, 640, 3)

# 可以用NumPy方式操作
roi = img[100:200, 200:300]  # 裁剪感兴趣区域
img[:, :, 0] = 0  # 将蓝色通道全部置0

关键认知:OpenCV + NumPy = 图像处理的“倚天剑+屠龙刀”。

图像的读写与显示 —— Hello World

读取图像:cv2.imread()

import cv2

# 最基本的读取——彩色模式(默认)
img_color = cv2.imread('cat.jpg')
# 等价于 img_color = cv2.imread('cat.jpg', cv2.IMREAD_COLOR)

# 灰度模式读取
img_gray = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)

# 保留Alpha通道(透明度)
img_unchanged = cv2.imread('cat.png', cv2.IMREAD_UNCHANGED)

# 检查是否读取成功(文件路径错误时不会报错,但返回None)
if img_color is None:
    print("错误:无法读取图像,请检查文件路径")
    exit()

重要提示:OpenCV读取彩色图像时,默认通道顺序是 BGR(蓝-绿-红),而不是常见的RGB!这一点常被初学者忽视,导致用Matplotlib显示时颜色异常。

显示图像:cv2.imshow()

# 创建窗口并显示图像
cv2.imshow('My Cat', img_color)

# 等待按键(必须!否则窗口一闪而过)
cv2.waitKey(0)  # 0表示无限等待,直到按下任意键

# 关闭所有窗口
cv2.destroyAllWindows()

cv2.waitKey()的秘密

  • 它不仅等待键盘输入,还负责处理GUI事件——没有它,图像窗口根本不会正常显示
  • 可以传入毫秒数:cv2.waitKey(5000) 等待5秒后自动关闭

保存图像:cv2.imwrite()

# 保存图像(格式由文件扩展名决定)
cv2.imwrite('cat_gray.jpg', img_gray)  # 保存为JPEG
cv2.imwrite('cat_copy.png', img_color)  # 保存为PNG

完整示例:读取→显示→保存

import cv2

# 读取图像
img = cv2.imread('cat.jpg', cv2.IMREAD_COLOR)

# 检查
if img is None:
    print("文件读取失败")
    exit()

# 显示原图
cv2.imshow('Original Cat', img)

# 转换为灰度并显示
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray Cat', gray)

# 保存灰度图
cv2.imwrite('cat_gray.jpg', gray)

# 等待按键
cv2.waitKey(0)
cv2.destroyAllWindows()

颜色空间转换 —— 让颜色更“听话”

为什么要转换颜色空间?

RGB虽然符合人类视觉,但在计算机视觉任务中并不是最佳选择:

  • RGB三个通道高度相关,受光照影响大
  • HSV(色调-饱和度-明度)将颜色信息与亮度分离,更适合基于颜色的目标提取

BGR ↔ 灰度

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

BGR ↔ HSV

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

HSV各通道范围(OpenCV中):

  • H(色调):0~179
  • S(饱和度):0~255
  • V(明度):0~255

实战:提取蓝色物体

import cv2
import numpy as np

# 读取图像
img = cv2.imread('colorful_balls.jpg')

# 转换为HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 定义蓝色的HSV范围
lower_blue = np.array([110, 50, 50])
upper_blue = np.array([130, 255, 255])

# 创建掩膜(在范围内的像素为白色,其余为黑色)
mask = cv2.inRange(hsv, lower_blue, upper_blue)

# 将掩膜与原图进行“与”操作,只保留蓝色区域
result = cv2.bitwise_and(img, img, mask=mask)

# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Mask', mask)
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

这段代码能做什么?—— 在彩色球堆中,只把蓝色的球“抠”出来,其他变黑。这是颜色追踪的基础。

图像的几何变换 —— 移动、旋转、缩放

缩放:cv2.resize()

# 按指定尺寸缩放
resized = cv2.resize(img, (300, 200))

# 按比例缩放
scale_percent = 50  # 缩小到50%
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
resized = cv2.resize(img, (width, height))

# 插值方法的选择
# 缩小:INTER_AREA 效果最好
# 放大:INTER_LINEAR 或 INTER_CUBIC
resized = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)

平移:cv2.warpAffine()

# 定义平移矩阵 [1, 0, tx; 0, 1, ty]
tx, ty = 50, 100 # 向右移50,向下移100
M = np.float32([[1, 0, tx], [0, 1, ty]])

# 应用仿射变换
shifted = cv2.warpAffine(img, M, (img.shape[1], img.shape[0]))

旋转:cv2.getRotationMatrix2D()

# 获取旋转矩阵
h, w = img.shape[:2]
center = (w // 2, h // 2)  # 旋转中心
angle = 45  # 逆时针45度
scale = 1.0  # 不缩放

M = cv2.getRotationMatrix2D(center, angle, scale)

# 应用旋转
rotated = cv2.warpAffine(img, M, (w, h))

仿射变换:三点对应

仿射变换是“线性变换+平移”,可以完成旋转、缩放、剪切等操作。OpenCV提供了根据三个对应点计算变换矩阵的函数:

# 原图中的三个点
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
# 目标图中的三个点
pts2 = np.float32([[10, 100], [200, 50], [100, 250]])

# 计算仿射变换矩阵
M = cv2.getAffineTransform(pts1, pts2)

# 应用变换
dst = cv2.warpAffine(img, M, (img.shape[1], img.shape[0]))

透视变换:四点对应

当需要处理倾斜视角(如拍摄的文档、路牌)时,透视变换是利器:

# 原图中的四个角点(通常手动选取)
pts1 = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]])
# 目标图中的四个角点(矩形)
pts2 = np.float32([[0, 0], [300, 0], [0, 300], [300, 300]])

# 计算透视变换矩阵
M = cv2.getPerspectiveTransform(pts1, pts2)

# 应用变换
dst = cv2.warpPerspective(img, M, (300, 300))

应用场景:扫描文档矫正、广告牌替换、AR效果。

图像滤波 —— 去噪与增强

低通滤波:平滑/模糊

# 均值滤波(每个像素替换为邻域平均值)
blur = cv2.blur(img, (5, 5))

# 高斯滤波(邻域加权平均,中心权重高)
gaussian = cv2.GaussianBlur(img, (5, 5), 1.5)

# 中值滤波(对椒盐噪声特别有效)
median = cv2.medianBlur(img, 5)

参数解释

  • (5, 5):滤波器核大小(必须是奇数)
  • 1.5:高斯核的标准差(σ)

高通滤波:边缘检测

作用:提取图像中灰度变化剧烈的地方——即边缘。

Sobel算子:一阶导数

# 计算x方向的梯度
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=5)

# 计算y方向的梯度
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=5)

# 计算梯度幅值
sobel = cv2.magnitude(sobelx, sobely)
sobel = cv2.convertScaleAbs(sobel)  # 转为8位图像

Canny边缘检测:最常用的边缘算法

Canny算法包括高斯滤波 + 梯度计算 + 非极大值抑制 + 双阈值处理,效果通常最好。

# 简单的两行代码,效果惊人
edges = cv2.Canny(gray, threshold1=100, threshold2=200)

# 显示结果
cv2.imshow('Edges', edges)

参数含义

  • threshold1:低阈值(低于此值的不是边缘)
  • threshold2:高阈值(高于此值的肯定是边缘)
  • 中间的点,如果与高阈值边缘相连,则也被认为是边缘

视频处理 —— 让静态图像动起来

从摄像头捕获实时视频

import cv2

# 打开摄像头(0表示第一个摄像头)
cap = cv2.VideoCapture(0)

# 检查是否成功打开
if not cap.isOpened():
    print("无法打开摄像头")
    exit()

while True:
    # 逐帧捕获
    ret, frame = cap.read()

    # 如果正确读取帧,ret为True
    if not ret:
        print("无法接收帧,退出...")
        break

    # 处理帧(例如转为灰度)
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 显示结果
    cv2.imshow('Camera', gray)

    # 按'q'键退出
    if cv2.waitKey(1) == ord('q'):
        break

# 释放资源
cap.release()
cv2.destroyAllWindows()

从视频文件读取

cap = cv2.VideoCapture('video.mp4')  # 只需替换为文件路径
# 其余代码与摄像头相同

保存视频

# 定义视频编码器和输出对象
fourcc = cv2.VideoWriter_fourcc(*'XVID')  # 或 'MJPG', 'X264'
out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480))

while True:
    ret, frame = cap.read()
    if not ret:
        break

    # 处理帧(例如水平翻转)
    frame = cv2.flip(frame, 1)

    # 写入帧
    out.write(frame)

    cv2.imshow('Frame', frame)
    if cv2.waitKey(1) == ord('q'):
        break

# 释放所有资源
cap.release()
out.release()
cv2.destroyAllWindows()

实战项目——摄像头人脸检测与跟踪

让我们把今天学的所有知识整合起来,做一个完整的计算机视觉应用——实时人脸检测与跟踪。

核心思路

  1. 人脸检测:使用Haar级联分类器在每帧中查找人脸
  2. 人脸跟踪:检测到人脸后,用CSRT跟踪器持续追踪
  3. 状态切换:跟踪失败时,自动切换回检测模式

完整代码实现

import cv2

class FaceTracker:
    """实时人脸检测+跟踪器"""

    def __init__(self):
        # 加载预训练的人脸检测模型
        self.face_cascade = cv2.CascadeClassifier(
            cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
        )
        self.tracker = None
        self.tracking = False

    def detect_face(self, frame):
        """检测人脸"""
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = self.face_cascade.detectMultiScale(
            gray,
            scaleFactor=1.1,    # 图像金字塔缩放比例
            minNeighbors=5,      # 每个候选框需要保留的邻域数
            minSize=(30, 30)     # 最小人脸尺寸
        )
        return faces

    def init_tracker(self, frame, bbox):
        """初始化跟踪器"""
        self.tracker = cv2.TrackerCSRT_create()
        self.tracker.init(frame, tuple(bbox))
        self.tracking = True

    def update_tracker(self, frame):
        """更新跟踪"""
        success, bbox = self.tracker.update(frame)
        return success, bbox

    def process_frame(self, frame):
        """处理单帧图像"""
        # 如果当前未跟踪,执行检测
        if not self.tracking:
            faces = self.detect_face(frame)
            if len(faces) > 0:
                # 跟踪第一个检测到的人脸
                bbox = faces[0]  # (x, y, w, h)
                self.init_tracker(frame, bbox)
                # 绘制检测框
                x, y, w, h = bbox
                cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
                cv2.putText(frame, 'Detection', (x, y-10),
                           cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
        else:
            # 跟踪模式
            success, bbox = self.update_tracker(frame)
            if success:
                x, y, w, h = [int(v) for v in bbox]
                cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
                cv2.putText(frame, 'Tracking', (x, y-10),
                           cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
            else:
                # 跟踪失败,下一帧重新检测
                self.tracking = False

        return frame

# 主程序
def main():
    cap = cv2.VideoCapture(0)
    tracker = FaceTracker()

    while True:
        ret, frame = cap.read()
        if not ret:
            break

        # 处理帧
        processed_frame = tracker.process_frame(frame)

        # 显示
        cv2.imshow('Face Tracking', processed_frame)

        # 按'q'退出
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()

if __name__ == '__main__':
    main()

代码解析

组件 功能 技术点
Haar检测 快速定位人脸 detectMultiScale参数调优
CSRT跟踪 高精度跟踪 抗遮挡、尺度自适应
状态切换 检测→跟踪→检测 跟踪失败自动回退

运行效果:打开摄像头后,首先检测到人脸用绿色框标注,然后转为蓝色框跟踪;如果人脸移出画面或遮挡,系统自动重新检测。

从OpenCV开始,走进视觉世界

今天,我们学习了OpenCV最核心的基础知识:

模块 核心函数 应用场景
图像I/O imread, imshow, imwrite 所有项目的起点
颜色空间 cvtColor, inRange 颜色提取、目标追踪
几何变换 warpAffine, warpPerspective 图像矫正、数据增强
滤波 blur, Sobel, Canny 去噪、边缘检测
视频 VideoCapture, VideoWriter 实时处理、视频分析
检测跟踪 CascadeClassifier, Tracker 人脸识别、目标追踪

OpenCV最大的价值:它不是黑箱,而是工具箱——你清楚地知道每个函数在做什么,并且可以组合它们解决实际问题。这篇文章只是帮你打开了 计算机视觉 这扇大门,想了解更多实战项目和进阶技巧,欢迎在 云栈社区 继续探索和交流。




上一篇:Claude Code远程控制功能实战指南:实现移动端跨设备开发
下一篇:深入理解Kubernetes架构与核心机制:从Pod到Service的实战部署指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-26 17:33 , Processed in 0.487165 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表