OpenCV的HSV颜色空间在无人车中颜色识别的应用

news2024/11/28 10:40:44

RGB属于三基色空间,是大家最为熟悉的,看到的任何一种颜色都可以由三基色进行混合而成。然而一般对颜色空间的图像进行有效处理都是在HSV空间进行的,HSV(色调Hue,饱和度Saturation,亮度Value)是根据颜色的直观特性创建的一种颜色空间, 也称六角锥体模型

 

在OpenCV中HSV颜色空间的取值范围=> H:[0, 180],S:[0, 255],V:[0, 255]H色调越小越接近于红色,越高越接近于蓝色,这种表达方式也要比单纯使用红色来表示红色更加准确;S饱和度越小颜色越淡,越大颜色越浓;V亮度越小就越暗,越大越明亮。注意观察上面图片颜色的变化!
之所以选择HSV,是因为H代表的色调基本上可以确定某种颜色,再结合饱和度和亮度信息判断大于某一个阈值。而RGB 由三个分量构成, 需要判断每种分量的贡献比例。HSV空间的识别的范围更广,使用起来更方便。

1、实例演示

 我们来看一个实例,拿一包蓝色外包装的宽窄香烟,识别并跟踪颜色。

1.1、颜色识别跟踪

由于本人台式机没有安装摄像头,这里使用无人车上面的摄像头,其获取摄像头视频的方法与OpenCV有点点区别,也差不多,毕竟是在OpenCV的基础上来的。

from jetbotmini import Camera
from jetbotmini import bgr8_to_jpeg
import cv2
import numpy as np
import traitlets
import ipywidgets.widgets as widgets
from IPython.display import display

# 目标颜色,这里设置为蓝色数组
color_lower = np.array([100,43,46])
color_upper = np.array([124, 255, 255])
# 相机实例
camera = Camera.instance(width=720, height=720)
# 显示控件(视频也是图片的连续帧)
color_image = widgets.Image(format='jpeg', width=500, height=400)
display(color_image)

# 实时识别颜色并反馈到上面的控件里
while 1:
    frame = camera.value # (720, 720, 3) (H,W,C)
    frame = cv2.resize(frame, (400, 400)) # (400, 400, 3)
    frame = cv2.GaussianBlur(frame,(5,5),0) # 高斯滤波(5, 5)表示高斯矩阵的长与宽都是5,标准差为0
    hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)#将BGR转成HSV
    mask=cv2.inRange(hsv,color_lower,color_upper)
    mask=cv2.erode(mask,None,iterations=2) # 进行腐蚀操作,去除边缘毛躁
    mask=cv2.dilate(mask,None,iterations=2) # 进行膨胀操作
    mask=cv2.GaussianBlur(mask,(3,3),0)
    cnts=cv2.findContours(mask.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2] # 轮廓
    if len(cnts)>0:
        cnt = max(cnts,key=cv2.contourArea) # 轮廓面积
        (color_x,color_y),color_radius=cv2.minEnclosingCircle(cnt) # 外接圆的位置信息
        if color_radius > 10:
            # 圆圈标注
            cv2.circle(frame,(int(color_x),int(color_y)),int(color_radius),(255,0,255),2)
    color_image.value = bgr8_to_jpeg(frame) # 转成图片传入Image组件

可以看到,粉红色的圆圈,在追踪画面中的蓝色。其中摄像头的用法与widgets中有很多的web交互组件,有兴趣的可以查阅:无人车的摄像头的实时捕获图片以及widgets小部件的相关操作

这段代码的意思就比较清晰了,实例化摄像头,将每一帧图像中按照不同颜色的HSV色域空间进行分类标记出来,通过 cv2.cvtColor(frame,cv2.COLOR_BGR2HSV) 先将BGR(这里读取到的图片是BGR而不是RGB)转换成HSV,将掩码mask和原图片进行按位与操作,找到颜色后,在颜色的轮廓上画出圆形(最小外接圆)进行标注。 如果在环境光充足的情况下,识别效果不太理想的话,我们可以在死循环里面的进行手动的更改一些参数的设置。

1.2、cv2.inRange

接下来对上述代码中的一些函数进行解释
在做mask的时候,mask=cv2.inRange(hsv,color_lower,color_upper) 这个就是低于下边界数组和高于上边界数组的值为0黑色,介于之间的为255白色,属于单通道。我们来看代码直观了解下: 

import cv2
import matplotlib.pyplot as plt

img_cv2 = cv2.imread('test.jpg')
hsv = cv2.cvtColor(img_cv2, cv2.COLOR_BGR2HSV)
lowerb = np.array([20, 20, 20])
upperb = np.array([200, 200, 200])

# 黑白单通道(H,W)
mask = cv2.inRange(hsv, lowerb, upperb)
cv2.imshow('Display', mask)
cv2.waitKey(0)
cv2.destroyAllWindows()

plt.subplot(1,2,1); plt.imshow(img_cv2,aspect='auto');plt.axis('off');plt.title('BGR')
plt.subplot(1,2,2); plt.imshow(mask,aspect='auto');plt.axis('off');plt.title('mask')
plt.show()

 如下图BGR和mask:

 

1.3、cv2.erode和cv2.dilate

腐蚀操作,属于图像形态学,跟字面意思一样,进行腐蚀,我们可以查看下这个函数的帮助:
erode(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]]) -> dst
src原图跟目标图dst的大小是一样的,然后这里的kernel核的大小决定腐蚀的大小,可选。代码测试下:

import cv2
import numpy as np

image = cv2.imread('test.jpg')
kernel = np.ones((5, 5), np.uint8)
image = cv2.erode(image, None)
cv2.imshow('erode', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

其中这里的 image = cv2.erode(image, None) 第二个kernel参数可以指定也可以不指定,指定之后image = cv2.erode(image, kernel),以及修改kernel 中的大小看下效果:

实质也是做卷积运算,对于不是1的操作内的数置0,只有全为1才为1。作用如同代码中的注释,用来去掉边缘一些噪声毛刺等。cv2.dilate膨胀函数,这个可以看成是腐蚀函数的反向操作,腐蚀之后就是黑色部分扩大,膨胀函数就是缩小。

1.4、cv2.GaussianBlur

高斯模糊也是用来去噪的,我们来看下对一张图片加了高斯噪声之后,再通过这个函数来做个去噪的效果,主要针对高斯噪声: 

import cv2  as cv
import numpy as np
 
def myShow(name,img):
    cv.imshow(name,img)
    cv.waitKey(0)
    cv.destroyAllWindows()
# 加高斯噪声
def addGauss(img,mean=0,val=0.01):
    img = img / 255
    gauss = np.random.normal(mean,val**0.05,img.shape)
    img = img + gauss
    return img

img = cv.imread('gauss.png')
img1 = addGauss(img)
myShow('img1',img1)

img2 = cv.GaussianBlur(img1,(3,3),0)
myShow('img2',img2)

这里我将原图和加了高斯噪声与降噪处理的三张图放在了一起,如下图:

1.5、cv2.findContours和drawContours 

查找轮廓函数findContours(image, mode, method[, contours[, hierarchy[, offset]]]) -> contours, hierarchy 是在二值图像中检测,所以我们先将其转成灰度图片,通过threshold转成二值图片。
最后我们将轮廓画出来,使用 drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]]) -> image
我们来看下代码的实现:

import cv2
import numpy as np

image = cv2.imread('test.jpg')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
image = cv2.drawContours(image,contours,-1,(255,0,0),2)

#cv2.imshow('erode',binary)
cv2.imshow('erode',image)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

cv2.RETR_EXTERNAL:检测外轮廓,忽略轮廓内部的结构。
cv2.CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩阵轮廓只需 4 个点来保存轮廓信息。
cv2.CHAIN_APPROX_NONE:存储所有的轮廓点,相邻两个点的像素位置差不超过1

1.6、HSV颜色取值 

这里使用的是蓝色来示例,如果想要其他颜色呢?在HSV中的取值情况是怎么样的,如下图:

 

2、OpenCV知识点

这里用的很多的是OpenCV的一些知识,我们来熟悉下常用的,比如读取图片转成灰色图片。 

2.1、读取和显示图片 

import cv2

img = cv2.imread('test.jpg', 0)
cv2.imshow("image",img)
# 如果注释下面的等待按键和释放窗口资源,会出现“窗口未响应”的状态,不能正常显示图片
cv2.waitKey()
cv2.destroyAllWindows()

 当然如果没有安装这个视觉库的话,报错:ModuleNotFoundError: No module named 'cv2'

安装命令:pip install opencv-python -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com 

读取图片cv2.imread()的时候,第二个参数为0,表示灰度模式,也可以使用cv2.IMREAD_GRAYSCALE 代替0
其他数字表示如下:
1表示彩色图片cv2.IMREAD_GRAYSCALE
2表示不修改模式包括通道cv2.IMREAD_UNCHANGED 

2.2、读取和保存图片

img = cv2.imread('test.jpg', 0)
cv2.imwrite('new.jpg', img)

这样就将以灰度模式读取的图片,通过cv2.imwrite写入的方式,保存为了一张新的图片。

2.3、读取和显示视频 

同样的,我们来看下视频的效果,通俗理解就是视频不断的读取里面的每帧(图片) 

import cv2

cap = cv2.VideoCapture('test.mp4')
print(cap.read()[1].shape) # (1080, 1920, 3)

 这样就读取了视频的一帧,返回值是一个元组,如果读取整个视频呢?我们一起来看下:

import cv2

cap = cv2.VideoCapture('test.mp4')
if (cap.isOpened() == False):
    print('不能打开视频文件')
else:
    fps = cap.get(cv2.CAP_PROP_FPS) # 参数可直接使用5代替
    print('每帧速度:', fps,'FPS') # 每帧速度: 29.97002997002997 FPS
    f_count = cap.get(cv2.CAP_PROP_FRAME_COUNT) # 7
    print('总帧数: ', f_count) #总帧数:  3394.0

while(cap.isOpened()):
    ret, frame = cap.read()
    if ret == True:
        cv2.imshow('Frame',frame)
        key = cv2.waitKey(20)
        # 按q键退出
        if key == ord('q'):break
    else:
        break

# 注意释放资源
cap.release()
cv2.destroyAllWindows()

这样就跟显示图片一样,显示视频了。其中cv2.waitKey(20)表示在连续帧之间等待20毫秒,那这个数值越大,等待时长越大,可以看到视频的播放就变得比较慢了。
当然更关键的是用来获取摄像头监控的视频。

# 参数 0 表示设备的默认摄像头,当设备有多个摄像头时可以改变参数选择
cap = cv2.VideoCapture(0)

当然有些外接摄像头的ID可能不是0,我们可以使用遍历来获取到:

import cv2
ID = 0
while(1):
    cap = cv2.VideoCapture(ID)
    ret, frame = cap.read()
    if ret == False:
        ID += 1
    else:
        print(ID)
        break

 2.4、多张图片合成视频

将一个目录里面的图片写入到视频中,方法跟前面差不多,不过需要注意的是,合成视频的图片尺寸必须一致,也就是说如果目录里面的图片大小各不相同的话,单纯的直接写入视频会失败,所以这里需要将图片裁剪成一样,利用插值法来操作:

import cv2
import os
path ='imgs'
size = (600,400) # (W,H)
fps = 1
#fourcc = cv2.VideoWriter_fourcc('X','V','I','D')
fourcc = cv2.VideoWriter_fourcc(*'XVID')
video = cv2.VideoWriter('hi.avi',fourcc,fps,size)

for item in os.listdir(path):
    if item.lower().endswith('.jpg'):
        img = cv2.imread(os.path.join(path,item))
        img1 = cv2.resize(img, size, interpolation=cv2.INTER_CUBIC)
        print(img1.shape) # (H,W,C)
        video.write(img1)
video.release()
cv2.destroyAllWindows()

其中interpolation参数指定的插值法:

INTER_NEAREST:最邻近插值法
INTER_LINEAR:双线性插值,默认
INTER_CUBIC:4x4像素邻域内的双立方插值
INTER_LANCZOS4:8x8像素邻域内的Lanczos插值

2.5、直线、矩形、圆圈等形状

一些常见的形状,在实际应用中很常见,这里列举如下:

2.5.1、直线

cv2.line(img, startPoint, endPoint, color, thickness)
startPoint	:起始位置像素坐标
endPoint:结束位置像素坐标
color:绘制的颜色
thickness:绘制的线条宽度

2.5.2、圆圈

cv2.circle(img, centerPoint, radius, color, thickness)
img:需要绘制的目标图像对象
centerPoint:绘制的圆的圆心位置像素坐标
radius:绘制的圆半径
color:绘制的颜色
thickness:绘制的线条宽度(thickness 是负数,表示圆被填充)

2.5.3、矩形

cv2.rectangle(img, point1, point2, color, thickness)
img:需要绘制的目标图像对象
point1:左上顶点位置像素坐标
point2:右下顶点位置像素坐标
color:绘制的颜色
thickness:绘制的线条宽度

2.5.4、文本

cv2.putText(img, text, point, font, size, color, thickness)
img:需要绘制的目标图像对象
text:绘制的文字
point:左上顶点位置像素坐标
font:绘制的文字格式
size:绘制的文字大小
color:绘制使用的颜色
thickness:绘制的线条宽度

2.5.5、图片缩放

cv2.resize(InputArray src, OutputArray dst, Size, fx, fy, interpolation)
InputArray src:输入图片
OutputArray dst:输出图片
Size:输出图片尺寸
fx, fy:沿x轴,y轴的缩放系数
interpolation:插值方法

3、bgr8_to_jpeg

其中最后在图片组件中需要显示图片的时候,我们需要将每帧的图像反馈到图片组件里面,然后每帧的图像是BGR,而图像组件格式为jpeg,所以需要做一个转换。
bgr8_to_jpeg这个函数就是将图像编码到内存缓冲区中,实质是一个imencode函数的封装,对图像做压缩处理
函数源码是:

def bgr8_to_jpeg(value, quality=75):
    return bytes(cv2.imencode('.jpg', value)[1])

里面的imencode函数如下:

imencode(ext, img[, params]) -> retval, buf
ext:定义输出格式的文件扩展名
img:要写入的图像
buf:输出缓冲区调整大小,以适应压缩图像

快速查看函数源码或不方便查看源文件的情况:

import inspect
print(inspect.getsource(bgr8_to_jpeg))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/759434.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何撤销git上一次的commit(或已push)

如何撤销git上一次的commit(或已push) 当多人开发时,我们本地commit后,刚要push,发现忘记pull最新代码,此时会有冲突push失败, 我们想要撤销最近的一次commit 我们先简单介绍一下git git有三大…

GreenPlum数据库日常维护

📢📢📢📣📣📣 哈喽!大家好,我是【IT邦德】,江湖人称jeames007,10余年DBA工作经验 一位上进心十足的【大数据领域博主】!😜&#x1f61…

使用 YOLOv8 和 Streamlit 构建实时对象检测和跟踪应用程序:第3部分:添加跟踪算法

介绍 对象跟踪是随着时间的推移识别一系列帧中的特定对象或多个对象的过程。它涉及定位对象在每个帧中的位置并跟踪其跨帧的移动。对象跟踪在各个领域都有广泛的应用,包括监控、机器人、自动驾驶、运动分析等。 跟踪算法使用各种技术(例如颜色直方图、运动分析、深度学习等)…

【多线程】(二)线程安全问题与线程同步

文章目录 一、多线程带来的风险1.1 观察线程不安全1.2 线程安全概念1.3 线程不安全的原因1.4 线程安全的解决方法 二、synchronized关键字2.1 synchronized 的特性2.2 synchronized 使用示例2.3 Java 标准库中的线程安全类 三、volatile关键字3.1 保证内存可见性3.2 禁止指令重…

Java反射的应用:动态代理

代理设计模式的原理 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。 对于静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程…

基于FPGA的按键消抖

文章目录 基于FPGA的按键消抖一、按键消抖原理二、按键消抖代码三、仿真代码编写四:总结 基于FPGA的按键消抖 一、按键消抖原理 按键抖动:按键抖动通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性…

怎么使用Netty解码自定义通信协议

网络协议的基本要素 一个完备的网络协议需要具备哪些基本要素 魔数:魔数是通信双方协商的一个暗号,通常采用固定的几个字节表示。魔数的作用是防止任何人随便向服务器的端口上发送数据。协议版本号:随着业务需求的变化,协议可能…

SAP顾问生涯闲记:在SAP工作是什么体验

又有一段时间没更新自己的公众号了,为什么突然决定新开一篇SAP顾问生涯闲记系列的文章呢,是因为最近很荣幸地当选了SAP雇主品牌推广大使,作为SAP官方的推广大使在收获这份荣誉的同时,也承担了一些工作以及责任。 集结完毕︱SAP雇…

Flask_实现token鉴权

目录 1、安装依赖 2、实现代码 3、测试 源码等资料获取方法 1、安装依赖 pip install flask pip install pycryptodome 2、实现代码 import random import string import time import base64from functools import wrapsfrom flask import Flask, jsonify, session, req…

苍穹外卖day02——员工管理功能代码开发+分类管理代码导入

目录 新增员工——需求分析与设计 产品原型 接口设计: 数据库设计: 新增员工——代码开发 在Controller层中 在Service层中 在Mapper层中 功能测试 接口文档测试: 前后端联调测试: 新增员工——代码完善 ​编辑 第一个问题 第二个问题 员工分页查询 需求分析与设计 …

PostgreSQL考试难不难 ?

当涉及到PostgreSQL考试的详细难度,以下是一些可能涉及的主题和考点,这些主题在不同的考试中可能有所不同: 1.数据库基础知识:数据库的基本概念、关系型数据库模型、表、字段、主键、外键等。 2.SQL语言:对SQL语言的掌…

数据集——个人收集标注与使用过的数据集

前言 这是一个我个人在工作和学习中使用过以数据集的一部分,有语义分割,目标识别,人像抠图等几个大类,这只是我用过数据集中的一部分,这些数据集有小一部分是来源自网络,很大一部分都是我自己收集。 一、…

【动手学深度学习】--05.权重衰退

文章目录 权重衰退1.原理1.1使用均方范数作为硬性限制1.2使用均方范数作为柔性限制1.3对最优解的影响1.4参数更新法则 2.从零开始实现权重衰退2.1初始化模型参数2.2定义L2范数惩罚2.3训练2.4忽略正则化直接训练2.5使用权重衰退 3.简洁实现 权重衰退 学习视频:权重衰…

在网格化数据集上轻松执行 2D 高通、低通、带通或带阻滤波器研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

ModaHub魔搭社区:AI原生云向量数据库Zilliz Cloud设置白名单

目录 前提条件 操作步骤 下一步 在 Zilliz Cloud 中,白名单是针对项目的安全设置,适用于项目下的所有集群。设置白名单后,仅白名单中的 IP 地址可以访问您项目下的所有集群。白名单能够有效降低受到恶意攻击的风险 本教程将介绍如何设置白名单。 前提条件 确保满足以…

EasyX测试布局代码

#include <iostream> #include <algorithm> #include <graphics.h> // 引用图形库头文件 #include <conio.h> #include <unordered_map> #include <Windows.h> #include "layout/LayoutSystem.h"#define DEFAULT_PANELS_LAYOUT…

谈二级索引

前提&#xff1a; 在数据库中&#xff0c;1、索引分为聚簇索引和非聚簇索引两类。2、所有索引的数据结构都是树&#xff0c;查找树上的节点数据时通过用二分法来锁定数据范围&#xff0c;指定数据排序的规则&#xff0c;比如&#xff1a;有小到大&#xff0c;对比之后最终确定…

Sequencer使用心得

在关卡序列中设置了触发蓝图的关键帧&#xff0c;为什么播放的时候没有触发蓝图事件呢&#xff1f; 在关卡序列中触发蓝图&#xff0c;一般是将蓝图添加到轨道中&#xff0c;设置触发器&#xff0c;在对应的关键帧中&#xff0c;绑定蓝图事件。 一般的话&#xff0c;点击播…

栈、队列、优先级队列详解【c++】

目录 &#x1f3c0;stack的介绍和使用⚽stack的介绍⚽stack的使用 &#x1f3c0;queue的介绍和使用⚽queue的介绍⚽queue的使用 &#x1f3c0;priority_queue的介绍和使用⚽priority_queue的介绍⚽priority_queue的使用 &#x1f3c0;总结 &#x1f3c0;stack的介绍和使用 ⚽s…