如何在Java中创建X%的灰色?
假设我想要Java中25%或31%的灰色 下面的代码显示如何在Java中创建X%的灰色?,java,rgb,color-space,hsb,Java,Rgb,Color Space,Hsb,假设我想要Java中25%或31%的灰色 下面的代码显示 BufferedImage image = new BufferedImage(2, 2, BufferedImage.TYPE_BYTE_GRAY); image.setRGB(0, 0, new Color(0,0,0).getRGB()); image.setRGB(1, 0, new Color(50, 50, 50).getRGB()); image.setRGB(0, 1, new Color
BufferedImage image = new BufferedImage(2, 2, BufferedImage.TYPE_BYTE_GRAY);
image.setRGB(0, 0, new Color(0,0,0).getRGB());
image.setRGB(1, 0, new Color(50, 50, 50).getRGB());
image.setRGB(0, 1, new Color(100,100,100).getRGB());
image.setRGB(1, 1, new Color(255,255,255).getRGB());
Raster raster = image.getData();
double[] data = raster.getPixels(0, 0, raster.getWidth(), raster.getHeight(), (double[]) null);
System.out.println(Arrays.toString(data));
显然,RGC与密度(?)非线性有关
[0.0, 8.0, 32.0, 255.0]
那么,如何创建给定密度的颜色呢
更新
我尝试过@icza和@hlg提出的方法,我还发现了另一种方法:
double[] data;
Raster raster;
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY);
float[] grays = {0, 0.25f, 0.5f, 0.75f, 1};
ColorSpace linearRGB = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
ColorSpace GRAY = ColorSpace.getInstance(ColorSpace.CS_GRAY);
Color color;
int[] rgb;
for(int i=0; i<grays.length; ++i) {
System.out.println("\n\nShould be " + (grays[i]*100) + "% gray");
color = new Color(linearRGB, new float[] {grays[i], grays[i], grays[i]}, 1f);
image.setRGB(0, 0, color.getRGB());
raster = image.getData();
data = raster.getPixels(0, 0, 1, 1, (double[]) null);
System.out.println("data by CS_LINEAR_RGB (hlg method) = " + Arrays.toString(data));
color = new Color(GRAY, new float[] {grays[i]}, 1f);
image.setRGB(0, 0, color.getRGB());
raster = image.getData();
data = raster.getPixels(0, 0, 1, 1, (double[]) null);
System.out.println("data by CS_GRAY = " + Arrays.toString(data));
rgb = getRGB(Math.round(grays[i]*255));
color = new Color(rgb[0], rgb[1], rgb[2]);
image.setRGB(0, 0, color.getRGB());
raster = image.getData();
data = raster.getPixels(0, 0, 1, 1, (double[]) null);
System.out.println("data by icza method = " + Arrays.toString(data));
}
现在我想知道哪一个是正确的
更新2
对不起,灰/白百分比当然应该颠倒。将RGB颜色转换为灰度时,使用以下权重: 0.2989,0.5870,0.1140 资料来源: 在维基百科上: 正式地说:
gray = 0.2989*R + 0.5870*G + 0.1140*B
基本上你需要的是这个函数的倒数。您需要找到R、G和B值,这些值给出了您要查找的结果灰色值。由于方程中有3个参数,因此在大多数情况下,会有大量RGB值,这将导致您要查找的灰度值
试想一下:一个具有高R分量的RGB颜色,并且G和B中没有一个给出灰色,可能存在另一个具有某些G分量的RGB颜色,并且R和B中没有一个给出相同的灰色,因此有多种可能的RGB解决方案可用于所需的灰色
算法
这里有一个可能的解决方案。它所做的是尝试将第一个RGB组件设置为同样大,因此乘以其权重将返回灰色
。如果其“溢出”超过255,则会被切割,我们使用组件的最大值可以“表示”的量减少灰色
,并尝试使用剩余的灰色
量对下一个组件执行此操作
这里我使用了灰色
输入范围0..255
。如果要以百分比为单位指定,只需将其转换为gray=255*percent/100
private static double[] WEIGHTS = { 0.2989, 0.5870, 0.1140 };
public static int[] getRGB(int gray) {
int[] rgb = new int[3];
for (int i = 0; i < 3; i++) {
rgb[i] = (int) (gray / WEIGHTS[i]);
if (rgb[i] < 256)
return rgb; // Successfully "distributed" all of gray, return it
// Not quite there, cut it...
rgb[i] = 255;
// And distribute the remaining on the rest of the RGB components:
gray -= (int) (255 * WEIGHTS[i]);
}
return rgb;
}
结果显示了基于该算法我们所期望的结果:R分量首先是“填充”的,一旦达到255,G分量将被“填充”,最后G分量将被使用。一种颜色有一个特定的亮度,如果颜色更灰,您希望保留该亮度
亮度可能类似于:
Y = 0.2989*R + 0.5870*G + 0.1140*B
Y = 0.2126*R + 0.7152*G + 0.0722*B
因此,新颜色(Y,Y,Y)
对应于具有相同亮度的灰度值。
灰显到特定百分比是一种插值
Color grayed(Color color, int perc) {
double percGrayed = perc / 100.0;
double percColored = 1.0 - percGrayed;
double[] weights = { 0.2989, 0.5870, 0.1140 };
double[] rgb = { color.getR(), color.getG(), color.getB() };
// Determine luminance:
double y = 0.0;
for (int i = 0; i < 3; ++i) {
y += weights[i] * rgb[i];
}
// Interpolate between (R, G, B) and (Y, Y, Y):
for (int i = 0; i < 3; ++i) {
rgb[i] *= percColoured;
rgb[i] += y * percGrayed;
}
return new Color((int)rgb[0], (int)rgb[1], (int)rgb[2]);
}
Color grayedColor = grayed(color, 30); // 30% grayed.
颜色灰显(颜色,int perc){
双PERCGREED=perc/100.0;
双过彩色=1.0-过灰色;
双[]权重={0.2989,0.5870,0.1140};
double[]rgb={color.getR(),color.getG(),color.getB()};
//确定亮度:
双y=0.0;
对于(int i=0;i<3;++i){
y+=权重[i]*rgb[i];
}
//在(R,G,B)和(Y,Y,Y)之间插值:
对于(int i=0;i<3;++i){
rgb[i]*=1;
rgb[i]+=y*灰色;
}
返回新颜色((int)rgb[0],(int)rgb[1],(int)rgb[2]);
}
颜色灰色=灰色(颜色,30);//30%为灰色。
巨大的差异是由于sRGB()中的gamma编码造成的。sRGB是color
构造函数中使用的默认颜色空间。如果改用线性RGB颜色空间设置颜色,则灰色值不会失真:
ColorSpace linearRGB = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
Color grey50 = new Color(linearRGB, new float[]{50f/255,50f/255,50f/255}, 1f);
Color grey100 = new Color(linearRGB, new float[]{100f/255,100f/255,100f/255}, 1f);
Color grey255 = new Color(linearRGB, new float[]{1f,1f,1f}, 1f);
但是,当使用Color.getRGB
和ImageBuffer.setRGB
设置像素时,线性灰度值将转换为sRGB并返回。因此,它们是伽马编码和解码的,根据所选的颜色空间产生舍入误差
通过直接在灰度颜色模型后面设置原始像素数据,可以避免这些错误:
WritableRaster writable = image.getRaster();
writable.setPixel(0,0, new int[]{64});
请注意,您必须对百分比值进行四舍五入,例如,对于25%,您不能存储63.75
。如果需要更高的精度,请使用TYPE\u USHORT\u GRAY
而不是TYPE\u BYTE\u GRAY
+1。这个问题实际上是放在MathOverflow上进行反向函数计算。在问题示例中,我们有R=G=B,因此权重应该是无关的。+1。我想我误解了什么。这应该是公认的答案。但是请注意,您使用整数除法的结果初始化浮点
数组,因此,例如50/255
和100/255
将全部为零!使用50f/255
和100f/255
正确获取float
数字。在某些舍入误差范围内,CS\u LINEAR\u RGB
和CS\u GRAY
方法都给出了正确的结果。是否可以修复舍入误差,使100%的结果为255?
Y = 0.2989*R + 0.5870*G + 0.1140*B
Y = 0.2126*R + 0.7152*G + 0.0722*B
Color grayed(Color color, int perc) {
double percGrayed = perc / 100.0;
double percColored = 1.0 - percGrayed;
double[] weights = { 0.2989, 0.5870, 0.1140 };
double[] rgb = { color.getR(), color.getG(), color.getB() };
// Determine luminance:
double y = 0.0;
for (int i = 0; i < 3; ++i) {
y += weights[i] * rgb[i];
}
// Interpolate between (R, G, B) and (Y, Y, Y):
for (int i = 0; i < 3; ++i) {
rgb[i] *= percColoured;
rgb[i] += y * percGrayed;
}
return new Color((int)rgb[0], (int)rgb[1], (int)rgb[2]);
}
Color grayedColor = grayed(color, 30); // 30% grayed.
ColorSpace linearRGB = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
Color grey50 = new Color(linearRGB, new float[]{50f/255,50f/255,50f/255}, 1f);
Color grey100 = new Color(linearRGB, new float[]{100f/255,100f/255,100f/255}, 1f);
Color grey255 = new Color(linearRGB, new float[]{1f,1f,1f}, 1f);
WritableRaster writable = image.getRaster();
writable.setPixel(0,0, new int[]{64});