这是一个关于如何识别特定内容的教程,是通过opencv-python库将内容识别出来。
实现步骤
-
搭建环境(conda)
-
实例:识别盲道
-
修改代码,增加识别它物的能力
一、搭建环境
本次教程我们使用conda,来部署python环境。
Conda 是一个开源的包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖项,并在它们之间轻松切换。Conda 最初是为 Python 相关的项目设计的,但现在已经支持多种语言,包括 R、Julia、Node.js 和其他科学计算工具。
Conda 的主要特点:
跨平台:Conda 可以在 Windows、macOS 和 Linux 上运行。
环境管理:用户可以创建独立的环境来隔离不同的项目或实验,每个环境都可以有自己的软件包集合和 Python 版本。
包管理:Conda 有一个庞大的软件包仓库(Anaconda Repository),其中包含了数千个预先编译好的科学计算和数据科学相关的软件包。
命令行工具:Conda 提供了一系列命令行工具来简化安装、更新、卸载和管理软件包的过程。
兼容性:除了管理 conda 包之外,Conda 还可以安装普通的 Python 软件包(通过 pip)。
但是这次我们用到的库并不多,所以我们不用标准的anaconda(1.01GB),而是用小巧的miniconda,先从网上下载下来,这里用清华镜像:
Windows https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py39_24.5.0-0-Windows-x86_64.exe
macos arm https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py39_24.5.0-0-MacOSX-arm64.pkg
macos X_86 https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py39_24.5.0-0-MacOSX-x86_64.pkg
下载完毕后安装即可。
接着,就是搭建python环境了。
Windows
在搜索框搜索miniconda,应该会跳出一个带有miniconda名字的命令行应用
打开它,然后我们需要创建一个3.10版本的python环境
conda create -n ColorDetection python=3.10.0
这样就可以创建一个环境了,中途遇到是否继续[y/n],输入y再回车就行,在安装好后,我们输入以下命令来启动这个环境
conda activate ColorDetection
Macos
打开自带的终端,再输入上面Windows的命令就行。
安装第三方库
这次我们需要numpy和opencv-python
可以使用pip安装
pip3 install numpy opencv-python
实例:识别盲道
在说如何识别特定颜色前,我们先来举一个例子,就是识别盲道,本实例针对传统、普遍的黄色盲道展开。
首先,先下载示例图片:https://tse3-mm.cn.bing.net/th/id/OIP-C.MjmVaLe5KcBb-AOCPD6b0gHaJ4?rs=1&pid=ImgDetMain
如果下载不了的话,可以在网上(bing)搜“盲道”,找一张。
接着,就是编写代码了,打开你们的ide,创建一个名为“ColorDetection”的文件(项目),并在其中新建一个名为“BlindLaneDetection”的python文件,我们将在这个python文件中编写识别盲道的示例代码。
首先是导入numpy库和opencv-python库,在一些ide中,你可能需要手动启用我们刚刚搭建的conda环境。
import cv2
import numpy as np
接着是导入我们下载的示例图片
frame = cv2.imread('your_image_path')
其中frame的意思是我们将这个图片当作一帧来处理,frame也是opencv中常被命名的变量。
cv2.imread是opencv-python的一个读取图片的类。
请将“your_image_path”替换成你的示例图片的位置。
然后是将读取到的图片转换为hsv色彩格式
一、HSV颜色系统简介 HSV是一种在人们生活中甚至更常用的颜色系统,在电视遥控器上、在画画的调色板中、在你用爱某艺视频调整亮度时都很常见,因为它更符合人们描述颜色的方式——是什么颜色、颜色有多深、颜色有多亮。 H——Hue即色相,就是我们平时所说的红、绿,如果你分的更细的话可能还会有洋红、草绿等等;在HSV模型中,用度数来描述色相,其中红色对应0度,绿色对应120度,蓝色对应240度。
S——Saturation即饱和度,色彩的深浅度(0-100%) ,对于一种颜色比如红色,我们可以用浅红——大红——深红——红得发紫等等语言来描述它(请原谅一个纯理科生的匮乏的颜色系统),对应在画水彩的时候即一种颜料加上不同分量的水形成不同的饱和度。 V——Value即色调,纯度,色彩的亮度(0-100%) ,这个在调整屏幕亮度的时候比较常见。
注:在模型2中: H是色彩点在对应圆形切面上与红色半径(对于H=0度)所形成的圆心角。 V是色彩点所在圆形切面到圆锥顶点的距离。在顶面上V=1 顶点V=0 S是色彩点到所在圆形切面圆心的距离与该圆半径的比例值,在圆锥表面上S=1,在圆心处S=0
frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
这里将frame增加了一个后缀_hsv表示以hsv为颜色空间的图片
cv2.cvtColor()是opencv一个转换图片色彩空间的类
cv2.cvtColor(input_image, flag)
参数 | 描述 | 返回值 |
---|---|---|
input_image | 需要转换的图片 | 颜色空间转换后的图片矩阵 |
flag | 转换的类型 | 颜色空间转换后的图片矩阵 |
两个参数分别表示原始图像和转换方式
继续创建掩膜
掩膜 数字图像处理中的掩膜概念是借鉴于PCB制版的过程,在半导体制作中,许多芯片工艺步骤采用光刻技术,用于这些步骤的图形“底片”称为掩膜,其作用是:在硅片上选定的区域中对一个不透明的图形模板掩盖,继而下面的腐蚀或扩散只影响选定区域以外的区域。 图像掩膜与其类似,用选定的图像、图形或物体,对处理的图像进行遮挡,来控制图像处理区域或处理过程。 数字图像处理中,掩膜为―维矩阵数组有时也用多值图像,图像掩膜主要用于: ①提取感兴趣区,用预先制作的感兴趣区掩膜与待处理图像相乘.得到感兴趣区图像,感兴趣区内图像值保持不变,而区外图像值都为0。 ②屏蔽作用,用掩膜对图像上某些区域作屏蔽使其不参加处理或不参加处理参数的计算.或仅对屏蔽区作处理或统计。 ③结构特征提取,用相似性变量或图像匹配方法检测和提取图像中与掩膜相似的结构特征。 ④特殊形状图像的制作。
mask = cv2.inRange(frame_hsv, np.array([17, 117, 238]), np.array([19, 158, 255]))
-
cv2.inRange()
是OpenCV中的一个函数,用于根据指定的阈值范围对图像进行阈值操作。这个函数会返回一个二值图像(黑白图像),其中满足颜色范围条件的像素被标记为白色(通常值为255),不满足条件的像素则被标记为黑色(值为0)。 -
np.array([17, 117, 238])
和np.array([19, 158, 255])
定义了HSV色彩空间中的下限和上限。这意味着代码将会选取那些H(色调)在17到19之间、S(饱和度)在117到158之间、V(明度/亮度)在238到255之间的像素作为目标颜色区域。
接下来就可以调用
cv2.imshow('Filtered Image1', mask)
来显示创建的掩膜。
可以看到盲道的主要部分已经被区分出来了,接下来给出这部分的完整代码
import cv2
import numpy as np
frame = cv2.imread('images/test_photo.jpg')
frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
cv2.imshow("Original image", frame_hsv)
# 定义黄色的HSV范围
lower_yellow = np.array([17, 117, 238], dtype=np.uint8)
upper_yellow = np.array([19, 158, 255], dtype=np.uint8)
# 根据HSV范围创建掩膜
mask = cv2.inRange(frame_hsv, lower_yellow, upper_yellow) #lower_yellow, upper_yellow是为了代码的美观而定义的
cv2.imshow('Filtered Image1', mask)
不过我么看到,虽然区别出来的图像具备盲道的很多纹理,看起来非常逼真,但是在计算机看来,那些纹理(黑色部分)就不是盲道,所以我们就需要对其进行膨胀操作,通俗的来讲,就是填满那些纹路。将其变成几何图形。
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
mask = cv2.dilate(mask, kernel, iterations=5)
cv2.getStructuringElement()
cv2.getStructuringElement()
是一个用于生成结构元素的函数。结构元素是用来与原图像进行数学形态学操作(如膨胀、腐蚀等)的核。在这个例子中,创建的是一个矩形的结构元素。
-
参数:
-
cv2.MORPH_RECT
: 指定结构元素的形状为矩形。 -
(3, 3)
: 结构元素的大小,即宽度和高度。
-
cv2.dilate()
cv2.dilate()
函数用于执行二值图像的膨胀操作。膨胀是一种形态学变换,可以用来扩大图像中的白色区域(前景)。这通常用于连接相邻的物体、填充小洞或者扩大边界。
-
参数:
-
mask
: 这是输入的二值图像,即要进行膨胀操作的对象。 -
kernel
: 用于膨胀操作的结构元素。 -
iterations=5
: 膨胀操作重复执行的次数。迭代次数越多,膨胀的效果越明显。
-
同样我们可以加上
cv2.imshow('Filtered Image2', mask)
来查看效果
可以看到,程序已经识别出了盲道(膨胀后)
下面是识别盲道的完整示例代码:
import cv2
import numpy as np
frame = cv2.imread('images/test_photo.jpg')
frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
cv2.imshow("Original image", frame_hsv)
# 定义黄色的HSV范围
lower_yellow = np.array([17, 117, 238])
upper_yellow = np.array([19, 158, 255])
# 根据HSV范围创建掩膜
mask = cv2.inRange(frame_hsv, lower_yellow, upper_yellow)
cv2.imshow('Filtered Image1', mask)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
mask = cv2.dilate(mask, kernel, iterations=5)
cv2.imshow('Filtered Image2', mask)
三、拓展:识别他物
体验了识别盲道后,相信这一部分会很轻松!
还是上面的代码:
import cv2
import numpy as np
frame = cv2.imread('images/test_photo.jpg')
frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
cv2.imshow("Original image", frame_hsv)
# 定义黄色的HSV范围
lower_yellow = np.array([17, 117, 238])
upper_yellow = np.array([19, 158, 255])
# 根据HSV范围创建掩膜
mask = cv2.inRange(frame_hsv, lower_yellow, upper_yellow)
cv2.imshow('Filtered Image1', mask)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
mask = cv2.dilate(mask, kernel, iterations=5)
cv2.imshow('Filtered Image2', mask)
只不过我们可以修改代码,让代码可以识别特定颜色。
我们先放一放上面这段代码,想想如何快速获得hsv的颜色信息呢?
为此我们可以为此新建一个程序color_selection.py
import cv2
# 读取图像
image = cv2.imread('your_image_path(替换为你图片的路径)')
# 将图像从BGR颜色空间转换为HSV颜色空间
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# 获取鼠标点击事件
def mouse_callback(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
# 获取点击位置的HSV值
hsv_value = hsv[y, x]
print(hsv_value)
# 创建一个窗口并绑定鼠标回调函数
cv2.namedWindow('image')
cv2.setMouseCallback('image', mouse_callback)
# 显示图像
cv2.imshow('image', image)
# 等待按键,然后关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
其中定义(使用)了一个名为mouse_callback的函数,event == cv2.EVENT_LBUTTONDOWN
可以检测鼠标左键是否按下,hsv_value = hsv[y, x]
可以获取鼠标所指位置的hsv颜色。
运行程序,可以发现程序可以正常返回所需的值,然后我们只需要修改lower_yellow
和upper_yellow
(因为此时已经不只检测黄色可以考虑将变量改为lower_color
和upper_color
)的值就可以了比如:
import cv2
import numpy as np
frame = cv2.imread('images/test_photo.jpg')
frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
cv2.imshow("Original image", frame_hsv)
# 定义黄色的HSV范围
lower_yellow = np.array([16, 88, 168]) #注意此处的更改
upper_yellow = np.array([17, 98, 190]) #注意此处的更改
# 根据HSV范围创建掩膜
mask = cv2.inRange(frame_hsv, lower_yellow, upper_yellow)
cv2.imshow('Filtered Image1', mask)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
mask = cv2.dilate(mask, kernel, iterations=5)
cv2.imshow('Filtered Image2', mask)
可以检测示例图片中的墙壁(不太准,大家可自行修改)
我们可以将这个程序与前面的ColorDetection.py结合,实现类似指哪打哪的功能。
在编写具体程序前,我们需要考虑一个问题,就是一次提取只能提取一种hsv颜色,那如何取到最高值和最小值呢?
笔者觉得5次可以
所以我们让color_selection.py检测按了多少次鼠标
首先在mouse_callback定义一个计数器c,检测按了多少次,并且在大小大于4时返回,在每次获取颜色后加1
i = 0
...
if i > 4:
...
...
i += 1
def mouse_callback(event, x, y, flags, param):
i = 0
if i > 4:
pass
if event == cv2.EVENT_LBUTTONDOWN:
# 获取点击位置的HSV值
hsv_value = hsv[y, x]
i += 1
接着定义lower_h
, upper_h
, lower_s
, upper_s
, lower_v
, upper_v
,分别代表h, s, v的最大值和最小值(h, s, v介绍看前文)
lower_h = 300
upper_h = 0
lower_s = 300
upper_s = 0
lower_v = 300
upper_v = 0
接着通过类似打擂台的方式赋值最终的lower_h
, upper_h
, lower_s
, upper_s
, lower_v
, upper_v
if hsv_value[0] < lower_h:
lower_h = hsv_value[0]
if hsv_value[0] > upper_h:
upper_h = hsv_value[0]
if hsv_value[1] < lower_s:
lower_s = hsv_value[1]
if hsv_value[1] > upper_s:
upper_s = hsv_value[1]
if hsv_value[2] < lower_v:
lower_v = hsv_value[2]
if hsv_value[2] > upper_v:
upper_v = hsv_value[2]
在if i > 4:
下输出返回值
if i > 4:
return lower_h, upper_h, lower_s, upper_s, lower_v, upper_v
完整代码
def mouse_callback(event, x, y, flags, param):
i = 0
lower_h = 300
upper_h = 0
lower_s = 300
upper_s = 0
lower_v = 300
upper_v = 0
if i > 4:
return lower_h, upper_h, lower_s, upper_s, lower_v, upper_v
if event == cv2.EVENT_LBUTTONDOWN:
# 获取点击位置的HSV值
hsv_value = hsv[y, x]
if hsv_value[0] < lower_h:
lower_h = hsv_value[0]
if hsv_value[0] > upper_h:
upper_h = hsv_value[0]
if hsv_value[1] < lower_s:
lower_s = hsv_value[1]
if hsv_value[1] > upper_s:
upper_s = hsv_value[1]
if hsv_value[2] < lower_v:
lower_v = hsv_value[2]
if hsv_value[2] > upper_v:
upper_v = hsv_value[2]
print("Lower H: ", lower_h)
print("Upper H: ", upper_h)
print("Lower S: ", lower_s)
print("Upper S: ", upper_s)
print("Lower V: ", lower_v)
print("Upper V: ", upper_v)
i += 1
不过将lower_h
, upper_h
, lower_s
, upper_s
, lower_v
, upper_v
在函数内定义的话,因为外部无法访问函数内的变量,所以可以定义全局变量,再在函数内调用(global)
import cv2
import numpy as np
# 读取图像
frame = cv2.imread('images/test_photo.jpg')
# 将图像从BGR颜色空间转换为HSV颜色空间
frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower_h = 300
upper_h = 0
lower_s = 300
upper_s = 0
lower_v = 300
upper_v = 0
# 获取鼠标点击事件
def mouse_callback(event, x, y, flags, param):
i = 0
global lower_h, upper_h, lower_s, upper_s, lower_v, upper_v #global就是让全局变量能在函数内使用(全局)的方法
if event == cv2.EVENT_LBUTTONDOWN:
# 获取点击位置的HSV值
hsv_value = frame_hsv[y, x]
if hsv_value[0] < lower_h:
lower_h = hsv_value[0]
if hsv_value[0] > upper_h:
upper_h = hsv_value[0]
if hsv_value[1] < lower_s:
lower_s = hsv_value[1]
if hsv_value[1] > upper_s:
upper_s = hsv_value[1]
if hsv_value[2] < lower_v:
lower_v = hsv_value[2]
if hsv_value[2] > upper_v:
upper_v = hsv_value[2]
# print("Lower H: ", lower_h)
# print("Upper H: ", upper_h)
# print("Lower S: ", lower_s)
# print("Upper S: ", upper_s)
# print("Lower V: ", lower_v)
# print("Upper V: ", upper_v)
i += 1
# 创建一个窗口并绑定鼠标回调函数
cv2.namedWindow('image')
cv2.setMouseCallback('image', mouse_callback)
# 显示图像
cv2.imshow('image', frame)
# 等待按键,然后关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
print(lower_h) #测试用,后续可删
接下来就简单了,只需要粘贴入ColorDetection.py就行了,完整代码
import cv2
import numpy as np
# 读取图像
frame = cv2.imread('images/test_photo.jpg')
# 将图像从BGR颜色空间转换为HSV颜色空间
frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower_h = 300
upper_h = 0
lower_s = 300
upper_s = 0
lower_v = 300
upper_v = 0
# 获取鼠标点击事件
def mouse_callback(event, x, y, flags, param):
i = 0
global lower_h, upper_h, lower_s, upper_s, lower_v, upper_v
if i > 4:
return
if event == cv2.EVENT_LBUTTONDOWN:
# 获取点击位置的HSV值
hsv_value = frame_hsv[y, x]
if hsv_value[0] < lower_h:
lower_h = hsv_value[0]
if hsv_value[0] > upper_h:
upper_h = hsv_value[0]
if hsv_value[1] < lower_s:
lower_s = hsv_value[1]
if hsv_value[1] > upper_s:
upper_s = hsv_value[1]
if hsv_value[2] < lower_v:
lower_v = hsv_value[2]
if hsv_value[2] > upper_v:
upper_v = hsv_value[2]
# print("Lower H: ", lower_h)
# print("Upper H: ", upper_h)
# print("Lower S: ", lower_s)
# print("Upper S: ", upper_s)
# print("Lower V: ", lower_v)
# print("Upper V: ", upper_v)
i += 1
# 创建一个窗口并绑定鼠标回调函数
cv2.namedWindow('image')
cv2.setMouseCallback('image', mouse_callback)
# 显示图像
cv2.imshow('image', frame)
# 等待按键,然后关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
lower_yellow = np.array([lower_h, lower_s, lower_v])
upper_yellow = np.array([upper_h, upper_s, upper_v])
mask = cv2.inRange(frame_hsv, lower_yellow, upper_yellow)
cv2.imshow('Filtered Image1', mask)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
mask = cv2.dilate(mask, kernel, iterations=5)
cv2.imshow('Filtered Image2', mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
提示:在选择完颜色后需要先关闭颜色选择窗口,才能显示区分内容
盲道识别示例