前言
1、更改颜色空间
1.1BGR 到 Gray 的示例
1.2 BGR 到 HSV 的示例:
编辑 1.3 通过HSV进行颜色追踪
1.3.1hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
1.3.2 BGR vs HSV:
1.3.3 为什么使用 HSV 颜色空间?
1.3.4 cv.inRange(hsv, lower_red1, upper_red1)
1.3.5 cv.bitwise_and(frame, frame, mask=mask)
参数解释:
返回值:
1.4 通过HSV进行颜色追踪-框选出目标区域
1.4.1 cv.morphologyEx(mask, cv.MORPH_OPEN, np.ones((3, 3), np.uint8))函数
1.4.2 cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)函数
1.4.3 cv.contourArea(contour)函数
1.5 通过HSV进行颜色追踪-围绕边缘实现框选
参考文献
前言
主要案例是实现对颜色的追踪操作,这是一个案例可以直接复制代码进行展示
1、更改颜色空间
在本教程中:
- 你会学到如何将图片从一个颜色空间转换到另一个,例如 BGR 到 Gray,BGR 到 HSV 等。
- 另外,我们会创建一个从视频中提取彩色对象的应用。
- 你会学到如下函数:cv.cvtColor(),cv.inRange()
1.1BGR 到 Gray 的示例
BGR 是 OpenCV 中的默认颜色空间,它的顺序是蓝-绿-红,而不是红-绿-蓝。将 BGR 图像转换为灰度图像,可以通过以下代码实现:
import cv2 as cv
# 读取图片
image = cv.imread('images/demo2.png')
# 将 BGR 图片转换为灰度图像
gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# 显示原图和灰度图
cv.imshow('Original Image', image)
cv.imshow('Gray Image', gray_image)
cv.waitKey(0)
cv.destroyAllWindows()
1.2 BGR 到 HSV 的示例:
HSV 是色相 (Hue),饱和度 (Saturation) 和亮度 (Value) 的颜色空间。它在许多应用中,例如颜色分割时非常有用。
注意 对于 HSV, 色调(Hue)范围为 [0,179], 饱和度(Saturation)范围为 [0,255] ,明亮度(Value)为 [0,255]. 不同的软件使用不同的比例. 所以如果你想用 OpenCV 的值与别的软件的值作对比,你需要归一化这些范围。
import cv2 as cv
# 读取图片
image = cv.imread('images/demo2.png')
# 将 BGR 图片转换为 HSV 图像
hsv_image = cv.cvtColor(image, cv.COLOR_BGR2HSV)
# 显示原图和 HSV 图像
cv.imshow('Original Image', image)
cv.imshow('HSV Image', hsv_image)
cv.waitKey(0)
cv.destroyAllWindows()
1.3 通过HSV进行颜色追踪
现在我们知道了如何将 BGR 图片转化为 HSV 图片,我们可以使用它去提取彩色对象。HSV 比 BGR 在颜色空间上更容易表示颜色。在我们的应用中,我们会尝试提取一个红色的彩色对象,方法为:
- 提取每一视频帧。
- 将 BGR 转化为 HSV 颜色空间。
- 我们用蓝色像素的范围对该 HSV 图片做阈值。
- 现在提取出了红色对象,我们可以随意处理图片了 。
import cv2 as cv
import numpy as np
# 打开摄像头
cap = cv.VideoCapture(0)
while True:
# 读取摄像头的一帧
ret, frame = cap.read()
if not ret:
print("无法从摄像头获取帧")
break
# 将 BGR 帧转换为 HSV 颜色空间
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
# 定义红色的 HSV 范围(红色有两个范围)
lower_red1 = np.array([0, 120, 70]) # 红色的低端色调范围
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([170, 120, 70]) # 红色的高端色调范围
upper_red2 = np.array([180, 255, 255])
# 创建两个掩膜
mask1 = cv.inRange(hsv, lower_red1, upper_red1) # 对应第一个红色范围
mask2 = cv.inRange(hsv, lower_red2, upper_red2) # 对应第二个红色范围
# 将两个掩膜合并,覆盖所有红色区域
mask = mask1 + mask2
# 使用位操作将掩膜应用到原图像,提取红色对象
result = cv.bitwise_and(frame, frame, mask=mask)
# 显示原始帧和提取的红色对象
cv.imshow('Original Frame', frame)
cv.imshow('Red Object Extraction', result)
# 按下 'q' 键退出
if cv.waitKey(1) & 0xFF == ord('q'):
break
# 释放摄像头并关闭所有窗口
cap.release()
cv.destroyAllWindows()
如果mask = mask2的时候效果更好哈
1.3.1hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
cv.cvtColor()
是 OpenCV 中用于转换图像颜色空间的函数。在你的例子中,hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
将图像从 BGR 颜色空间转换为 HSV 颜色空间。这个操作在图像处理任务(如颜色对象提取)中非常常见,因为 HSV 颜色空间在颜色检测方面比 BGR 更直观和方便。
参数解释:
-
frame
:这是输入图像,通常是 BGR 格式的彩色图像(BGR 是 OpenCV 默认的颜色格式)。 -
cv.COLOR_BGR2HSV
:这是颜色空间转换的标志,表示将输入图像从 BGR 颜色空间转换为 HSV 颜色空间。OpenCV 中预定义了多种颜色空间转换的标志,BGR2HSV
是其中之一。
返回值:
-
hsv
:输出图像,这是输入图像在 HSV 颜色空间下的表现形式。
1.3.2 BGR vs HSV:
-
BGR (蓝-绿-红) 是 OpenCV 使用的默认颜色空间,它表示的是三个颜色通道的强度值。
-
例如,
BGR(255, 0, 0)
代表蓝色,BGR(0, 255, 0)
代表绿色,BGR(0, 0, 255)
代表红色。
-
-
HSV (色调-饱和度-亮度) 是另一种表示颜色的方法,它将颜色信息分为三个独立的属性:
-
H (Hue,色调):表示颜色的种类,范围为 [0, 179],例如 0 表示红色,120 表示绿色,240 表示蓝色。
-
S (Saturation,饱和度):表示颜色的纯度,范围为 [0, 255],值越高表示颜色越纯,越低表示颜色越灰。
-
V (Value,亮度):表示颜色的亮度,范围为 [0, 255],值越高表示颜色越亮。
-
1.3.3 为什么使用 HSV 颜色空间?
在颜色对象检测的任务中,HSV 比 BGR 更直观:
-
颜色分离更明显:在 BGR 中,颜色是由各通道的组合决定的,而在 HSV 中,色调
H
值直接表示颜色种类,使得对特定颜色的检测更方便。 -
更容易调节阈值:在 HSV 中,只需要调整色调(Hue)的范围来检测特定颜色,而在 BGR 中,你可能需要分别考虑三个通道的值。
如果你想检测红色对象,在 BGR 中可能很复杂,因为红色会涉及到 B、G、R 通道的值。而在 HSV 中,你只需要设定 H
在 0 和 10 或 170 和 180 之间,即可轻松提取红色。
HSV 转换的常见用途:
-
颜色对象提取:例如,你想从图像或视频中提取某种颜色的物体,可以先将图像转换为 HSV 颜色空间,再通过定义
H
、S
、V
的范围,使用cv.inRange()
来提取特定颜色。 -
颜色过滤:你可以通过 HSV 的颜色空间,过滤掉不需要的颜色,保留或高亮感兴趣的部分。
1.3.4 cv.inRange(hsv, lower_red1, upper_red1)
参数说明:
-
hsv
:输入图像,通常是已经转换为 HSV 颜色空间的图像。 -
lower_red1
:颜色范围的下界,表示最低的 HSV 值。例如[0, 120, 70]
,对应的是色调 (H)、饱和度 (S)、亮度 (V) 的最小值。 -
upper_red1
:颜色范围的上界,表示最高的 HSV 值。例如[10, 255, 255]
,对应的是色调 (H)、饱和度 (S)、亮度 (V) 的最大值。
返回值:
-
mask
:输出的二值图像(掩膜)。在这个掩膜中,符合给定 HSV 范围的像素会被设置为白色(255),不符合的像素会被设置为黑色(0)。
1.3.5 cv.bitwise_and(frame, frame, mask=mask)
cv.bitwise_and()
是 OpenCV 中用于按位与运算的函数。该函数的主要作用是将两个图像的每个像素按位进行与操作(AND),并生成一个新图像。结合掩膜使用时,它可以用于提取特定颜色或区域。
参数解释:
src1
:输入图像的第一个源,可以是 BGR 或其他颜色空间的图像。src2
:输入图像的第二个源,通常与src1
相同。这表示将src1
中的像素与src2
中的像素进行按位与运算。mask
(可选):掩膜图像,指定哪些区域需要进行运算。掩膜中的白色区域(255)表示保留对应像素,黑色区域(0)表示不保留对应像素。
返回值:
output
:输出图像,包含src1
和src2
按位与运算的结果,或仅保留掩膜指定的区域。
1.4 通过HSV进行颜色追踪-框选出目标区域
import cv2 as cv
import numpy as np
# 打开摄像头
cap = cv.VideoCapture(0)
while True:
# 读取摄像头的一帧
ret, frame = cap.read()
if not ret:
print("无法从摄像头获取帧")
break
# 将 BGR 帧转换为 HSV 颜色空间
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
# 定义红色的 HSV 范围
lower_red1 = np.array([0, 120, 70])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([170, 120, 70])
upper_red2 = np.array([180, 255, 255])
# 创建两个掩膜,分别对应红色的低端和高端
mask1 = cv.inRange(hsv, lower_red1, upper_red1)
mask2 = cv.inRange(hsv, lower_red2, upper_red2)
# 合并掩膜,提取所有红色区域
mask = mask2
# 对掩膜进行形态学操作,去除噪点(可选)
mask = cv.morphologyEx(mask, cv.MORPH_OPEN, np.ones((3, 3), np.uint8))
mask = cv.morphologyEx(mask, cv.MORPH_CLOSE, np.ones((3, 3), np.uint8))
# 找到红色区域的轮廓
contours, _ = cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 在原图上绘制轮廓
for contour in contours:
if cv.contourArea(contour) > 500: # 过滤掉较小的轮廓
x, y, w, h = cv.boundingRect(contour) # 获取包围轮廓的矩形框
cv.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2) # 绘制矩形框
# 显示原始帧和红色区域的掩膜
cv.imshow('Original Frame', frame)
cv.imshow('Red Object Mask', mask)
# 按下 'q' 键退出
if cv.waitKey(1) & 0xFF == ord('q'):
break
# 释放摄像头并关闭所有窗口
cap.release()
cv.destroyAllWindows()
1.4.1 cv.morphologyEx(mask, cv.MORPH_OPEN, np.ones((3, 3), np.uint8))函数
cv.morphologyEx()
是 OpenCV 中用于执行形态学操作的函数。形态学操作常用于图像处理,特别是在处理二值图像时,如去除噪声、填补孔洞等。cv.MORPH_OPEN
是形态学操作中的一种,称为开运算。后期的形态学中会进行进一步深入。
参数解释:
-
src
:输入图像,通常是二值图像(即掩膜),在这个情况下是你之前提到的mask
。 -
op
:指定要执行的形态学操作。在你的例子中,使用的是cv.MORPH_OPEN
,表示执行开运算。 -
kernel
:结构元素,是一个用于形态学操作的卷积核。通常使用np.ones()
创建一个全为 1 的矩阵,指定卷积核的大小和形状。例如,在你的例子中,使用的是一个 3x3 的矩阵。
返回值:
-
output
:输出图像,经过形态学操作后的结果。
开运算 (cv.MORPH_OPEN
) 的作用:
开运算是一个由两个基本操作组成的组合操作:
-
腐蚀(Erosion):用结构元素对图像进行腐蚀操作,通常用于去除小的噪声点。
-
膨胀(Dilation):随后对腐蚀后的图像进行膨胀操作,恢复原始图像中大部分的结构。
开运算的效果是:
-
去除小的噪声和细小的连接。
-
保留较大的结构,平滑边缘。
import cv2 as cv
import numpy as np
# 创建一个二值掩膜
mask = cv.imread('mask.jpg', cv.IMREAD_GRAYSCALE)
# 使用开运算去除噪声
kernel = np.ones((3, 3), np.uint8) # 创建一个3x3的结构元素
cleaned_mask = cv.morphologyEx(mask, cv.MORPH_OPEN, kernel)
# 显示结果
cv.imshow('Cleaned Mask', cleaned_mask)
cv.waitKey(0)
cv.destroyAllWindows()
处理过程:
-
读取掩膜:首先读取一个二值掩膜
mask
。 -
创建结构元素:使用
np.ones((3, 3), np.uint8)
创建一个 3x3 的结构元素。 -
应用开运算:调用
cv.morphologyEx(mask, cv.MORPH_OPEN, kernel)
执行开运算,去除掩膜中的小噪声。 -
显示结果:输出的
cleaned_mask
是经过开运算处理后的二值图像。
实际效果:
-
开运算的输出图像将减少小的白色噪声点,同时保持较大物体的形状。这样可以为后续的图像处理(如轮廓检测)提供更清晰的输入。
总结:
-
cv.morphologyEx()
是一个强大的函数,用于执行多种形态学操作。 -
开运算(
cv.MORPH_OPEN
)是处理二值图像中的噪声和细节的常用技术,特别适合于图像分割和目标提取等应用。
1.4.2 cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)函数
cv.findContours()
是 OpenCV 中用于检测图像中的轮廓的函数。轮廓是图像中相同颜色或相似亮度的边界曲线,可以用于形状分析和对象检测等任务。
contours, hierarchy = cv.findContours(image, mode, method)
参数解释:
-
image
:输入图像,通常是一个二值图像(如掩膜)。该图像将用于检测轮廓,轮廓通常是图像中物体的边界。注意,这个输入图像会被修改,通常在调用该函数前需要对其进行复制(如果需要保留原图像)。 -
mode
:轮廓检索模式,决定了如何检索轮廓。常用的选项包括:-
cv.RETR_EXTERNAL
:仅检索外部轮廓,忽略内部轮廓。 -
cv.RETR_LIST
:检索所有轮廓,并将其放入列表中,但不建立层级关系。 -
cv.RETR_TREE
:检索所有轮廓,并建立轮廓之间的层级关系。
-
-
method
:轮廓逼近方法,决定了如何存储轮廓的点。常用的选项包括:-
cv.CHAIN_APPROX_SIMPLE
:压缩轮廓,存储轮廓的端点,节省存储空间。 -
cv.CHAIN_APPROX_NONE
:存储所有轮廓点,不进行任何压缩。
-
返回值:
-
contours
:返回一个列表,其中包含图像中所有检测到的轮廓。每个轮廓是一个由轮廓点组成的 numpy 数组。 -
hierarchy
:返回一个层级数组,表示轮廓之间的关系(可选,通常用于需要分析轮廓层级的场景)。
1.4.3 cv.contourArea(contour)函数
cv.contourArea()
是 OpenCV 中用于计算轮廓面积的函数。它接受一个轮廓作为输入,并返回该轮廓所围成区域的面积。
参数解释:
-
contour
:输入轮廓,通常是通过cv.findContours()
函数获得的轮廓。轮廓是一个由多个点组成的数组,定义了物体的边界。
返回值:
-
area
:返回轮廓所围成区域的面积,单位是像素。
使用示例:
在你的示例 cv.contourArea(contour) > 500
中,函数的作用可以分为以下几步:
-
计算轮廓面积:调用
cv.contourArea(contour)
计算当前轮廓的面积。 -
与阈值比较:将计算出的面积与阈值(例如 500 像素)进行比较,以筛选出较大的轮廓。
1.5 通过HSV进行颜色追踪-围绕边缘实现框选
import cv2 as cv
import numpy as np
# 打开摄像头
cap = cv.VideoCapture(0)
while True:
# 读取摄像头的一帧
ret, frame = cap.read()
if not ret:
print("无法从摄像头获取帧")
break
# 将 BGR 帧转换为 HSV 颜色空间
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
# 定义红色的 HSV 范围
lower_red1 = np.array([0, 120, 70])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([170, 120, 70])
upper_red2 = np.array([180, 255, 255])
# 创建两个掩膜,分别对应红色的低端和高端
mask1 = cv.inRange(hsv, lower_red1, upper_red1)
mask2 = cv.inRange(hsv, lower_red2, upper_red2)
# 合并掩膜,提取所有红色区域
mask = cv.add(mask1, mask2)
# 对掩膜进行形态学操作,去除噪点(可选)
mask = cv.morphologyEx(mask, cv.MORPH_OPEN, np.ones((3, 3), np.uint8))
mask = cv.morphologyEx(mask, cv.MORPH_CLOSE, np.ones((3, 3), np.uint8))
# 找到红色区域的轮廓
contours, _ = cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 在原图上绘制轮廓
for contour in contours:
if cv.contourArea(contour) > 500: # 过滤掉较小的轮廓
cv.drawContours(frame, [contour], -1, (0, 255, 0), 2) # 用绿色绘制轮廓
# 显示原始帧和红色区域的掩膜
cv.imshow('Original Frame', frame)
cv.imshow('Red Object Mask', mask)
# 按下 'q' 键退出
if cv.waitKey(1) & 0xFF == ord('q'):
break
# 释放摄像头并关闭所有窗口
cap.release()
cv.destroyAllWindows()
cv.drawContours(frame, [contour], -1, (0, 255, 0), 2)
这行代码用于在原始帧上绘制轮廓,轮廓的颜色为绿色,线条宽度为 2 像素。通过这种方式,红色区域的边缘将被绘制出来,而不是用矩形框包围。
实际效果:
-
运行这个修改后的代码时,程序将识别并框选红色对象的边缘,显示更精确的轮廓,而不是简单的矩形框。这种方式能够更好地表现物体的形状和边界。
总结:
-
使用
cv.drawContours()
代替矩形框绘制,使得检测结果更贴合物体的实际形状,适用于需要对形状进行更细致分析的应用场景。
2、图像的几何变换