Java RGB 转飞利浦 Hue (HSB)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22564187/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
RGB to Philips Hue (HSB)
提问by tim
I'm making a musicplayer in Processing for an assignment for school. The Philips Hue lights will make some corresponding visual effects. I wanted to make the visuals kinda unique for each song. So I fetched the cover art (using LastFM API) of the playing track to get the most frequent color and use this as a base for creating the other colors. The Philips Hue has a different way of showing colors namely (HSB). So I converted it via
我正在 Processing 中制作一个音乐播放器,用于学校作业。飞利浦 Hue 灯会产生一些相应的视觉效果。我想让每首歌的视觉效果都独一无二。因此,我获取了播放曲目的封面艺术(使用 LastFM API)以获得最常见的颜色,并将其用作创建其他颜色的基础。飞利浦 Hue 有一种不同的颜色显示方式,即 (HSB)。所以我通过
Color.RGBtoHSB();
Color.RGBtoHSB();
For ex. it gives me for R= 127, G=190, B=208 the values H= 0.5370371, S=0.38942307, B=0.8156863. Now I'm guessing they were calculated on base 1 so I multiplied the Brightness en Saturation by 255. And the Hue by 65535. (As seen on http://developers.meethue.com/1_lightsapi.html)
例如。它给了我 R= 127、G=190、B=208 的值 H= 0.5370371、S=0.38942307、B=0.8156863。现在我猜它们是基于 1 计算的,所以我将亮度饱和度乘以 255。色相乘以 65535。(如http://developers.meethue.com/1_lightsapi.html 所示)
When setting these calculated values in the Philips Hue no matter what song is playing the color is always redish or white.
在 Philips Hue 中设置这些计算值时,无论播放什么歌曲,颜色始终为红色或白色。
Did something go wrong with the conversion between RGB to HSB?
RGB 到 HSB 之间的转换有问题吗?
On popular request my code:
应大众要求,我的代码:
As a test:
作为测试:
Color c = Colorconverter.getMostCommonColour("urltoimage");
float[] f = Colorconverter.getRGBtoHSB(c);
ArrayList<Lamp> myLamps = PhilipsHue.getInstance().getMyLamps();
State state = new State();
state.setBri((int) Math.ceil(f[2]*255));
state.setSat((int) Math.ceil(f[1]*255));
state.setHue((int) Math.ceil(f[0]*65535));
state.setOn(true);
PhilipsHue.setState(myLamps.get(1), state);
The functions as seen above
上面看到的功能
public static Color getMostCommonColour(String coverArtURL) {
Color coulourHex = null;
try {
BufferedImage image = ImageIO.read(new URL(coverArtURL));
int height = image.getHeight();
int width = image.getWidth();
Map m = new HashMap();
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
int rgb = image.getRGB(i, j);
int[] rgbArr = getRGBArr(rgb);
// No grays ...
if (!isGray(rgbArr)) {
Integer counter = (Integer) m.get(rgb);
if (counter == null) {
counter = 0;
}
counter++;
m.put(rgb, counter);
}
}
}
coulourHex = getMostCommonColour(m);
System.out.println(coulourHex);
} catch (IOException e) {
e.printStackTrace();
}
return coulourHex;
}
private static Color getMostCommonColour(Map map) {
List list = new LinkedList(map.entrySet());
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
return ((Comparable) ((Map.Entry) (o1)).getValue())
.compareTo(((Map.Entry) (o2)).getValue());
}
});
Map.Entry me = (Map.Entry) list.get(list.size() - 1);
int[] rgb = getRGBArr((Integer) me.getKey());
String r = Integer.toHexString(rgb[0]);
String g = Integer.toHexString(rgb[1]);
String b = Integer.toHexString(rgb[2]);
Color c = new Color(rgb[0], rgb[1], rgb[2]);
return c;
}
private static int[] getRGBArr(int pixel) {
int alpha = (pixel >> 24) & 0xff;
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel) & 0xff;
return new int[] { red, green, blue };
}
private static boolean isGray(int[] rgbArr) {
int rgDiff = rgbArr[0] - rgbArr[1];
int rbDiff = rgbArr[0] - rgbArr[2];
// Filter out black, white and grays...... (tolerance within 10 pixels)
int tolerance = 10;
if (rgDiff > tolerance || rgDiff < -tolerance)
if (rbDiff > tolerance || rbDiff < -tolerance) {
return false;
}
return true;
}
public static float[] getRGBtoHSB(Color c) {
float[] hsv = new float[3];
return Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), hsv);
}
The set state just does a simple put to the philips light bulbs. When I check the JSON on the Affected Light bulb
设置状态只是对飞利浦灯泡进行了简单的放置。当我检查受影响灯泡上的 JSON 时
{
"state": {
"on": true,
"bri": 81,
"hue": 34277,
"sat": 18,
"xy": [
0.298,
0.2471
],
"ct": 153,
"alert": "none",
"effect": "none",
"colormode": "hs",
"reachable": true
},
"type": "Extended color light",
"name": "Hue Spot 1",
"modelid": "LCT003",
"swversion": "66010732",
"pointsymbol": {
"1": "none",
"2": "none",
"3": "none",
"4": "none",
"5": "none",
"6": "none",
"7": "none",
"8": "none"
}
}
采纳答案by tim
A special thanks to StackOverflow user, Gee858eeG, to notice my typo and Erickson for the great tips and links.
特别感谢 StackOverflow 用户 Gee858eeG 注意到我的错字和 Erickson 提供的重要提示和链接。
Here is a working function to convert any RGB color to a Philips Hue XY values. The list returned contains just two element 0 being X, 1 being Y. The code is based on this brilliant note: https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/commit/f41091cf671e13fe8c32fcced12604cd31cceaf3
这是将任何 RGB 颜色转换为飞利浦 Hue XY 值的工作函数。返回的列表只包含两个元素 0 是 X,1 是 Y。代码基于这个精彩的笔记:https: //github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/commit/f41091cf671e13fe8c32fcced12604cd31cceaf3
Eventhought this doesn't return the HSB value the XY values can be used as a replacement for changing colors on the Hue.Hopefully it can be helpful for other people, because Philips' API doesn't mention any formula.
尽管这不会返回 HSB 值,但 XY 值可以用作 Hue 上更改颜色的替代品。希望它对其他人有帮助,因为飞利浦的 API 没有提到任何公式。
public static List<Double> getRGBtoXY(Color c) {
// For the hue bulb the corners of the triangle are:
// -Red: 0.675, 0.322
// -Green: 0.4091, 0.518
// -Blue: 0.167, 0.04
double[] normalizedToOne = new double[3];
float cred, cgreen, cblue;
cred = c.getRed();
cgreen = c.getGreen();
cblue = c.getBlue();
normalizedToOne[0] = (cred / 255);
normalizedToOne[1] = (cgreen / 255);
normalizedToOne[2] = (cblue / 255);
float red, green, blue;
// Make red more vivid
if (normalizedToOne[0] > 0.04045) {
red = (float) Math.pow(
(normalizedToOne[0] + 0.055) / (1.0 + 0.055), 2.4);
} else {
red = (float) (normalizedToOne[0] / 12.92);
}
// Make green more vivid
if (normalizedToOne[1] > 0.04045) {
green = (float) Math.pow((normalizedToOne[1] + 0.055)
/ (1.0 + 0.055), 2.4);
} else {
green = (float) (normalizedToOne[1] / 12.92);
}
// Make blue more vivid
if (normalizedToOne[2] > 0.04045) {
blue = (float) Math.pow((normalizedToOne[2] + 0.055)
/ (1.0 + 0.055), 2.4);
} else {
blue = (float) (normalizedToOne[2] / 12.92);
}
float X = (float) (red * 0.649926 + green * 0.103455 + blue * 0.197109);
float Y = (float) (red * 0.234327 + green * 0.743075 + blue * 0.022598);
float Z = (float) (red * 0.0000000 + green * 0.053077 + blue * 1.035763);
float x = X / (X + Y + Z);
float y = Y / (X + Y + Z);
double[] xy = new double[2];
xy[0] = x;
xy[1] = y;
List<Double> xyAsList = Doubles.asList(xy);
return xyAsList;
}
回答by Jeff
Your RGB as HSB should be 193 degrees, 39% and 82% respectively. So at the very least S and B seem correct. Looking at the Philips hue API documentation, you're doing the right thing by multiplying those numbers by 255.
您的 RGB 作为 HSB 应分别为 193 度、39% 和 82%。所以至少 S 和 B 看起来是正确的。查看飞利浦色调 API 文档,您将这些数字乘以 255 是正确的。
To get the H value as degrees, multiply the calculated H value by 360. That's how you arrive at the 193 in your case. Once you have the degrees, you multiply by 182 to get the value that you should send to the Philips hue API (from Hack the Hue):
要获得以度数表示的 H 值,请将计算出的 H 值乘以 360。这就是在您的情况下得出 193 的方法。获得度数后,乘以 182 以获得应发送到飞利浦色调 API(来自Hack the Hue)的值:
hue
The parameters 'hue' and 'sat' are used to set the colour
The 'hue' parameter has the range 0-65535 so represents approximately
182*degrees (technically 182.04 but the difference is imperceptible)
This should give you different H values than you're obtaining with the * 65535
method.
这应该为您提供与使用该* 65535
方法获得的 H 值不同的 H 值。
回答by erickson
I think the problem here is that the Hue has a pretty limited color gamut.It's heavy on reds and purples, but can't produce as much saturation in the blue-green region..
我认为这里的问题是Hue 的色域非常有限。它在红色和紫色上很重,但不能在蓝绿色区域产生那么多的饱和度。
I would suggest setting the saturation to the maximum, 255, and vary only the hue.
我建议将饱和度设置为最大值 255,并且只改变色调。
Based on the table given in the documentation, the Hue's "hue" attribute doesn't map directly to HSV's hue. The approximation might be close enough, but if not, it might be worthwhile to try a conversion to CIE 1931 color space,and then set the "xy" attribute instead of the hue.
根据文档中给出的表格,色调的“色调”属性不会直接映射到 HSV 的色调。近似值可能足够接近,但如果不是,则可能值得尝试转换为 CIE 1931 色彩空间,然后设置“xy”属性而不是色调。