在計(jì)算機(jī)視覺(jué)中,顏色提取是一種常用的圖像處理技術(shù),其中HSV(色相、飽和度、明度)是一種常見(jiàn)的顏色空間,用于描述和提取圖像中的顏色信息。
HSV顏色空間將顏色表示為三個(gè)分量:色相(H)、飽和度(S)和明度(V)。
?色相(Hue):色相是顏色的類型或類別,描述了顏色在光譜中的位置。在HSV模型中,色相表示為一個(gè)角度值,通常在0到360度之間,將整個(gè)顏色光譜分為不同的顏色區(qū)域,如紅、橙、黃、綠、藍(lán)、紫等。
?飽和度(Saturation):飽和度表示顏色的純度或強(qiáng)度,即顏色的深淺程度。飽和度為0時(shí),顏色變?yōu)榛译A;飽和度為最大值時(shí),顏色呈現(xiàn)出最鮮艷的狀態(tài)。飽和度的取值范圍通常在0到1之間,也可以表示為0%到100%。
?明度(Value):明度表示顏色的明亮度,即顏色的亮度和深度。明度為0時(shí),顏色為黑色;明度為最大值時(shí),顏色為白色。明度的取值范圍通常在0到1之間,也可以表示為0%到100%。
通過(guò)這三個(gè)參數(shù)的組合,可以在HSV模型中精確地描述各種顏色,使得調(diào)整和理解顏色更加直觀和容易。在圖像處理中,HSV模型常用于調(diào)整圖像的顏色平衡、色調(diào)和亮度等方面。
硬件環(huán)境
操作環(huán)境及軟硬件配置如下:
?PC:Ubuntu (≥20.04) + ROS2 (≥Foxy)
代碼
import cv2
import numpy as np
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
from collections import deque
import textwrap
class GetHsv(Node):
def __init__(self):
super().__init__('get_hsv')
self.get_logger().info("Start get HSV")
self.bridge = CvBridge()
self.image_sub = self.create_subscription(Image, '/image_raw', self.image_callback, 10)
self.pub = self.create_publisher(Image, '/camera/process_image', 10)
self.count = 0
self.box_width = 60
self.collect_times = 300
self.HSV_value = deque(maxlen=self.collect_times)
self.H_range, self.S_range, self.V_range = 10, 80, 80
self.lower_HSV = None
self.upper_HSV = None
def image_callback(self, msg):
image = self.bridge.imgmsg_to_cv2(msg, 'bgr8')
if self.count < self.collect_times:
self.draw_rectangle(image)
elif self.count < self.collect_times + self.collect_times:
self.collect_hsv_value(image)
else:
self.save_hsv_value(image)
self.count += 1
self.pub.publish(self.bridge.cv2_to_imgmsg(image, 'bgr8'))
def draw_rectangle(self, image):
text = 'please put the color being tested in the rectangle box!'
self.add_text(image, text)
rect_x = (image.shape[1] - self.box_width) // 2
rect_y = (image.shape[0] - self.box_width) // 2
cv2.rectangle(image, (rect_x, rect_y), (
rect_x + self.box_width, rect_y + self.box_width), (0, 255, 0), 3)
def collect_hsv_value(self, image):
text = 'HSV_value is collecting!'
self.add_text(image, text)
frame_x = (image.shape[1] - self.box_width) // 2
frame_y = (image.shape[0] - self.box_width) // 2
frame = image[frame_y:frame_y + self.box_width, frame_x:frame_x + self.box_width]
self.HSV_value.append(self.calculate_mean_hsv(frame))
def save_hsv_value(self, image):
text = 'HSV_value is collected!'
self.add_text(image,text)
mean_HSV = np.mean(self.HSV_value, axis=0)
self.lower_HSV, self.upper_HSV = self.calculate_hsv_range(mean_HSV)
self.write_hsv_to_file(self.lower_HSV, self.upper_HSV)
def calculate_mean_hsv(self, img):
hsv_image = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
return np.mean(hsv_image, axis=(0, 1))
def calculate_hsv_range(self, HSV_value):
lower_H = np.clip(int(HSV_value[0] - self.H_range), 0, 180)
upper_H = np.clip(int(HSV_value[0] + self.H_range), 0, 180)
lower_S = np.clip(int(HSV_value[1] - self.S_range), 40, 255)
upper_S = np.clip(int(HSV_value[1] + self.S_range), 40, 255)
lower_V = np.clip(int(HSV_value[2] - self.V_range), 40, 255)
upper_V = np.clip(int(HSV_value[2] + self.V_range), 40, 255)
return np.array([lower_H, lower_S, lower_V]), np.array([upper_H, upper_S, upper_V])
def write_hsv_to_file(self, lower_HSV, upper_HSV):
content = f"block_HSV is : {lower_HSV[0]},
{lower_HSV[1]},{lower_HSV[2]} {upper_HSV[0]},{upper_HSV[1]},{upper_HSV[2]}
"
filename = "/userdata/dev_ws/color_block_HSV.txt"
with open(filename, "w+") as f:
f.write(content)
self.get_logger().info(f"HSV value has been saved in {filename}")
self.get_logger().info(content)
def add_text(self, image, text):
text = textwrap.wrap(text, width = 80)
text_y = image.shape[0] // 2 - 30
for line in text:
text_size, _ = cv2.getTextSize(line, cv2.FONT_HERSHEY_SIMPLEX, 1.2, 2)
text_x = (image.shape[1] - text_size[0]) // 2
cv2.putText(image, line, (text_x, text_y),
cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 255, 0), 2, cv2.LINE_AA)
def main(args=None):
rclpy.init(args=args)
get_hsv = GetHsv()
rclpy.spin(get_hsv)
get_hsv.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
簡(jiǎn)要說(shuō)明
這段代碼的作用是通過(guò)將相機(jī)對(duì)準(zhǔn)你希望捕捉的圖像HSV值,進(jìn)行一輪采集后便會(huì)將采集的HSV值存放在指定路徑了。
1、為什么要做HSV值獲取,HSV色彩空間相對(duì)于rgb色域有什么有特殊點(diǎn)嗎?
簡(jiǎn)單來(lái)說(shuō),HSV色彩空間相對(duì)于RGB色域更符合人類對(duì)顏色的感知方式,更方便地進(jìn)行顏色處理和分析,并且提供了更直觀和靈活的顏色定義和比較方式。舉個(gè)例子,人描述一個(gè)顏色通常不會(huì)直接說(shuō)它的rgb值,而是描述淡綠色,艷紅色等,而這正是HSV色彩空間描述的方式。
2、應(yīng)用HSV時(shí)為什么通常使用upper、lower兩組數(shù)據(jù)?
使用兩組閾值的好處是可以更準(zhǔn)確地定義要提取的顏色范圍,同時(shí)考慮到色調(diào)環(huán)的邊界情況。這樣可以避免顏色提取結(jié)果受到色調(diào)環(huán)邊界的影響,提高顏色提取的準(zhǔn)確性和穩(wěn)定性。
3、是否可以將圖像直接使用opencv做處理?
還需要解耦圖像信息,例如ROS的圖像需要使用CVBridge處理成Opencv能處理的圖片,又比如在處理NV12時(shí),需要將Hbmem轉(zhuǎn)成常規(guī)opencv識(shí)別的nv12格式。但無(wú)一例外我們可以稱之為解耦了。
4、 HSV如何和RGB進(jìn)行轉(zhuǎn)換呢?
首先,將HSV中的色相、飽和度和明度分別歸一化到區(qū)間[0, 1]。
歸一化的色相(H’):H' = H / 360
歸一化的飽和度(S’):S' = S / 100
歸一化的明度(V’):V' = V / 100
接下來(lái),使用以下公式計(jì)算RGB值:
如果飽和度為0,則RGB值為灰度色:
R = G = B = V'
如果飽和度不為0,則使用以下公式:
C = V' * S'
X = C * (1 - |(H' mod 2) - 1|
m = V' - C
然后,根據(jù)色相的不同情況,計(jì)算最終的RGB值:
當(dāng)0 <= H' < 1/6 時(shí):(R', G', B') = (C, X, 0)
當(dāng) 1/6 <= H' < 2/6 時(shí):(R', G', B') = (X, C, 0)
當(dāng) 2/6 <= H' < 3/6 時(shí):(R', G', B') = (0, C, X)
當(dāng) 3/6 <= H' < 4/6 時(shí):(R', G', B') = (0, X, C)
當(dāng) 4/6 <= H' < 5/6 時(shí):(R', G', B') = (X, 0, C)
當(dāng) 5/6 <= H' <= 1 時(shí):(R', G', B') = (C, 0, X)
最后,將得到的RGB值映射到區(qū)間[0, 255]。
-
圖像處理
+關(guān)注
關(guān)注
27文章
1289瀏覽量
56724 -
計(jì)算機(jī)視覺(jué)
+關(guān)注
關(guān)注
8文章
1698瀏覽量
45982 -
HSV
+關(guān)注
關(guān)注
0文章
10瀏覽量
2603
原文標(biāo)題:ROS2 HSV值獲取
文章出處:【微信號(hào):vision263com,微信公眾號(hào):新機(jī)器視覺(jué)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論