OpenCV 教程(python)
计算机视觉是人工智能领域最热门的方向之一,而 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:超出阈值后赋予的最大值,一般设为 255type:阈值处理模式THRESH_BINARY:大于阈值设为 255,小于设为 0THRESH_BINARY_INV:反向二值化,大于阈值设为 0,小于设为 255THRESH_TRUNC:超阈值像素截断为阈值,其余不变THRESH_TOZERO:大于阈值保留原值,小于置 0THRESH_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:满足条件后赋予的最大值,一般设为 255adaptiveMethod:自适应阈值计算方法ADAPTIVE_THRESH_GAUSSIAN_C:根据周围像素高斯加权计算阈值ADAPTIVE_THRESH_MEAN_C:根据周围像素平均值计算阈值
thresholdType:阈值处理模式(只能选二值化相关)THRESH_BINARY:大于局部阈值设为 255,小于设为 0THRESH_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 视觉开发之旅。
更多推荐
所有评论(0)