Python OpenCV 快速入门指南:从零开始掌握计算机视觉基础
计算机视觉(Computer Vision)是人工智能领域的一个重要分支,旨在让计算机“看懂”世界。而 OpenCV(Open Source Computer Vision Library)是目前最流行、功能最强大的开源计算机视觉库之一。它提供了 C++, Python, Java 等多种语言的接口,其中 Python 接口因其简洁易用、生态丰富而备受青睐。
本文将为你提供一份详尽的 Python OpenCV 快速入门指南,带你从安装开始,逐步掌握 OpenCV 的核心概念和基本操作,为你打开计算机视觉的大门。无论你是学生、开发者还是对图像处理感兴趣的爱好者,本文都将是你迈出第一步的坚实基础。
文章目录
- 引言:什么是 OpenCV?为什么选择 Python?
- 环境搭建:安装 Python 和 OpenCV
- 核心概念:图像表示与基本属性
- 图像是什么?(NumPy 数组)
- 颜色空间(BGR vs RGB, 灰度图)
- 坐标系统
- 基础操作:加载、显示和保存图像
- 读取图像:
cv2.imread()
- 显示图像:
cv2.imshow()
- 等待按键与关闭窗口:
cv2.waitKey()
,cv2.destroyAllWindows()
- 保存图像:
cv2.imwrite()
- 读取图像:
- 图像属性与像素操作
- 获取图像属性:形状、尺寸、数据类型
- 访问和修改像素值
- ROI (Region of Interest) 区域提取
- 在图像上绘制图形和文字
- 绘制直线:
cv2.line()
- 绘制矩形:
cv2.rectangle()
- 绘制圆形:
cv2.circle()
- 绘制文字:
cv2.putText()
- 绘制直线:
- 基本的图像处理技术
- 色彩空间转换:
cv2.cvtColor()
(BGR转灰度, BGR转HSV) - 图像阈值化:
cv2.threshold()
,cv2.adaptiveThreshold()
- 图像平滑/模糊:
cv2.GaussianBlur()
,cv2.medianBlur()
- 图像几何变换:
- 图像缩放:
cv2.resize()
- 图像平移:
cv2.warpAffine()
(需要构建变换矩阵) - 图像旋转:
cv2.getRotationMatrix2D()
,cv2.warpAffine()
- 图像缩放:
- 图像算术运算:加法、减法、混合
- 图像位运算:AND, OR, NOT, XOR (结合掩膜实现区域操作)
- 边缘检测:
cv2.Canny()
- 色彩空间转换:
- 处理视频与摄像头
- 读取视频或摄像头:
cv2.VideoCapture()
- 读取帧:
cap.read()
- 显示帧
- 释放资源
- 读取视频或摄像头:
- 下一步:进阶学习方向
- 总结与展望
1. 引言:什么是 OpenCV?为什么选择 Python?
什么是 OpenCV?
OpenCV 是一个跨平台的计算机视觉库,由英特尔公司发起并持续开发。它包含了大量的函数,涵盖了计算机视觉领域众多方向,包括:
- 图像处理(Filtering, Transformation, etc.)
- 特征检测与描述(Corners, Edges, Keypoints)
- 目标检测(Face Detection, Object Detection using Haar Cascades, DNNs)
- 目标跟踪(Object Tracking)
- 三维重建
- 相机校准
- 机器学习算法(SVM, K-Means, etc. – 虽然更多用于计算机视觉任务)
OpenCV 设计之初就考虑了效率问题,其核心功能由 C++ 实现,因此运行速度较快。
为什么选择 Python 接口?
Python 作为一种高级编程语言,以其简洁的语法、丰富的库生态和强大的社区支持而闻名。将 OpenCV 与 Python 结合使用,具有以下优势:
- 易学易用: Python 语法简单直观,可以快速上手。
- 开发效率高: Python 的动态特性和丰富的标准库、第三方库(如 NumPy, Matplotlib)能极大地提高开发效率。OpenCV 的 Python 接口实际上是 C++ 库的 Python 封装,它返回和操作的对象通常是 NumPy 数组,这使得图像处理与科学计算库(如 NumPy, SciPy)的结合变得非常方便和高效。
- 快速原型开发: Python 非常适合快速构建和测试计算机视觉算法的原型。
- 丰富的生态系统: 可以轻松地将 OpenCV 与其他 Python 库集成,例如使用 Matplotlib 进行可视化,使用 Scikit-learn 进行机器学习,使用深度学习框架(TensorFlow, PyTorch)进行更高级的视觉任务。
因此,Python OpenCV 是学习和应用计算机视觉一个非常理想的起点。
2. 环境搭建:安装 Python 和 OpenCV
在开始之前,你需要确保你的计算机上已经安装了 Python。推荐安装 Python 3.6 或更高版本。你可以从 Python 官网 (python.org) 下载安装包。
安装好 Python 后,安装 OpenCV 的 Python 库非常简单,通常使用 pip 包管理器即可。
打开你的终端或命令提示符,运行以下命令:
bash
pip install opencv-python
如果你需要额外的contrib模块(包含一些实验性或非免费许可的功能,尽管现在大部分常用功能已集成),可以安装:
bash
pip install opencv-contrib-python
对于大多数入门用途,opencv-python
就足够了。
验证安装
安装完成后,打开 Python 交互式环境或创建一个 Python 脚本,尝试导入 cv2
库:
python
import cv2
print(cv2.__version__)
如果能成功导入并打印出版本号,说明 OpenCV 已经成功安装。
推荐使用虚拟环境
为了避免不同项目之间的库版本冲突,强烈建议使用虚拟环境(Virtual Environment)。你可以使用 venv
(Python 3.3+ 自带)或 conda
。
使用 venv
创建虚拟环境:
“`bash
python -m venv myenv
激活虚拟环境
Windows: myenv\Scripts\activate
macOS/Linux: source myenv/bin/activate
在激活的虚拟环境中安装 OpenCV
pip install opencv-python
“`
使用 conda
创建虚拟环境:
bash
conda create -n myenv python=3.8
conda activate myenv
conda install opencv
本文后续的代码示例都假设你已经进入了安装有 OpenCV 的 Python 环境。
3. 核心概念:图像表示与基本属性
在计算机中,图像是如何被表示和处理的呢?OpenCV 主要使用 NumPy 库来处理图像。
图像是什么?(NumPy 数组)
在 OpenCV 中,图像被视为多维的 NumPy 数组(numpy.ndarray
)。
- 灰度图像: 是一个二维数组,每个元素代表一个像素的灰度值,范围通常是 0 到 255(8 位)。数组的形状是
(高度, 宽度)
。 - 彩色图像: 是一个三维数组,形状是
(高度, 宽度, 通道数)
。对于常见的 RGB 或 BGR 图像,通道数是 3。每个像素由一个包含三个元素的数组表示,分别代表不同通道的强度值。
颜色空间(BGR vs RGB, 灰度图)
- 灰度图 (Grayscale): 只包含一个通道,表示亮度信息。像素值范围 0-255,0 代表黑色,255 代表白色。
- BGR (Blue-Green-Red): OpenCV 默认的彩色图像格式是 BGR,而不是我们通常认为的 RGB。这意味着对于一个彩色像素,其存储顺序是蓝色分量、绿色分量、红色分量。例如,白色像素在 8 位 BGR 图像中是
(255, 255, 255)
,纯蓝色是(255, 0, 0)
,纯红色是(0, 0, 255)
。理解这一点非常重要,否则颜色操作可能会出错。 - RGB (Red-Green-Blue): Web 和许多图像文件格式使用 RGB。OpenCV 也支持 RGB 格式,但默认是 BGR。
- HSV (Hue-Saturation-Value): 色调-饱和度-明度。HSV 颜色空间更符合人类感知颜色的方式,并且在基于颜色的目标检测等任务中非常有用,因为它能将颜色(Hue)与亮度和饱和度分离。OpenCV 也支持 HSV。
坐标系统
在 OpenCV 中,图像的坐标原点 (0, 0)
位于图像的左上角。水平方向是 x 轴(列),垂直方向是 y 轴(行)。
- 访问像素时,使用
img[y, x]
或img[y, x, c]
,其中y
是行索引,x
是列索引,c
是通道索引。注意,先行后列,与我们数学中习惯的 (x, y) 坐标顺序(列, 行)
相反,但与 NumPy 数组的索引[row, column]
保持一致。
4. 基础操作:加载、显示和保存图像
这是使用 OpenCV 进行图像处理的第一步。
读取图像:cv2.imread()
cv2.imread(filepath, flags)
函数用于从指定文件路径加载图像。
filepath
: 图像文件的路径(可以是相对路径或绝对路径)。flags
: 指定读取图像的方式。cv2.IMREAD_COLOR
或1
: 读取彩色图像(默认)。忽略图像的透明度信息。cv2.IMREAD_GRAYSCALE
或0
: 读取灰度图像。cv2.IMREAD_UNCHANGED
或-1
: 读取包含 alpha 通道的完整图像(如果存在)。
“`python
import cv2
import numpy as np
假设你有一张名为 ‘cat.jpg’ 的图片在当前目录下
读取彩色图像
img_color = cv2.imread(‘cat.jpg’, cv2.IMREAD_COLOR)
读取灰度图像
img_gray = cv2.imread(‘cat.jpg’, cv2.IMREAD_GRAYSCALE)
检查图像是否成功读取
if img_color is None:
print(“错误:无法读取图像文件 cat.jpg”)
exit() # 如果无法读取则退出程序
else:
print(“图像 cat.jpg 读取成功!”)
打印一些信息
print(“彩色图像的类型:”, type(img_color)) # 通常是
print(“彩色图像的形状 (高, 宽, 通道):”, img_color.shape)
print(“灰度图像的形状 (高, 宽):”, img_gray.shape) # 注意灰度图没有通道信息
图像是一个 NumPy 数组
可以像操作 NumPy 数组一样操作它
print(img_color) # 这会打印整个数组,非常大!
“`
显示图像:cv2.imshow()
cv2.imshow(winname, mat)
函数用于在窗口中显示图像。
winname
: 窗口的名称,一个字符串。多个窗口需要不同的名称。mat
: 要显示的图像矩阵(NumPy 数组)。
“`python
在名为 ‘彩色图像’ 的窗口中显示彩色图像
cv2.imshow(‘彩色图像’, img_color)
在名为 ‘灰度图像’ 的窗口中显示灰度图像
cv2.imshow(‘灰度图像’, img_gray)
注意:cv2.imshow() 会创建一个窗口,但程序会立即继续执行,
如果程序结束,窗口也会立即关闭。因此,你需要使用 cv2.waitKey()
来保持窗口打开,直到用户按下按键。
“`
等待按键与关闭窗口:cv2.waitKey()
, cv2.destroyAllWindows()
cv2.waitKey(delay)
: 等待用户按下键盘上的一个键。delay
参数是等待的毫秒数。如果delay <= 0
,函数会无限期地等待,直到用户按下任意键。- 如果按下了键,函数返回按键的 ASCII 码;如果没有按下键(当 delay > 0 时),函数返回 -1。
- 这是一个非常重要的函数,它不仅处理键盘事件,也是刷新 OpenCV 窗口的关键。没有
waitKey
,imshow
可能不会正常工作。
cv2.destroyAllWindows()
: 销毁所有 OpenCV 创建的窗口。cv2.destroyWindow(winname)
: 销毁指定名称的窗口。
“`python
显示图像后,等待用户按下任意键
0 表示无限等待
cv2.waitKey(0)
按键后,销毁所有 OpenCV 窗口
cv2.destroyAllWindows()
一个完整的读取、显示、等待、关闭的流程:
img = cv2.imread(‘cat.jpg’)
if img is not None:
cv2.imshow(‘Cat’, img)
# 等待 5000 毫秒 (5秒),如果期间按下任意键则立即返回
# key = cv2.waitKey(5000)
# 等待任意键按下
key = cv2.waitKey(0)
print(“您按下了键码:”, key) # 打印按下的键的ASCII码
cv2.destroyAllWindows()
else:
print(“无法加载图像”)
“`
保存图像:cv2.imwrite()
cv2.imwrite(filename, img)
函数用于将图像保存到文件。
filename
: 保存文件的路径和名称(包括扩展名,如 ‘.jpg’, ‘.png’)。img
: 要保存的图像矩阵。
根据文件扩展名,OpenCV 会自动选择相应的图像编码格式。
“`python
将灰度图像保存为 ‘cat_gray.png’
注意:保存为 PNG 格式通常是无损的
cv2.imwrite(‘cat_gray.png’, img_gray)
将彩色图像保存为 ‘cat_color.jpg’
注意:保存为 JPG 格式是压缩的,可能导致质量损失,但文件更小
cv2.imwrite(‘cat_color.jpg’, img_color)
print(“图像已保存。”)
“`
5. 图像属性与像素操作
图像在 OpenCV 中是 NumPy 数组,因此我们可以利用 NumPy 的强大功能来获取图像属性和操作像素。
获取图像属性
NumPy 数组的几个重要属性:
img.shape
: 返回图像的形状。灰度图返回(高度, 宽度)
,彩色图返回(高度, 宽度, 通道数)
。img.size
: 返回图像的总像素数。对于彩色图,是高度 * 宽度 * 通道数
;对于灰度图,是高度 * 宽度
。img.dtype
: 返回图像中每个像素值的数据类型。通常是uint8
(无符号 8 位整数),表示像素值在 0 到 255 之间。
“`python
img = cv2.imread(‘cat.jpg’)
if img is not None:
print(“图像形状 (高, 宽, 通道):”, img.shape) # e.g., (500, 800, 3)
print(“图像总像素数:”, img.size) # e.g., 500 * 800 * 3
print(“像素数据类型:”, img.dtype) # e.g., uint8
else:
print(“无法加载图像”)
“`
访问和修改像素值
你可以使用 NumPy 的索引方式访问或修改特定像素的值。记住索引顺序是 [y, x]
或 [y, x, c]
。
“`python
img = cv2.imread(‘cat.jpg’)
if img is not None:
# 获取 (100, 150) 坐标处的像素值
# 对于彩色图像,这将是一个 BGR 值的数组 [B, G, R]
pixel = img[150, 100] # 注意:先高(y/行), 后宽(x/列)
print(“像素 (100, 150) 的值 (BGR):”, pixel)
# 获取 (100, 150) 坐标处像素的蓝色分量
blue_channel = img[150, 100, 0] # 索引 0 是蓝色
green_channel = img[150, 100, 1] # 索引 1 是绿色
red_channel = img[150, 100, 2] # 索引 2 是红色
print("像素 (100, 150) 的蓝色分量:", blue_channel)
# 修改 (100, 150) 坐标处的像素值为纯蓝色
img[150, 100] = [255, 0, 0] # BGR顺序
# 将 (100, 150) 处像素的红色分量设置为 255
img[150, 100, 2] = 255 # 注意:这里只修改了红色分量
# OpenCV 函数通常比直接修改像素更快,但对于少量像素的修改,直接索引很方便。
# 显示修改后的图像
cv2.imshow('Modified Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“无法加载图像”)
“`
ROI (Region of Interest) 区域提取
利用 NumPy 的切片(slicing)功能,可以非常方便地提取图像的任意矩形区域,这就是 ROI。ROI 本身也是一个 NumPy 数组,是原图像的一个“视图”,对 ROI 的修改会反映到原图像上(除非你创建了副本)。
切片格式:img[y1:y2, x1:x2]
,其中 y1
到 y2-1
是行的范围,x1
到 x2-1
是列的范围。
“`python
img = cv2.imread(‘cat.jpg’)
if img is not None:
# 提取图像中从行 50 到 250,列 100 到 400 的区域
roi = img[50:250, 100:400]
# 显示提取的 ROI
cv2.imshow('ROI', roi)
cv2.waitKey(0)
# 示例:将 ROI 区域的像素值设置为 0(黑色)
img[50:250, 100:400] = [0, 0, 0]
# 显示修改后的原图
cv2.imshow('Image with Black ROI', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“无法加载图像”)
“`
ROI 的概念非常重要,它允许你只处理图像的特定部分,提高效率。
6. 在图像上绘制图形和文字
OpenCV 提供了一系列函数可以在图像上绘制直线、矩形、圆形等多边形以及添加文本。这些函数通常会直接修改原始图像。
常用绘制函数:
cv2.line(img, pt1, pt2, color, thickness)
: 绘制直线。pt1
和pt2
是起点和终点坐标(x, y)
。cv2.rectangle(img, pt1, pt2, color, thickness)
: 绘制矩形。pt1
是左上角坐标,pt2
是右下角坐标。thickness=-1
表示填充矩形。cv2.circle(img, center, radius, color, thickness)
: 绘制圆形。center
是圆心坐标(x, y)
。cv2.putText(img, text, org, fontFace, fontScale, color, thickness, lineType)
: 绘制文字。text
是文字内容,org
是文字的起始坐标(x, y)
(通常是文字基线的左下角),fontFace
是字体类型(如cv2.FONT_HERSHEY_SIMPLEX
),fontScale
是字体缩放比例。
参数说明:
img
: 要在其上绘制的图像。color
: 绘制的颜色,以 BGR 元组形式表示,如(0, 255, 0)
表示绿色。thickness
: 线条的粗细。对于填充图形,设置为-1
。
“`python
img = cv2.imread(‘cat.jpg’)
if img is not None:
# 绘制一条红色的对角线,粗细为 2 像素
cv2.line(img, (0, 0), (img.shape[1], img.shape[0]), (0, 0, 255), 2)
# 绘制一个绿色的矩形
cv2.rectangle(img, (100, 50), (300, 200), (0, 255, 0), 3)
# 绘制一个填充的蓝色圆形
cv2.circle(img, (400, 300), 50, (255, 0, 0), -1)
# 在图像上添加文字
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, 'Hello OpenCV!', (50, 50), font, 1, (255, 255, 255), 2, cv2.LINE_AA)
cv2.imshow('Drawing Demo', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“无法加载图像”)
“`
7. 基本的图像处理技术
OpenCV 提供了大量的图像处理函数。这里介绍一些最基本和常用的。
色彩空间转换:cv2.cvtColor()
用于将图像从一种颜色空间转换到另一种。
cv2.cvtColor(src, code)
src
: 输入图像(NumPy 数组)。code
: 转换的代码,例如cv2.COLOR_BGR2GRAY
将 BGR 图像转换为灰度图,cv2.COLOR_BGR2HSV
将 BGR 图像转换为 HSV 图像。
“`python
img_color = cv2.imread(‘cat.jpg’)
if img_color is not None:
# 将彩色图像转换为灰度图像
img_gray = cv2.cvtColor(img_color, cv2.COLOR_BGR2GRAY)
# 将彩色图像转换为 HSV 图像
img_hsv = cv2.cvtColor(img_color, cv2.COLOR_BGR2HSV)
cv2.imshow('Original (BGR)', img_color)
cv2.imshow('Grayscale', img_gray)
cv2.imshow('HSV', img_hsv)
# 注意:HSV 图像通常不直接显示其三个通道,因为人眼无法直接感知。
# 你可以显示它的某个通道,例如 H 通道
cv2.imshow('Hue Channel', img_hsv[:, :, 0]) # 显示色调通道
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“无法加载图像”)
“`
图像阈值化:cv2.threshold()
, cv2.adaptiveThreshold()
阈值化是图像分割的一种简单方法,将图像转换为二值图(只有黑白两个像素值)。
cv2.threshold(src, thresh, maxval, type)
: 全局阈值化。src
: 输入图像(通常是灰度图)。thresh
: 阈值。maxval
: 最大值(通常是 255)。type
: 阈值类型,如cv2.THRESH_BINARY
(大于阈值设为 maxval,否则为 0),cv2.THRESH_BINARY_INV
(反向二值化),cv2.THRESH_TRUNC
,cv2.THRESH_TOZERO
,cv2.THRESH_TOZERO_INV
等。- 返回两个值:实际使用的阈值和阈值化后的图像。
cv2.adaptiveThreshold(src, maxval, adaptiveMethod, thresholdType, blockSize, C)
: 自适应阈值化,根据像素周围的小区域计算不同的阈值,适合处理光照不均的图像。adaptiveMethod
: 自适应方法,如cv2.ADAPTIVE_THRESH_MEAN_C
(邻域像素平均值)或cv2.ADAPTIVE_THRESH_GAUSSIAN_C
(邻域像素加权平均值)。blockSize
: 用于计算阈值的邻域大小(奇数)。C
: 从计算出的平均值或加权平均值中减去的常数。
“`python
img_gray = cv2.imread(‘cat.jpg’, cv2.IMREAD_GRAYSCALE)
if img_gray is not None:
# 全局二值化阈值设为 127
ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
# 全局反向二值化阈值设为 127
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
# 自适应阈值化 (均值)
# blockSize 11x11,C=2
thresh_adaptive_mean = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
# 自适应阈值化 (高斯)
# blockSize 11x11,C=2
thresh_adaptive_gaussian = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
cv2.imshow('Original Gray', img_gray)
cv2.imshow('Binary Threshold', thresh1)
cv2.imshow('Binary Inverse Threshold', thresh2)
cv2.imshow('Adaptive Mean Threshold', thresh_adaptive_mean)
cv2.imshow('Adaptive Gaussian Threshold', thresh_adaptive_gaussian)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“无法加载灰度图像”)
“`
图像平滑/模糊
用于减少图像噪声,使图像更平滑。常见的滤波器有均值滤波、高斯滤波、中值滤波等。
cv2.GaussianBlur(src, ksize, sigmaX)
: 高斯模糊。ksize
是高斯核的大小(奇数元组,如(5, 5)
),sigmaX
是高斯函数在 X 方向的标准差(设为 0 会根据 ksize 自动计算)。cv2.medianBlur(src, ksize)
: 中值模糊。ksize
是中值滤波器的大小(奇数,如 5)。对椒盐噪声(salt-and-pepper noise)效果较好。
“`python
img = cv2.imread(‘cat.jpg’)
if img is not None:
# 应用高斯模糊,核大小 5×5
blur_gaussian = cv2.GaussianBlur(img, (5, 5), 0)
# 应用中值模糊,核大小 5x5
blur_median = cv2.medianBlur(img, 5)
cv2.imshow('Original', img)
cv2.imshow('Gaussian Blur', blur_gaussian)
cv2.imshow('Median Blur', blur_median)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“无法加载图像”)
“`
图像几何变换
包括缩放、平移、旋转等。
- 缩放:
cv2.resize()
cv2.resize(src, dsize, fx, fy, interpolation)
dsize
: 输出图像的大小(宽度, 高度)
。如果设置为(0, 0)
,则根据fx
和fy
计算大小。fx
,fy
: 沿水平和垂直方向的缩放比例。interpolation
: 插值方法,如cv2.INTER_AREA
(缩小时推荐),cv2.INTER_CUBIC
(放大时推荐,效果好但慢),cv2.INTER_LINEAR
(默认,较快)。
“`python
img = cv2.imread(‘cat.jpg’)
if img is not None:
# 按比例缩放,缩小到原来的一半
img_small = cv2.resize(img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
# 缩放到指定大小 (宽 300, 高 200)
img_fixed_size = cv2.resize(img, (300, 200), interpolation=cv2.INTER_LINEAR)
cv2.imshow('Original', img)
cv2.imshow('Scaled Down (0.5x)', img_small)
cv2.imshow('Fixed Size (300x200)', img_fixed_size)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“无法加载图像”)
“`
- 平移与旋转:
cv2.warpAffine()
平移和旋转通常通过一个 2×3 的变换矩阵实现,然后使用 cv2.warpAffine()
应用这个变换。
- 平移:
变换矩阵 M =[[1, 0, tx], [0, 1, ty]]
,其中tx
是水平平移量,ty
是垂直平移量。
“`python
img = cv2.imread(‘cat.jpg’)
if img is not None:
rows, cols = img.shape[:2] # 获取图像的高和宽
# 构建平移矩阵:向右平移 100 像素,向下平移 50 像素
# M = [[1, 0, 100], [0, 1, 50]]
M_translation = np.float32([[1, 0, 100], [0, 1, 50]])
# 应用平移变换
img_translated = cv2.warpAffine(img, M_translation, (cols, rows)) # 指定输出图像大小
cv2.imshow('Original', img)
cv2.imshow('Translated', img_translated)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“无法加载图像”)
“`
-
旋转:
使用cv2.getRotationMatrix2D(center, angle, scale)
生成旋转矩阵。center
: 旋转中心坐标(x, y)
,通常是图像中心(cols/2, rows/2)
。angle
: 旋转角度(逆时针为正)。scale
: 缩放比例(保持原大小设为 1.0)。
然后使用
cv2.warpAffine()
应用旋转。
“`python
img = cv2.imread(‘cat.jpg’)
if img is not None:
rows, cols = img.shape[:2]
# 获取旋转矩阵:围绕中心旋转 45 度,不缩放
M_rotation = cv2.getRotationMatrix2D((cols / 2, rows / 2), 45, 1.0)
# 应用旋转变换
img_rotated = cv2.warpAffine(img, M_rotation, (cols, rows)) # 注意输出大小通常与原图相同,可能会裁剪掉一部分内容
cv2.imshow('Original', img)
cv2.imshow('Rotated 45 Degrees', img_rotated)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“无法加载图像”)
“`
图像算术运算
OpenCV 提供了一些函数进行图像的加法、减法、混合等运算。注意,这些操作是基于像素值的。
cv2.add(img1, img2)
: 图像加法。像素值相加,结果超过 255 会被截断为 255(饱和操作)。cv2.addWeighted(img1, alpha, img2, beta, gamma)
: 图像混合(加权求和)。result = img1 * alpha + img2 * beta + gamma
。常用于图像叠加或透明效果。
“`python
img1 = cv2.imread(‘cat.jpg’)
创建一个与img1大小相同的纯色图像或加载另一张图像
为了简单,这里使用纯色图像
img2 = np.full_like(img1, (50, 50, 50), dtype=np.uint8) # 创建一个增加亮度的纯灰色图像
if img1 is not None:
# 图像加法 (增加亮度)
img_added = cv2.add(img1, img2)
# 图像混合:img1 占 70%, img2 占 30%, gamma 为 0
# 制造半透明效果
img_blended = cv2.addWeighted(img1, 0.7, img2, 0.3, 0)
cv2.imshow('Original', img1)
cv2.imshow('Added (Brighter)', img_added)
cv2.imshow('Blended', img_blended) # 这里因为img2是纯色,看起来像调整了img1的亮度
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“无法加载图像”)
“`
图像位运算
位运算(AND, OR, NOT, XOR)在图像处理中非常有用,特别是结合掩膜(Mask)进行图像区域提取或操作。
cv2.bitwise_and(src1, src2, mask)
cv2.bitwise_or(src1, src2, mask)
cv2.bitwise_not(src, mask)
cv2.bitwise_xor(src1, src2, mask)
这些函数都支持一个可选的 mask
参数。如果提供了掩膜,操作只会在掩膜中非零(白色)的区域进行。
“`python
img1 = cv2.imread(‘cat.jpg’)
if img1 is not None:
rows, cols = img1.shape[:2]
# 创建一个黑色的背景图像
img2 = np.zeros((rows, cols, 3), dtype=np.uint8)
# 在黑色图像上创建一个白色的圆形作为掩膜
# 注意:掩膜通常是灰度图像或单通道图像
mask = np.zeros((rows, cols), dtype=np.uint8)
cv2.circle(mask, (cols // 2, rows // 2), 100, 255, -1) # 在中心绘制一个填充的白色圆
# 显示掩膜
cv2.imshow('Mask', mask)
# 应用位 AND 运算结合掩膜:只保留 img1 中掩膜非零区域的内容
# img2 (全黑) & img1 (原图) 并在 mask 非零区域进行操作
img_masked = cv2.bitwise_and(img1, img1, mask=mask) # 常用技巧: src1和src2都是原图
# 应用位 NOT 运算结合掩膜:只对 img1 中掩膜非零区域进行反色
img_not_masked = cv2.bitwise_not(img1, mask=mask)
cv2.imshow('Original', img1)
cv2.imshow('Masked Region', img_masked)
cv2.imshow('NOT Masked Region', img_not_masked)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“无法加载图像”)
“`
边缘检测:cv2.Canny()
Canny 边缘检测器是经典的边缘检测算法,效果较好。
cv2.Canny(image, threshold1, threshold2)
image
: 8 位输入图像(通常是灰度图)。threshold1
,threshold2
: 滞后性阈值。小于 threshold1 的被排除;大于 threshold2 的被确定为边缘;介于两者之间的,如果与确定边缘相连,也被认为是边缘。推荐threshold2
是threshold1
的 2-3 倍。
“`python
img_gray = cv2.imread(‘cat.jpg’, cv2.IMREAD_GRAYSCALE)
if img_gray is not None:
# 应用 Canny 边缘检测
# 参数 100 和 200 是阈值
edges = cv2.Canny(img_gray, 100, 200)
cv2.imshow('Original Gray', img_gray)
cv2.imshow('Canny Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“无法加载灰度图像”)
“`
8. 处理视频与摄像头
OpenCV 不仅处理静态图像,也能方便地处理视频流,无论是来自文件还是摄像头。
-
读取视频或摄像头:
cv2.VideoCapture()
cv2.VideoCapture(index)
或cv2.VideoCapture(filename)
index
: 设备索引号。通常 0 代表默认摄像头,1 代表第二个摄像头,以此类推。filename
: 视频文件的路径。
-
读取帧:
cap.read()
在一个循环中调用cap.read()
来读取视频流中的下一帧。
它返回两个值:success
: 布尔值,如果成功读取帧则为True
,否则为False
。frame
: 读取到的帧图像(一个 NumPy 数组)。如果没有读取到帧(视频结束或出错),则为None
。
-
释放资源:
cap.release()
,cv2.destroyAllWindows()
处理完毕后,需要释放视频捕获对象和关闭所有窗口。
“`python
0 表示打开默认摄像头
cap = cv2.VideoCapture(0)
或者打开一个视频文件
cap = cv2.VideoCapture(‘my_video.mp4’)
检查摄像头或视频是否成功打开
if not cap.isOpened():
print(“错误:无法打开摄像头或视频文件。”)
exit()
循环读取并显示帧
while True:
# 读取一帧
success, frame = cap.read()
# 如果无法读取帧(例如视频结束)则退出循环
if not success:
print("无法接收帧 (视频流已结束?)。退出...")
break
# 你可以在这里对每一帧进行图像处理
# 例如:将帧转换为灰度图
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 显示原始帧
cv2.imshow('Original Frame', frame)
# 显示处理后的帧
cv2.imshow('Gray Frame', gray_frame)
# 检测按键,如果按下 'q' 键则退出
# waitKey 参数为 1ms,表示每帧之间等待 1ms,以确保窗口响应和显示帧
# 按下 'q' 键的 ASCII 码是 104 (小写q) 或 81 (大写Q),但更通用的是 ord('q')
if cv2.waitKey(1) == ord('q'):
break
循环结束后,释放视频捕获对象并关闭所有窗口
cap.release()
cv2.destroyAllWindows()
print(“视频处理结束。”)
“`
这段代码会打开你的摄像头,实时显示原始画面和灰度化后的画面,直到你按下键盘上的 ‘q’ 键。
9. 下一步:进阶学习方向
恭喜你已经掌握了 Python OpenCV 的基础知识!这只是计算机视觉的冰山一角。接下来,你可以根据自己的兴趣深入学习以下方向:
- 特征检测与匹配: SIFT, SURF, ORB 等特征点检测算法,用于图像拼接、目标识别等。
- 轮廓检测: 查找和分析图像中的轮廓,常用于目标识别和形状分析。
- 图像分割: 将图像分成有意义的区域(例如,前景和背景)。
- 对象检测: 使用 Haar 特征级联(如人脸检测)或更先进的深度学习方法(如 YOLO, SSD, Faster R-CNN)。
- 目标跟踪: 在视频序列中跟踪特定对象。
- 相机校准与三维重建: 理解相机的内外参数,从二维图像恢复三维信息。
- 机器学习模块: 虽然现在更多使用独立的机器学习库,但 OpenCV 仍包含一些基础算法。
- 深度学习推理: OpenCV 的 DNN 模块支持加载和运行主流深度学习框架(如 TensorFlow, PyTorch, Caffe)训练的模型。
10. 总结与展望
通过本文,你已经了解了 Python OpenCV 的基本概念、环境搭建方法、如何加载、显示、保存图像,以及如何进行基本的像素操作、图形绘制和一些核心的图像处理技术(色彩空间转换、阈值化、滤波、几何变换、算术和位运算、边缘检测)。你还学会了如何使用 OpenCV 处理视频流。
这为你进入更广阔的计算机视觉世界打下了坚实的基础。计算机视觉是一个充满挑战和机遇的领域,广泛应用于自动驾驶、医疗影像分析、工业自动化、安防监控、增强现实等众多领域。
持续实践是掌握任何技术的关键。尝试运行本文中的代码示例,并尝试修改它们,处理不同的图像和视频。查阅 OpenCV 官方文档(虽然主要是 C++ 文档,但函数名和参数通常一致,结合 Python 文档或示例进行理解)是进一步学习的好方法。
祝你在计算机视觉的学习旅程中取得更多进展!