计算机视觉是人工智能领域最热门的方向之一,而 OpenCV(Open Source Computer Vision Library)无疑是这个领域最通用、最强大的开源工具库。无论你是想做图像识别、视频处理、目标检测还是工业视觉项目,OpenCV 都是绕不开的基础。

一、OpenCV 简介

OpenCV 是 1999 年由 Intel 发起、现由 OpenCV 基金会维护的开源跨平台计算机视觉库,核心采用 C/C++ 编写,同时提供 Python、Java 等多种语言接口,完美支持 Windows、Linux、macOS、Android、iOS 全平台。它基于 Apache 2.0 协议开源,商业与非商业项目均可免费使用,是工业界和学术界的首选计算机视觉工具。

二、OpenCV 安装与验证

2.1 安装命令(Python 环境)

确保你的 Python 版本在 3.6 及以上,在终端执行以下命令即可完成安装:

# 安装基础版OpenCV(包含所有核心功能)
pip install opencv-python           

# 安装扩展版OpenCV(基础版+高级扩展算法,推荐安装)
pip install opencv-contrib-python    

2.2 验证安装

安装完成后,运行以下代码验证是否安装成功,若输出版本号则说明安装正常:

import cv2
print(cv2.__version__)

三、基础 IO 操作:图像与视频读写

3.1 图像读取与显示

这是 OpenCV 最基础的操作,所有图像处理都从读取图像开始:

import cv2  # 导入OpenCV

# 1. 读取图像(路径替换为你自己的图片路径)
img = cv2.imread("bus.jpg")

# 2. 显示图像(窗口名, 图像数据)
cv2.imshow("My Image", img)

# 3. 等待按键(0=无限等待,直到按任意键关闭窗口)
cv2.waitKey(0)

# 4. 关闭所有OpenCV窗口,释放资源
cv2.destroyAllWindows()

3.2 视频读写与摄像头调用

OpenCV 不仅能处理静态图像,还能轻松处理视频流和摄像头实时画面:

# 导入OpenCV图像处理库
import cv2

# 打开指定路径的视频文件,获取视频操作句柄
# 若要打开电脑默认摄像头,将路径改为0即可:cv2.VideoCapture(0)
cap = cv2.VideoCapture("街头行人测试.mp4")

# ========== 获取原视频基础参数,用于匹配保存规格 ==========
# 获取视频画面宽度并转为整型
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
# 获取视频画面高度并转为整型
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 获取视频每秒帧数(帧率)
fps = int(cap.get(cv2.CAP_PROP_FPS))

# 定义MP4格式视频编码器,适配Windows系统
fourcc = cv2.VideoWriter_fourcc(*'mp4v')

# 创建视频写入对象,设置保存名称、编码、帧率、画面尺寸
out = cv2.VideoWriter("output.mp4", fourcc, fps, (width, height))

# ========== 循环逐帧读取、处理并保存视频 ==========
while True:
    # 读取单帧画面,ret标记是否读取成功,frame为读取到的图像帧
    ret, frame = cap.read()

    # 读取失败说明视频播放完毕,跳出循环
    if not ret:
        break

    # 将当前帧写入新视频文件
    out.write(frame)

    # 弹窗实时展示当前视频画面
    cv2.imshow("Recording", frame)

    # 等待1毫秒按键响应,按下q键退出播放录制
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 释放视频读取资源
cap.release()
# 释放视频保存资源,完成文件写入
out.release()
# 关闭所有OpenCV弹出窗口
cv2.destroyAllWindows()

# 控制台提示保存完成
print("✅ 视频已保存为 output.mp4")

四、核心功能一:色彩空间转换

色彩空间是图像处理的基础,不同的色彩空间适用于不同的处理场景。OpenCV 默认使用 BGR 色彩空间,这与我们常见的 RGB 顺序相反,这是初学者最容易踩的坑之一。

4.1 三种最常用的色彩空间

  • BGR(OpenCV 默认):蓝绿红通道顺序,不影响图像显示,但会影响颜色处理相关操作
  • GRAY(灰度图):只有黑白灰三种颜色,数据量小、计算速度快,人脸识别、边缘检测、轮廓提取等操作必须使用灰度图
  • HSV:由色调(H,代表颜色种类)、饱和度(S,代表颜色浓淡)、亮度(V,代表明暗)组成,专门用于提取特定颜色的物体

4.2 色彩空间转换代码

import cv2

# ================== 1. 读取图像 ==================
# 读取图片(OpenCV默认是BGR格式)
img = cv2.imread("bus.jpg")

# ================== 2. BGR → 灰度图 GRAY ==================
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# ================== 3. BGR → HSV ==================
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# ================== 4. 分割HSV三个通道(方便理解) ==================
h, s, v = cv2.split(hsv)

# ================== 5. 显示所有结果 ==================
cv2.imshow("Original BGR", img)        # 原图
cv2.imshow("Gray", gray)        # 灰度图
cv2.imshow("HSV", hsv)              # HSV图
cv2.imshow("Hue", h)               # 色调通道
cv2.imshow("Saturation", s)      # 饱和度通道
cv2.imshow("Value", v)            # 亮度通道

# 等待按键关闭窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

4.3 实战:提取图像中的绿色物体

利用 HSV 色彩空间的特性,我们可以精准提取特定颜色的物体:

import cv2
import numpy as np

# 读取图片
img = cv2.imread("bus.jpg")

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

# 定义绿色范围(HSV)
lower_green = np.array([35, 43, 46])
upper_green = np.array([77, 255, 255])

# 提取绿色区域,生成掩码
mask = cv2.inRange(hsv, lower_green, upper_green)

# 只保留绿色部分
result = cv2.bitwise_and(img, img, mask=mask)

# 显示结果
cv2.imshow("Original", img)
cv2.imshow("Green Mask", mask)
cv2.imshow("Result", result)

cv2.waitKey(0)
cv2.destroyAllWindows()

五、核心功能二:图像阈值处理

阈值处理是像素二分类操作,通过设定一个分界阈值,将像素灰度值划分为黑白两类,常用于图像分割、前景提取、降噪预处理。OpenCV 默认处理灰度图,像素取值范围为 0~255(0 为纯黑,255 为纯白)。

5.1 固定阈值处理

核心函数

ret, dst = cv2.threshold(src, thresh, maxval, type)

参数说明

  • src:输入灰度图像
  • thresh:设定的阈值分界值
  • maxval:超出阈值后赋予的最大值,一般设为 255
  • type:阈值处理模式
    • THRESH_BINARY:大于阈值设为 255,小于设为 0
    • THRESH_BINARY_INV:反向二值化,大于阈值设为 0,小于设为 255
    • THRESH_TRUNC:超阈值像素截断为阈值,其余不变
    • THRESH_TOZERO:大于阈值保留原值,小于置 0
    • THRESH_TOZERO_INV:小于阈值保留原值,大于置 0

演示代码

import cv2

# 1. 读取图片 + 转灰度(必须灰度图!)
img = cv2.imread("bus.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转灰度

# 2. 演示 5 种最常用的阈值类型
# 统一用:阈值127,最大值255

# ------------------
# 1) 基本二值化 THRESH_BINARY
# 规则:>127 → 255(白),≤127 → 0(黑)
# ------------------
ret1, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# ------------------
# 2) 反二值化 THRESH_BINARY_INV
# 规则:>127 → 0(黑),≤127 →255(白)
# ------------------
ret2, binary_inv = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)

# ------------------
# 3) 截断阈值 THRESH_TRUNC
# 规则:>127 → 变成127,≤127 → 保持不变
# ------------------
ret3, trunc = cv2.threshold(gray, 127, 255, cv2.THRESH_TRUNC)

# ------------------
# 4) 阈值置0 THRESH_TOZERO
# 规则:≤127 → 变成0,>127 → 保持不变
# ------------------
ret4, tozero = cv2.threshold(gray, 127, 255, cv2.THRESH_TOZERO)

# ------------------
# 5) 反阈值置0 THRESH_TOZERO_INV
# 规则:>127 → 变成0,≤127 → 保持不变
# ------------------
ret5, tozero_inv = cv2.threshold(gray, 127, 255, cv2.THRESH_TOZERO_INV)


# 3. 显示所有效果
cv2.imshow("Gray Origin", gray)
cv2.imshow("THRESH_BINARY", binary)
cv2.imshow("THRESH_BINARY_INV", binary_inv)
cv2.imshow("THRESH_TRUNC", trunc)
cv2.imshow("THRESH_TOZERO", tozero)
cv2.imshow("THRESH_TOZERO_INV", tozero_inv)

cv2.waitKey(0)
cv2.destroyAllWindows()

5.2 自适应阈值处理

固定阈值受光线影响大,自适应阈值会根据局部区域亮度自动计算阈值,完美适配明暗差异较大的画面。

核心函数

dst = cv2.adaptiveThreshold(src, maxval, adaptiveMethod, thresholdType, blockSize, C)

参数说明

  • src:输入灰度图像(必须是灰度图)
  • maxval:满足条件后赋予的最大值,一般设为 255
  • adaptiveMethod:自适应阈值计算方法
    • ADAPTIVE_THRESH_GAUSSIAN_C:根据周围像素高斯加权计算阈值
    • ADAPTIVE_THRESH_MEAN_C:根据周围像素平均值计算阈值
  • thresholdType:阈值处理模式(只能选二值化相关)
    • THRESH_BINARY:大于局部阈值设为 255,小于设为 0
    • THRESH_BINARY_INV:反向二值化,大于局部阈值设为 0,小于设为 255
  • blockSize:局部计算区域大小,必须是奇数(3,5,7,9...)
  • C:从局部均值中减去的常数,用于微调结果

演示代码

import cv2

img = cv2.imread("bus.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 自适应阈值处理
# 块大小11,差值常数2
adap = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11,2)

cv2.imshow("Original Gray", gray)
cv2.imshow("Adaptive Threshold", adap)
cv2.waitKey(0)
cv2.destroyAllWindows()

六、核心功能三:滤波与平滑

滤波与平滑的作用是消除图像噪点、弱化突兀像素,让画面更柔和,是图像预处理中最常用的操作之一。

6.1 四种常用滤波方法

滤波方法 核心函数 原理 适用场景
均值滤波 cv2.blur(src, ksize) 取核内所有像素平均值作为当前像素值 通用简单降噪,运算速度快
高斯滤波 cv2.GaussianBlur(src, ksize, sigmaX) 周边像素加权平均,距离中心越近权重越高 日常图像平滑,画面过渡柔和
中值滤波 cv2.medianBlur(src, ksize) 取核内像素灰度中间值替换当前像素 去除黑白斑点类椒盐噪声
双边滤波 cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace) 兼顾像素距离与色彩差异 人像美颜、物体降噪,保留边缘细节

6.2 滤波效果演示代码

import cv2

# 读取原图
img = cv2.imread("bus.jpg")

# 均值滤波
blur_img = cv2.blur(img, (5,5))
# 高斯滤波
gauss_img = cv2.GaussianBlur(img, (5,5), 0)
# 中值滤波
median_img = cv2.medianBlur(img, 5)
# 双边滤波
bilat_img = cv2.bilateralFilter(img, 5, 75, 75)

# 窗口展示
cv2.imshow("Original", img)
cv2.imshow("Blur", blur_img)
cv2.imshow("GaussianBlur", gauss_img)
cv2.imshow("MedianBlur", median_img)
cv2.imshow("BilateralFilter", bilat_img)

cv2.waitKey(0)
cv2.destroyAllWindows()

七、核心功能四:边缘检测

边缘检测的作用是识别图像中像素灰度突变的区域,提取物体的轮廓边界,常用于目标分割、轮廓查找、特征提取。

7.1 四种常用边缘检测算子

算子 核心函数 特点 适用场景
Sobel cv2.Sobel(src, ddepth, dx, dy, ksize) 一阶导数算子,可分别检测 X、Y 方向边缘 常规边缘提取,计算速度快
Scharr cv2.Scharr(src, ddepth, dx, dy) Sobel 的增强版,精度更高 精细弱边缘检测
Laplacian cv2.Laplacian(src, ddepth, ksize) 二阶导数算子,同时检测所有方向边缘 轮廓拐点、纹理边界识别
Canny cv2.Canny(src, threshold1, threshold2) 多阶段检测算法,效果最优 工业检测、轮廓定位,边缘检测首选

7.2 边缘检测完整演示代码

# 导入OpenCV库,用于图像处理
import cv2

# 读取本地图像文件 
img = cv2.imread("bus.jpg")

# 将彩色图像转换为灰度图像
# 边缘检测算法必须在灰度图上运行
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# ==================== Sobel 边缘检测 ====================
# 计算 X 方向边缘(垂直边缘)
# cv2.CV_64F:使用64位浮点型,防止梯度值为负时被截断
# dx=1, dy=0:只对X方向求导,检测竖边
sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)

# 计算 Y 方向边缘(水平边缘)
# dy=1, dx=0:只对Y方向求导,检测横边
sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)

# 将负数梯度转为正数(图像只能显示0~255)
sobel_x = cv2.convertScaleAbs(sobel_x)
sobel_y = cv2.convertScaleAbs(sobel_y)

# 将 X 和 Y 方向的边缘图加权融合,得到完整边缘
sobel_all = cv2.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0)

# ==================== Scharr 边缘检测 ====================
# Scharr 是 Sobel 的增强版,精度更高,检测更精细
scharr_x = cv2.Scharr(gray, cv2.CV_64F, 1, 0)
scharr_y = cv2.Scharr(gray, cv2.CV_64F, 0, 1)

# 同样转为绝对值
scharr_x = cv2.convertScaleAbs(scharr_x)
scharr_y = cv2.convertScaleAbs(scharr_y)

# 融合 X、Y 方向
scharr_all = cv2.addWeighted(scharr_x, 0.5, scharr_y, 0.5, 0)

# ==================== Laplacian 边缘检测 ====================
# 二阶导数算子,同时检测所有方向边缘
laplacian = cv2.Laplacian(gray, cv2.CV_64F, ksize=3)

# 转为可显示的图像
laplacian = cv2.convertScaleAbs(laplacian)

# ==================== Canny 边缘检测(最常用、最优) ====================
# 50:低阈值
# 150:高阈值
# 低于50舍弃,高于150保留,中间值仅与强边缘相连时保留
canny = cv2.Canny(gray, 50, 150)

# ==================== 显示所有结果 ====================
cv2.imshow("Original Gray", gray)    # 原始灰度图
cv2.imshow("Sobel", sobel_all)        # Sobel边缘
cv2.imshow("Scharr", scharr_all)      # Scharr边缘
cv2.imshow("Laplacian", laplacian)    # Laplacian边缘
cv2.imshow("Canny", canny)            # Canny边缘(效果最好)

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

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

八、核心功能五:形态学操作

形态学操作是基于形状处理图像的技术,专门针对二值图(黑白图)进行操作,用于去噪点、连接断裂区域、补空洞、细化 / 粗化轮廓,是图像处理必备的预处理步骤。

8.1 核心函数与结构元创建

核心形态学函数

dst = cv2.morphologyEx(src, op, kernel)
  • src:输入二值图像
  • op:形态学操作类型
  • kernel:结构元(卷积核)

结构元创建函数

kernel = cv2.getStructuringElement(shape, ksize)
  • shape:结构元形状
    • cv2.MORPH_RECT:矩形(最常用)
    • cv2.MORPH_ELLIPSE:椭圆形
    • cv2.MORPH_CROSS:十字形
  • ksize:核大小,如 (5,5),核越大处理效果越明显

8.2 六种形态学操作详解

  • 腐蚀(Erosion)

    • 函数:dst = cv2.erode(src, kernel)
    • 作用:白色区域变细、变小
    • 用途:去除小白噪点、分离粘连的物体
  • 膨胀(Dilation)

    • 函数:dst = cv2.dilate(src, kernel)
    • 作用:白色区域变粗、变大
    • 用途:连接断裂的区域、填补小洞
  • 开运算(Opening)

    • 函数:dst = cv2.morphologyEx(src, cv2.MORPH_OPEN, kernel)
    • 原理:先腐蚀 → 后膨胀
    • 用途:去除白色噪点,不改变主体大小
  • 闭运算(Closing)

    • 函数:dst = cv2.morphologyEx(src, cv2.MORPH_CLOSE, kernel)
    • 原理:先膨胀 → 后腐蚀
    • 用途:填充黑色小孔洞,连接邻近的物体
  • 形态学梯度(Gradient)

    • 函数:dst = cv2.morphologyEx(src, cv2.MORPH_GRADIENT, kernel)
    • 原理:膨胀图 - 腐蚀图
    • 用途:提取物体的轮廓边缘
  • 顶帽 / 黑帽(Top Hat/Black Hat)

    • 顶帽:tophat = cv2.morphologyEx(src, cv2.MORPH_TOPHAT, kernel)
      • 原理:原图 - 开运算
      • 用途:提取小白噪点和亮细微线条
    • 黑帽:blackhat = cv2.morphologyEx(src, cv2.MORPH_BLACKHAT, kernel)
      • 原理:闭运算 - 原图
      • 用途:提取小黑孔洞和暗细微线条

8.3 形态学操作完整演示代码

# 导入OpenCV库,用于图像处理
import cv2
# 导入numpy库,用于数值计算
import numpy as np

# ==================== 1. 图像预处理 ====================
# 读取本地图片 
img = cv2.imread("bus.jpg")

# 将默认的BGR彩色图像转换为灰度图像
# 因为阈值处理和形态学操作通常只针对灰度图/二值图进行
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化处理(生成黑白图)
# 像素值 > 127 的设为 255 (白色)
# 像素值 <= 127 的设为 0 (黑色)
# binary 是输出的二值图像,ret 是使用的阈值(127)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# ==================== 2. 创建核(结构元素) ====================
# 创建形态学操作需要的“刷子”
# MORPH_RECT 表示矩形刷子,尺寸为 5x5
# 刷子越大,处理效果越明显
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))

# ==================== 3. 基础形态学操作 ====================

# 腐蚀操作
# 效果:白色区域变小、变细,用于**去除小白噪点**
erosion = cv2.erode(binary, kernel)

# 膨胀操作
# 效果:白色区域变大、变粗,用于**连接断裂的线条**
dilation = cv2.dilate(binary, kernel)

# 开运算
# 原理:先腐蚀,后膨胀
# 效果:去除图像外围的小白噪点,**不破坏主体形状**
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)

# 闭运算
# 原理:先膨胀,后腐蚀
# 效果:填充图像内部的小黑孔洞,**连接邻近的物体**
closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)

# 形态学梯度
# 原理:膨胀图 - 腐蚀图
# 效果:提取出物体的**边缘轮廓**
gradient = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel)

# ==================== 4. 顶帽 & 黑帽操作 ====================

# 顶帽 (Top Hat)
# 原理:原图 - 开运算
# 效果:提取出**被去掉的小白噪点**和**亮细微线条**
tophat = cv2.morphologyEx(binary, cv2.MORPH_TOPHAT, kernel)

# 黑帽 (Black Hat)
# 原理:闭运算 - 原图
# 效果:提取出**被填充的小黑孔洞**和**暗细微线条**
blackhat = cv2.morphologyEx(binary, cv2.MORPH_BLACKHAT, kernel)

# ==================== 5. 显示所有结果窗口 ====================
cv2.imshow("Binary", binary)        # 原始二值图
cv2.imshow("Erosion", erosion)      # 腐蚀效果图
cv2.imshow("Dilation", dilation)    # 膨胀效果图
cv2.imshow("Opening", opening)      # 开运算效果图
cv2.imshow("Closing", closing)      # 闭运算效果图
cv2.imshow("Gradient", gradient)    # 梯度(轮廓)效果图
cv2.imshow("TopHat", tophat)        # 顶帽效果图(显示白点)
cv2.imshow("BlackHat", blackhat)    # 黑帽效果图(显示黑点)

# 等待按下任意按键,程序暂停
cv2.waitKey(0)

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

九、核心功能六:轮廓检测

轮廓检测是在二值图中找出所有物体的边缘形状,进而获取每个物体的坐标、大小、周长、面积等信息,广泛应用于物体计数、形状识别、目标定位等场景。

9.1 核心函数

查找轮廓

contours, hierarchy = cv2.findContours(src, mode, method)
  • src:输入二值图像(黑白图)
  • mode:轮廓检索模式
    • cv2.RETR_EXTERNAL:只查找最外层轮廓(最常用)
    • cv2.RETR_LIST:查找所有轮廓,不建立层级关系
    • cv2.RETR_TREE:查找所有轮廓,建立完整层级关系
  • method:轮廓近似方法
    • cv2.CHAIN_APPROX_SIMPLE:压缩轮廓点,只保留关键点(最常用)
    • cv2.CHAIN_APPROX_NONE:保存所有轮廓点
  • 返回值
    • contours:所有轮廓的点集(列表形式)
    • hierarchy:轮廓层级信息(一般不用)

绘制轮廓

cv2.drawContours(img, contours, contourIdx, color, thickness)
  • img:要绘制轮廓的图像
  • contours:查找到的轮廓列表
  • contourIdx:要绘制的轮廓索引(-1 表示绘制全部轮廓)
  • color:轮廓颜色,BGR 格式元组,如 (0,0,255) 表示红色
  • thickness:轮廓线条粗细

9.2 常用轮廓工具

 

# 计算轮廓面积
area = cv2.contourArea(cnt)

# 计算轮廓周长(第二个参数True表示闭合轮廓)
perimeter = cv2.arcLength(cnt, True)

# 获取轮廓外接矩形(返回左上角坐标x,y和宽高w,h)
x, y, w, h = cv2.boundingRect(cnt)
# 绘制外接矩形
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)

9.3 轮廓检测演示代码

import cv2
# ==================== 1. 读取 + 预处理 ====================
# 读取图片
img = cv2.imread("bus.jpg")

# 复制原图,用来画轮廓(防止破坏原图)
img_copy = img.copy()

# 转灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化(必须二值图才能找轮廓)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# ==================== 2. 查找轮廓 ====================
# 寻找轮廓
# RETR_EXTERNAL:只找最外层
# CHAIN_APPROX_SIMPLE:压缩轮廓点
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 输出找到多少个轮廓
print("检测到的轮廓数量:", len(contours))

# ==================== 3. 绘制轮廓 ====================
# 在复制的图上绘制所有轮廓
# -1:绘制全部轮廓
# (0,0,255):红色
# 2:线条粗细
cv2.drawContours(img_copy, contours, -1, (0, 0, 255), 2)

# ==================== 4. 显示结果 ====================
cv2.imshow("Binary", binary)
cv2.imshow("Contours", img_copy)

cv2.waitKey(0)
cv2.destroyAllWindows()

十、核心功能七:几何变换

几何变换是对图像的位置、尺寸、角度进行形变处理,常用的操作包括缩放、平移、旋转、翻转和仿射变换。

10.1 五种常用几何变换

1.图像缩放

dst = cv2.resize(src, dsize, fx, fy, interpolation)

参数:

  • src:输入图像
  • dsize:目标尺寸 (宽,高)
  • fx:水平缩放比例
  • fy:垂直缩放比例
  • interpolation:插值算法(默认即可)

2.图像平移

dst = cv2.warpAffine(src, M, dsize)

参数:

  • src:输入图像
  • M:2 行 3 列的平移变换矩阵
  • dsize:输出图像尺寸

3.图像旋转

#旋转矩阵生成
M = cv2.getRotationMatrix2D(center, angle, scale)

#执行旋转
dst = cv2.warpAffine(src, M, dsize)

参数:

  • center:旋转中心点坐标
  • angle:旋转角度(正数逆时针,负数顺时针)
  • scale:缩放倍数

4.图像翻转

dst = cv2.flip(src, flipCode)

参数:

  • flipCode=0:竖直上下翻转
  • flipCode=1:水平左右翻转
  • flipCode=-1:同时上下 + 左右翻转

5.仿射变换

#变换矩阵生成
M = cv2.getAffineTransform(pts1, pts2)

#执行变换
dst = cv2.warpAffine(src, M, dsize)

参数:

  • pts1:原图中的 3 个基准点
  • pts2:变换后对应的 3 个目标点

10.2 几何变换完整演示代码

# 导入OpenCV图像处理库
import cv2
# 导入numpy数值计算库,用于生成变换矩阵
import numpy as np

# 读取本地图像文件 bus.jpg
img = cv2.imread("bus.jpg")
# 获取图像的高度(height)和宽度(width)
h, w = img.shape[:2]

# ==================== 1. 图像缩放 ====================
# 固定尺寸缩放:直接指定宽度300,高度200
resize1 = cv2.resize(img, (300, 200))
# 按比例缩放:fx=0.5 宽度缩小一半,fy=0.5 高度缩小一半
resize2 = cv2.resize(img, None, fx=0.5, fy=0.5)

# ==================== 2. 图像平移 ====================
# 构建平移矩阵(必须是float32类型)
# [1,0,50] 表示向右移动50像素
# [0,1,30] 表示向下移动30像素
move_mat = np.float32([[1, 0, 50], [0, 1, 30]])
# 执行仿射变换实现平移,输出尺寸保持原图大小
move_img = cv2.warpAffine(img, move_mat, (w, h))

# ==================== 3. 图像旋转 ====================
# 构建旋转矩阵
# (w/2, h/2):以图像中心为旋转点
# 45:旋转角度(正数逆时针,负数顺时针)
# 1:缩放比例为1倍(不放大不缩小)
rot_mat = cv2.getRotationMatrix2D((w/2, h/2), 45, 1)
# 执行旋转,输出尺寸保持原图大小
rot_img = cv2.warpAffine(img, rot_mat, (w, h))

# ==================== 4. 图像翻转 ====================
flip_up = cv2.flip(img, 0)    # flipCode=0 → 上下垂直翻转
flip_lr = cv2.flip(img, 1)    # flipCode=1 → 左右水平翻转
flip_all = cv2.flip(img, -1)  # flipCode=-1 → 上下+左右同时翻转

# ==================== 5. 仿射变换 ====================
# 定义原图中的3个基准点
pts1 = np.float32([[50,50],[200,50],[50,200]])
# 定义变换后对应的3个目标点
pts2 = np.float32([[10,100],[200,80],[100,250]])
# 根据3组点计算仿射变换矩阵
affine_mat = cv2.getAffineTransform(pts1, pts2)
# 执行仿射变换
affine_img = cv2.warpAffine(img, affine_mat, (w, h))

# ==================== 显示所有结果 ====================
cv2.imshow("Original", img)        # 原始图像
cv2.imshow("Resize Fixed", resize1) # 固定尺寸缩放
cv2.imshow("Resize Scale", resize2) # 比例缩放
cv2.imshow("Move", move_img)        # 平移效果
cv2.imshow("Rotate", rot_img)       # 旋转效果
cv2.imshow("Flip Up", flip_up)      # 上下翻转
cv2.imshow("Flip LR", flip_lr)      # 左右翻转
cv2.imshow("Affine", affine_img)    # 仿射变换

# 等待按下任意按键
cv2.waitKey(0)
# 关闭所有OpenCV窗口
cv2.destroyAllWindows()

10.3 应用场景

  • 缩放:统一图像尺寸、压缩画面大小
  • 平移:调整目标在画面中的位置
  • 旋转:修正倾斜拍摄的图像
  • 翻转:数据集增广、镜像画面处理
  • 仿射:矫正图像形变、透视基础调整

十一、核心功能八:图像绘制

OpenCV 提供了丰富的绘制函数,可以在图像上绘制几何图形和文字标注,常用于框选目标、标记点位、添加说明信息。

11.1 常用绘制函数

1.绘制直线

cv2.line(img, pt1, pt2, color, thickness)
  • pt1:线段起点坐标
  • pt2:线段终点坐标

2.绘制矩形

cv2.rectangle(img, pt1, pt2, color, thickness)
  • pt1:矩形左上角坐标
  • pt2:矩形右下角坐标
  • thickness=-1时表示填充矩形

3.绘制圆形

cv2.circle(img, center, radius, color, thickness)
  • center:圆心坐标
  • radius:圆形半径

4.绘制多边形

cv2.polylines(img, pts, isClosed, color, thickness)
  • pts:多边形顶点坐标数组
  • isClosed=True表示闭合图形,False 表示开放线段

5.绘制文字

cv2.putText(img, text, org, font, scale, color, thickness)
  • text:要显示的文本内容
  • org:文字左下角的起始坐标
  • font:字体样式
  • scale:文字缩放大小

11.2 绘制功能演示代码

import cv2
import numpy as np

# 创建空白画布,高度500、宽度600,3通道彩色图
canvas = np.zeros((500, 600, 3), np.uint8)

# 1. 绘制直线
# 起点(50,50),终点(550,50),红色线条,粗细2
cv2.line(canvas, (50, 50), (550, 50), (0, 0, 255), 2)

# 2. 绘制矩形
# 左上角(50,100),右下角(250,250),绿色边框,粗细2
cv2.rectangle(canvas, (50, 100), (250, 250), (0, 255, 0), 2)
# 实心填充矩形,厚度设为-1
cv2.rectangle(canvas, (300, 100), (500, 250), (255, 0, 0), -1)

# 3. 绘制圆形
# 圆心(150,350),半径60,黄色边框
cv2.circle(canvas, (150, 350), 60, (0, 255, 255), 2)

# 4. 绘制多边形
# 定义四个顶点坐标
points = np.array([[350, 300], [450, 280], [500, 400], [380, 450]], np.int32)
cv2.polylines(canvas, [points], True, (255, 255, 255), 2)

# 5. 绘制文字
# 文字起始坐标(50,480),字体、大小、白色文字
cv2.putText(canvas, "Draw Test", (50, 480), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,255,255), 2)

# 展示绘图结果
cv2.imshow("Draw Board", canvas)

cv2.waitKey(0)
cv2.destroyAllWindows()

十二、进阶技巧

12.1 ROI 感兴趣区域

ROI(Region of Interest)即感兴趣区域,通过截取图像的局部区域进行单独处理,可以大幅减少运算量,同时精准聚焦目标位置。

截取原理

利用图像数组的行列索引进行裁剪,格式为:

roi = 原图[y起始:y结束, x起始:x结束]

注意:y 对应图像的纵向高度,x 对应图像的横向宽度。

演示代码

import cv2

img = cv2.imread("bus.jpg")
# 截取纵向100~300,横向200~400的区域
roi = img[100:300, 200:400]

cv2.imshow("Original", img)
cv2.imshow("ROI Area", roi)
cv2.waitKey(0)
cv2.destroyAllWindows()

12.2 模板匹配

模板匹配是在大图中查找与小模板图像相似的目标,实现物体的定位与检索。

核心函数

# 执行模板匹配
res = cv2.matchTemplate(image, templ, method)
# 获取匹配结果的极值和对应坐标
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

参数说明

  • image:待搜索的大图
  • templ:匹配用的小模板图
  • method:匹配计算方式
    • TM_SQDIFF:差值平方匹配,数值越小相似度越高
    • TM_CCORR:相关性匹配,数值越大相似度越高
    • TM_CCOEFF:系数匹配,数值越大相似度越高

演示代码

# 导入OpenCV库,用于图像处理
import cv2

# ==================== 1. 读取图像 ====================
# 读取待检测的大图(在这张图里找目标)
img = cv2.imread("bus.jpg")
# 读取模板小图(要查找的目标小图片)
template = cv2.imread("template.jpg")

# 获取模板图片的 高度h 和 宽度w
h, w = template.shape[:2]

# ==================== 2. 执行模板匹配 ====================
# 调用OpenCV模板匹配函数
# img: 原始大图
# template: 模板小图
# cv2.TM_SQDIFF: 匹配算法(差值平方匹配,值越小越相似)
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)

# 获取匹配结果中的最大值、最小值,以及对应的坐标位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

# ==================== 3. 确定目标位置 ====================
# TM_SQDIFF算法:取最小值位置作为最佳匹配点(左上角坐标)
top_left = min_loc

# 计算匹配区域的右下角坐标
# 右下角X = 左上角X + 模板宽度
# 右下角Y = 左上角Y + 模板高度
bottom_right = (top_left[0] + w, top_left[1] + h)

# ==================== 4. 绘制矩形框标记目标 ====================
# 在原图上画矩形框标出找到的目标
# (0,0,255) 表示红色框,2 表示线条粗细
cv2.rectangle(img, top_left, bottom_right, (0, 0, 255), 2)

# ==================== 5. 显示结果 ====================
# 显示模板小图
cv2.imshow("Template", template)
# 显示标记好目标的结果图
cv2.imshow("Match Result", img)

# 等待按下任意按键后关闭窗口
cv2.waitKey(0)
# 释放所有OpenCV窗口
cv2.destroyAllWindows()

12.3 特征检测(ORB / SIFT / SURF)

特征检测是找到图像中稳定、独特、不会因旋转、缩放、光照变化而消失的关键点,并生成描述子,广泛应用于图像匹配、目标识别、全景拼接、定位导航等领域。

三种主流特征检测算法对比

算法 类型 速度 专利状态 推荐使用
SIFT 尺度不变 有专利 精度高但不能商用
SURF 加速版 SIFT 有专利 较少使用
ORB 二进制描述子 极快 无专利 最常用、首选

ORB 特征检测与匹配演示代码

# 导入OpenCV图像处理库
import cv2

# ====================== 1. 读取原图 ======================
# 读取本地图像文件bus.jpg,默认BGR彩色格式
img = cv2.imread("bus.jpg")

# 获取图像的高度(h)和宽度(w)
# img.shape返回(高度, 宽度, 通道数),[:2]取前两个维度
h, w = img.shape[:2]

# ====================== 2. 生成灰度图 ======================
# 将彩色图转为灰度图
# COLOR_BGR2GRAY:BGR转灰度,减少计算量,特征检测常用灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# ====================== 3. 生成 旋转+缩放 后的图像 ======================
# 构建旋转缩放变换矩阵
# 参数1:旋转中心(w//2, h//2),即图像正中心
# 参数2:旋转角度30度(正数逆时针)
# 参数3:缩放比例0.8,缩小为原图80%
M = cv2.getRotationMatrix2D((w//2, h//2), 30, 0.8)

# 执行仿射变换,生成旋转缩放后的图像
# 参数1:原图;参数2:变换矩阵;参数3:输出图像尺寸(宽,高)
img_rot = cv2.warpAffine(img, M, (w, h))

# 将旋转缩放后的彩色图也转为灰度图,用于特征检测
gray_rot = cv2.cvtColor(img_rot, cv2.COLOR_BGR2GRAY)

# ====================== 4. ORB 特征检测 ======================
# 创建ORB特征检测器
# 参数200:最多检测200个特征点,可调整数量
orb = cv2.ORB_create(200)

# 在原始灰度图上检测关键点并计算描述子
# 参数1:灰度图;参数2:掩码(None表示全图检测)
# 返回值kp1:关键点列表;des1:对应描述子矩阵
kp1, des1 = orb.detectAndCompute(gray, None)

# 在旋转缩放后的灰度图上检测关键点并计算描述子
kp2, des2 = orb.detectAndCompute(gray_rot, None)

# ====================== 5. 特征匹配 ======================
# 创建暴力匹配器
# NORM_HAMMING:汉明距离,适合ORB二进制描述子
# crossCheck=True:交叉匹配,提高匹配精度,减少误匹配
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)

# 匹配两张图的描述子,返回匹配结果列表
matches = bf.match(des1, des2)

# 按匹配距离排序,距离越小匹配精度越高
# x.distance:单个匹配对的汉明距离
matches = sorted(matches, key=lambda x: x.distance)

# ====================== 6. 绘制匹配结果 ======================
# 绘制特征匹配连线图
# 参数1:图1;参数2:图1关键点;参数3:图2;参数4:图2关键点
# 参数5:前50个最佳匹配对;参数6:输出画布(None自动创建)
# flags:不绘制单独的关键点,只画匹配连线
img_result = cv2.drawMatches(
    gray, kp1,
    gray_rot, kp2,
    matches[:50],
    None,
    flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS
)

# ====================== 7. 显示结果 ======================
# 显示原始灰度图
cv2.imshow("Original Gray", gray)
# 显示旋转缩放后的灰度图
cv2.imshow("Rotated & Scaled", gray_rot)
# 显示ORB特征匹配结果图
cv2.imshow("ORB Feature Matching", img_result)

# 等待任意按键按下,暂停窗口
cv2.waitKey(0)
# 关闭所有OpenCV窗口,释放资源
cv2.destroyAllWindows()

12.4 摄像头标定

摄像头拍摄的图像会存在桶形或枕形畸变(边缘线条弯曲),通过摄像头标定可以校正这些畸变,同时还能求出像素与真实世界尺寸的对应关系,进而计算 3D 坐标。

核心作用

  • 消除镜头畸变,让弯曲的直线变直
  • 获取相机内参矩阵(焦距、主点坐标)
  • 获取相机外参(相机在空间中的位置和姿态)
  • 实现像素坐标到真实世界坐标(mm)的转换

常用工具

棋盘格标定板(最常用、最准确)

关键函数

查找棋盘格角点

ret, corners = cv2.findChessboardCorners(img, (cols, rows))
  • img:输入的棋盘格图像
  • (cols, rows):棋盘格的内角点数量(例如 8×6 的棋盘格,内角点为 (7,5))
  • 返回值:ret表示是否找到角点,corners是所有角点的像素坐标

亚像素精确化角点

cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)

用于提高角点检测的精度

摄像头标定

ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, w, None, None)

核心返回值:

  • mtx:相机内参矩阵(焦距、中心坐标)
  • dist:畸变系数(用于去畸变)
  • rvecs:旋转向量
  • tvecs:平移向量

图像去畸变

dst = cv2.undistort(img, mtx, dist, None, mtx)

输入原始畸变图像,输出校正后的正常图像

12.5 透视变换

透视变换可以将倾斜拍摄的图像矫正成俯视图或正视图,广泛应用于文档扫描、车牌识别、工业测量等场景。

原理

找到原图中的 4 个对应点(通常是目标物体的四个角)和目标图中的 4 个对应点,计算出透视变换矩阵 M,然后通过矩阵变换完成图像矫正。

核心函数

# 定义原图和目标图的4个对应点
pts1 = np.float32([左上, 右上, 左下, 右下])
pts2 = np.float32([[0,0], [w,0], [0,h], [w,h]])

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

# 执行透视矫正
dst = cv2.warpPerspective(img, M, (w, h))

总结

以上就是 OpenCV 从基础到进阶的全部核心操作,涵盖了计算机视觉项目中 90% 以上的常用功能。计算机视觉是一门实践性极强的学科,建议你将本文中的代码逐一运行,结合自己的图片和视频进行修改调试,在实践中加深理解。掌握了这些基础操作后,你就可以进一步学习目标检测、图像分割等高级计算机视觉技术,开启你的 AI 视觉开发之旅。

 

 

 

更多推荐