背景
项目中有一个场景需要判断红绿灯的状态。并且代码是java开发的,考虑了良久,决定使用像素点的rgb值来做,但是rgb值的颜色范围不好判断,所以就想到把rgb转成hsv来判断。那么会有如下问题:
RGB转HSV
首先解决一个问题,就是java中RGB怎么转HSV。
HSV(Hue, Saturation, Value)是一种描述颜色的模型,与常见的RGB(Red, Green, Blue)颜色模型不同。HSV模型更贴近人类感知颜色的方式,因为它将颜色的属性分解为更容易理解的部分。
这三个分量的含义如下:
- Hue(色调): 表示颜色的种类或名称。它是一个从0到360度的角度值,其中0和360是红色,120是绿色,240是蓝色,以此类推。色调决定了颜色是红色、绿色、蓝色等等。
![1](http://admin.liuhaihua.cn/wp-content/uploads/2023/12/1.png)
2.
Saturation(饱和度): 表示颜色的鲜艳程度或灰度。它是一个百分比值,从0%(灰度)到100%(全彩)变化。饱和度为0%时,颜色是灰阶的。
![2](http://admin.liuhaihua.cn/wp-content/uploads/2023/12/2.png)
3.
Value(明度): 表示颜色的亮度。它也是一个百分比值,从0%(黑色)到100%(最大亮度)变化。明度为0%时,颜色是黑色的。
![3](http://admin.liuhaihua.cn/wp-content/uploads/2023/12/3.png)
HSV模型的优势之一是,通过调整这三个参数,可以更直观地控制颜色的外观。在某些情况下,人们更容易使用HSV来选择或描述颜色,因为它更符合我们对颜色的主观感受。
![4](http://admin.liuhaihua.cn/wp-content/uploads/2023/12/4.png)
opencv官网给出了RGB转HSV的公式
![5](http://admin.liuhaihua.cn/wp-content/uploads/2023/12/5.png)
下面是我基于公式写的java代码,供参考:
public static float[] rgbToHsv(Color color) {
int R = color.getRed();
int G = color.getGreen();
int B = color.getBlue();
float R_1 = R / 255f;
float G_1 = G / 255f;
float B_1 = B / 255f;
float[] all = {R_1, G_1, B_1};
float max = all[0];
float min = all[0];
for (float v : all) {
if (max <= v) {
max = v;
}
if (min >= v) {
min = v;
}
}
float C_max = max;
float C_min = min;
float diff = C_max - C_min;
float hue = 0f;
if (diff == 0f) {
hue = 0f;
} else {
if (C_max == R_1) {
hue = (((G_1 - B_1) / diff) % 6) * 60f;
}
if (C_max == G_1) {
hue = (((B_1 - R_1) / diff) + 2f) * 60f;
}
if (C_max == B_1) {
hue = (((R_1 - G_1) / diff) + 4f) * 60f;
}
}
if (hue < 0f){
hue += 360f;
}
float saturation;
if (C_max == 0f) {
saturation = 0f;
} else {
saturation = diff / C_max;
}
return new float[]{hue, saturation, C_max};
}
hsv如何判断红绿灯的颜色
经过多次测试和对比,我得出如下数据阈值的范围可以
表达红色
h(0,50)and (300,360)
s (0.50,1.0)
v (0.50,1.0)
绿色的阈值如下:
h (80,180)
s (0.50,1.0)
v (0.50,1.0)
测试工具网站:可以直观的看到hsv值对应的颜色
HSV到RGB转换| 颜色转换 (rapidtables.org)
业务逻辑判断红绿灯的颜色
首先是需要对红绿灯进行画框表示 下面是简单的代码demo
public static boolean judeRedLight(String url, List<Integer> rectList){
try {
URL myurl = new URL(url);
BufferedImage srcImg = ImageIO.read(myurl);
return judeRedLight(srcImg, rectList.get(0), rectList.get(1), rectList.get(2), rectList.get(3));
} catch (IOException e) {
log.error("图片地址访问异常,url:" + url );
}
return false;
}
public static boolean judeRedLight(BufferedImage bi,int x1, int y1, int x2, int y2){
int red = 0;
int green = 0;
try{
for (int i = x1; i < x2; i++) {
for (int j = y1; j < y2; j++) {
Color color = new Color(bi.getRGB(i, j));
float[] hsv = rgbToHsv(color);
if (isRed(hsv)){
red++;
}
if (isGreen(hsv)){
green++;
}
}
}
}catch (Exception e){
log.info("判断红绿灯异常:{}",e.getMessage());
}
log.info("判断结果:red:{} green:{}",red,green);
return red > green;
}
经过上述的两个方法即可判断出当前的红绿灯。但是需要提前获取到红绿灯框的坐标