当前位置:首页>python>一天一个Python知识点——Day 156:OpenCV基础

一天一个Python知识点——Day 156:OpenCV基础

  • 2026-09-26 22:04:52
一天一个Python知识点——Day 156:OpenCV基础

昨天我们学习了计算机视觉的理论基础——知道了图像在计算机眼中是数字矩阵,理解了卷积如何提取特征,也看到了CNN如何一步步学会“看”。

但问题来了:在实际项目中,你怎么把一张照片读进Python?怎么把彩色图转成灰度?怎么检测人脸?

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

OpenCV是什么?
OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉库,包含2500+优化算法,涵盖图像处理、视频分析、目标检测、机器学习等几乎所有视觉任务。
它就像视觉工程师的“瑞士军刀”——你遇到的大多数图像处理问题,OpenCV都有现成函数。

OpenCV的安装与核心理念

安装OpenCV

# 标准安装(包含主要模块)pip install opencv-python# 如果需要额外模块(如跟踪算法)pip install opencv-contrib-python# 验证安装import cv2print(f"OpenCV版本: {cv2.__version__}")# 应输出 4.5.x 或更高版本

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

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

import cv2import 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)  # 保存为JPEGcv2.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 cv2import numpy as np# 读取图像img = cv2.imread('colorful_balls.jpg')# 转换为HSVhsv = 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_CUBICresized = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)

平移:cv2.warpAffine()

# 定义平移矩阵 [1, 0, tx; 0, 1, ty]tx, ty = 50, 100  # 向右移50,向下移100M = 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 cv2class 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最大的价值:它不是黑箱,而是工具箱——你清楚地知道每个函数在做什么,并且可以组合它们解决实际问题。

最新文章

随机文章