I'm trying to write function, which can generate colors between two colors based on a given value. An example would explain it better..
Input ..
X : 1
Y : 0.5
Z : 0
The user gives any set of color:value pairs, then enters a number(say 0.75). I have to then generate color which is a blend of Y and Z in proportion(based on the their values and the input value). I was thinking of the following approach.
Find the colors which surround the value, for 0.75 it will be 0.5 and 1.
Mix those two colors somehow, based on the value and generate new colors.
I'm completely lost, as how to generate colors and are there any libraries for this.
UPDATE:
It is part of a bigger project I'm working on. Lets say we have ..
1 : X
0 : Y
and the user inputs, 0.25
I would like to have something..
(X*0.25 + Y*0.75)
as it's more near to Y, that's why the higher proportion. If the user inputs, 0.5.. the output should be
(X*0.5 + Y*0.5)
and so on. I have no idea how to do this with RGB colors.
P.S: The questions is not specific to language, but I'm doing this in Java.
You have to blend each color channel (red, green and blue) seperately like this:
Color x,y; //set by you
float blending;//set by you
float inverse_blending = 1 - blending;
float red = x.getRed() * blending + y.getRed() * inverse_blending;
float green = x.getGreen() * blending + y.getGreen() * inverse_blending;
float blue = x.getBlue() * blending + y.getBlue() * inverse_blending;
//note that if i pass float values they have to be in the range of 0.0-1.0
//and not in 0-255 like the ones i get returned by the getters.
Color blended = new Color (red / 255, green / 255, blue / 255);
So far for the color example. Generally if you want a linear interpolation between two values you have to do the following:
var firstValue;
var secondValue;
var interpolation;
var interpolated = firstValue * interpolation +
secondValue * (1 - interpolation);
But since you have Color-Objects in your case, you cannot interpolate the whole object in one step, you have to interpolate each relevant value on its own. Eventually you have to interpolate the alpha-channel as well, don´t know that, since you didn´t mention it, but for completeness i include it in this answer.
A color is a point in a three-dimensional space. The exact coordinates used depend on what's called a "color space", of which there are several: RGB, HSV, and so on. So to compute a color in between two given colors, get those two colors in the same color space, and compute a third point between those two along the line in 3d-space between them.
The simplest way to do this would be simply to do a linear interpolation for each of the three values of the colorspace (R, G, and B, for example). But there's a further complication that the coordinate values are often not linear, so you have to linearize them first (for example, TV colors are exponential with a lambda of about 2.2). Depending on your application, incorrectly assuming linearity might work OK anyway, especially if the starting colors are already close.
(As mentioned by luk2302, add a fourth coordinate for alpha if necessary).
You could use Java.awt.color by doing somting like this:
public Color mixColors(Color color1, Color color2, double percent){
double inverse_percent = 1.0 - percent;
int redPart = (int) (color1.getRed()*percent + color2.getRed()*inverse_percent);
int greenPart = (int) (color1.getGreen()*percent + color2.getGreen()*inverse_percent);
int bluePart = (int) (color1.getBlue()*percent + color2.getBlue()*inverse_percent);
return new Color(redPart, greenPart, bluePart);
}
Related
I have to make a project for university. We have to simulate the growing of a disease. The simulation code is already working for me, but we had to insert the population from an image that display areas in the region which are correlated to a color gradient (like red means xy people per sqaure kilometer and yellow means yz people per square kilometer ...). Black pixels, and white pixels mean that there is no population at these area. How can such a function/algorithm look like? For example the image looks like this image, but i have no legend i just have the total population of the area?
What kind of algorithms can i use to solve my problem? I have no ideas how to deal with colors in such a kind of problem i first tried to weigth the red, green and blue part but thats not really working.
If you just want a linear gradient from yellow to red, that's relatively simple.
Pure red in RGB is (255, 0, 0). Makes sense, right? To get red, you max out the red component, and have no green or blue.
Pure yellow is (255, 255, 0) which is a perfectly even mix of red and green with no blue.
So, as you can see, in both cases R=255 and B=0. By altering the amount of green you'll get something in the middle. Your scale therefore has 255 distinct values - it's just a matter of mapping from population density to a value between 0 and 255.
private static final int MAX_RGB_VALUE = 255;
int getGreen(final int maxDensity, final int minDensity, final int density)
{
// we could/should check that minDensity < density < maxDensity else
// we'll get divide by zero errors
final int delta = maxDensity - minDensity;
final float relativeDensity = density / (float) delta;
return Math.round(relativeDensity * MAX_RGB_VALUE);
}
and then your final colour would look something like:
final int green = getGreen(maxDensity, minDensity, density);
Color colour = new Color(MAX_RGB_VALUE, green, MAX_RGB_VALUE);
I want to achieve interpolation between red and blue. something like this
but in a single line.
My java code:
private PixelData InterpolateColour(float totalLength, float curLength){
float startColourV[] = new float[3];
Color.RGBtoHSB(m_start.getColour().getR() & 0xFF, m_start.getColour().getG() & 0xFF, m_start.getColour().getB() & 0xFF, startColourV);
float endColourV[] = new float[3];
Color.RGBtoHSB(m_end.getColour().getR() & 0xFF, m_end.getColour().getG() & 0xFF, m_end.getColour().getB() & 0xFF, endColourV);
float endPercent = curLength / totalLength;
float startPercent = 1 - curLength / totalLength;
float h = endColourV[0] * endPercent + startColourV[0] * startPercent;
float s = endColourV[1] * endPercent + startColourV[1] * startPercent;
float b = endColourV[2] * endPercent + startColourV[2] * startPercent;
int colourRGB = Color.HSBtoRGB(h, s, b);
byte[] ByteArray = ByteBuffer.allocate(4).putInt(colourRGB).array();
return new PixelData(ByteArray[0], ByteArray[3], ByteArray[2], ByteArray[1]);
}
and the result i am getting is this
.
I don't understand, from where all that green is coming from. Can somebody please help me ?
why not use just RGB with simple linear interpolation for this:
color(t)=(color0*t)+(color1*(1.0-t))
where t=<0.0,1.0> is the parameter. So just loop it in the full range with as many steps as you need.
Integer C++/VCL example (sorry not a JAVA coder):
// borland GDI clear screen
Canvas->Brush->Color=clBlack;
Canvas->FillRect(ClientRect);
// easy access to RGB channels
union _color
{
DWORD dd;
BYTE db[4];
} c0,c1,c;
// 0x00BBGGRR
c0.dd=0x000000FF; // Red
c1.dd=0x00FF0000; // Blue
int x,y,t0,t1;
for (x=0,y=ClientHeight/2;x<ClientWidth;x++)
{
t0=x;
t1=ClientWidth-1-x;
c.db[0]=((DWORD(c0.db[0])*t0)+(DWORD(c1.db[0])*t1))/(ClientWidth-1);
c.db[1]=((DWORD(c0.db[1])*t0)+(DWORD(c1.db[1])*t1))/(ClientWidth-1);
c.db[2]=((DWORD(c0.db[2])*t0)+(DWORD(c1.db[2])*t1))/(ClientWidth-1);
c.db[3]=((DWORD(c0.db[3])*t0)+(DWORD(c1.db[3])*t1))/(ClientWidth-1);
Canvas->Pixels[x][y]=c.dd;
}
where ClientWidth,ClientHeight is my app form resolution, Canvas is access to the GDI interface of the form and Canvas->Pixels[x][y] is single pixel access (slow but for this example it is enough). The only important stuff is the for loop. Here resulting image:
Color interpolation is actually a fairly complex topic due to the way human vision works.
Physical intensity and wavelengths don't map directly to perceived luminance and hues. After all human eyes are not photon spectrometers, they just measure the intensity + three primaries, each with different sensitivity.
To have a metric, linear space that represents human color perception instead of physical attributes we have the CIELab. Since it's a linear metric doing interpolation between points should generally give you a linear transition between hues and also luminance.
But CIELab may not be sufficient since it only models perceptual sensitivity. If you need to match real lighting you also have to take into account that natural light sources do not illuminate all colors evenly.
If you need to match photorealistic material then additionally correcting for the intensity spectrum of natural light may also be necessary. I.e. something illuminated by a candle will not have intense blue components simply because the candle emits very little blue light that could be reflected.
Let's say I have two colors in Lab color space-
Color 1: L=81, a=-8, b=74
Color 2: L=64, a=-14, b=3
I want generate n colors between them. For say n=100 or as many colors possible between them.
I know the RGB and HSV color gradient algorithm. But I want to generate gradient in LAB color spaces. I don't want to convert the colors to HSV or RGB as different color models generate different gradients.
This is a link i found which generates gradient in Lab and other color models: http://davidjohnstone.net/pages/lch-lab-colour-gradient-picker
I'm aiming to do something similar in Java but language doesn't matter, I just need to understand the logic and algorithm behind it.
I'm doing this basically for matching a scanned color value with a chart of 5 colors I have. So, I have to first generate all colors in between those 5 colors (using gradient) and compare the other color to find the one which is closest to it. (For comparing I'm using CIEDE2000 Delta-e method). But that's secondary i guess.
Adding further to the last part of my question,
I suppose I'll have to generate a gradient because I want to find the exact location of the color from my sample in the sequence of chart I have.
For eg- I have 6 colors of green shades (light to dark) in my chart each corresponding to particular numeric data between 0 to 450 mg like below (with their LAB values)
Color 1: 78, -10, -71 [0 mg]
Color 2: 73,-14,44 [30 mg]
Color 3: 71, -19, 53 [80 mg]
Color 4: 67, -18, 31 [160 mg]
Color 5: 69, -2, 29 [300 mg]
Color 6: 61, -14, 3 [450 mg]
Now I want to generate all colors between them and find the location of my scanned color and return the mg value. Say my color is exactly between Color 1 and Color 2, then it'll return 15 mg, otherwise if it is closer to Color 2 it'll return 28.5 mg and so on.
Hope this clears up what I'm trying to achieve.
To generate a gradient between two color values, you can just linearly interpolate between them. This will basically work in any color space, although if the transformation between two color spaces is not linear, the gradient obtained by linearly interpolating in one space will not be linear in the other. Thus, generating gradients in Lab color space is exactly like generating them in, say, RGB space.
(For color spaces like HSV or HSL which have a hue coordinate that "loops around", some extra care may be needed to choose the right direction to interpolate in; fortunately, you're not asking about those color spaces here, so I don't need to go into such details.)
Just as a demonstration, here's how you'd generate an n-sample gradient between the colors c1 and c2 (each given as a LabColor object with properties L, a and b):
public static LabColor[] makeGradient(LabColor c1, LabColor c2, int n) {
LabColor gradient = new LabColor[n];
for (int i = 0; i < n; i++) {
float alpha = (float)i / (n-1); // 0.0 <= alpha <= 1.0
float L = (1-alpha) * c1.L + alpha * c2.L;
float a = (1-alpha) * c1.a + alpha * c2.a;
float b = (1-alpha) * c1.b + alpha * c2.b;
gradient[i] = new LabColor(L, a, b);
}
return gradient;
}
This will return a gradient with n color samples, where the first color is equal to c1, the last color is equal to c2, and the rest are interpolated between them.
However, based on the remark at the end of your question, I suspect you don't actually need to generate any gradients. Rather, to find the color in your chart that most closely matches the one in your sample, you just need to calculate the perceptual distance of each chart color from the sample (which, in the Lab color space, can be well approximated simply by their Euclidian distance) and select the closest one:
public static LabColor findNearest(LabColor sample, LabColor[] chart) {
LabColor nearest = null;
float minDistanceSquared = Float.POSITIVE_INFINITY;
for (int i = 0; i < chart.length; i++) {
float dL = sample.L - chart[i].L;
float da = sample.a - chart[i].a;
float db = sample.b - chart[i].b;
float distanceSquared = dL*dL + da*da + db*db;
if (distanceSquared < minDistanceSquared) {
nearest = chart[i];
minDistanceSquared = distanceSquared;
}
}
return nearest;
}
I have list of colors in HEX format (for example #000000) and I would like to detect color type (blue, red, green etc.) and then change color type to another color type. Is this possible and are there any frameworks/libraries for this task?
Example:
I have color #EB1369 (red) then I convert it to blue and it becomes for example #1313EB (blue).
Here's a function that will let you shift colors around the hue circle. You should read the wikipedia page on the HSB (or HSV) color system to really understand what is going on: http://en.wikipedia.org/wiki/HSV_color_space
/** Converts an input color given as a String such as "ab451e" to
* the HSB color space. Shifts its hue from the given angle in degrees.
* Then returns the new color in the same format it was given.
*
* For example shift("ff0000", 180); returns "80ff00" (green is the opposite of red).*/
public static String shift(String rgbS, int angle) {
// Convert String to integer value
int value = Integer.parseInt(rgbS, 16);
// Separate red green and blue
int r = value >> 16;
int g = (value >> 8) & 0xff;
int b = value & 0xff;
// Convert to hsb
float[] hsb = Color.RGBtoHSB(r, g, b, null);
// Convert angle to floating point between 0 and 1.0
float angleF = (float)(angle/360.0);
// Shift the hue using the angle.
float newAngle = hsb[0] + angleF;
if(newAngle > 1.0)
newAngle = newAngle - 1.0f;
hsb[0] = newAngle;
// Convert back to RGB, removing the alpha component
int rgb = Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]);
rgb = rgb & 0xffffff;
// Build a new String
return Integer.toHexString(rgb);
}
Detecting colors can be complex, it depends on the result you really expect.
If what you want is simply an approximation (red, green, blue, yellow, etc.) then you can look at the hue circle of the HSB color-space, choose a hue value for each color you want to define, and then map the color you get in input to the closest one you chose.
You can also rely on things like named HTML colors: http://www.w3schools.com/html/html_colornames.asp . Take this list, create a mapping in your program, then all you have to do is map the color you get to the closest one in your map, and return its name. Be wary though: computing the distance between two colors can be tricky (especially in RGB) and naive approaches (such as channel-by-channel difference) can give surprisingly bad results. Colorimetry is a complex topic, and you will find good methods on this page: http://en.wikipedia.org/wiki/Color_difference
Try convert RGB values to HSV (HSB exactly) - it is format for colors which is more comfortable for human. After conversion, all u need to do is change H V (probably) and convert it back to RGB.
I guess that you like to convert RGB color to HSB. YOu can do this wuth:
java.awt.Color.RGBtoHSB(...)
then you can easily determine whetther H value fits in your definition of blue, and modify it to whatever you like. After this, you can easily convert it back to RGB via:
java.awt.Color.getHSBColor(...)
And ifg you do not like jawa.awt.color just multiply color vector by transofrmation matrix.
Each HEX Color has three parts in it, red, green and blue the # identifies a HEX color, the following two letters are the amount of red; the next two are green and the next two are blue. i.e: RGB
The two letters can have a maximum hexidecimal value of FF which is 255, and a minimum of 00 which is zero.
So you can argue like this, I want a color with 2 red parts, 7 green parts, and zero blue parts, which will give you #020700
That is why #FFFFFF is white (all the colors together) and #000000 is black (no colors at all)
With this logic you can modify the color in any way you want; The Color class can also help a lot.
I am trying to make a color change on a canvas in java to respresent how strong or weak the values respresented by color are relative to each other.
The rgb colors have to be the same color just different shades, like white to grey to black and every shade of grey in between. How can I change the rgb values considering that the values I am representing vary a lot, from -9999999 to positive 9999999.
I think you should take a look at HSL/HSV instead of RGB.
While RGB is elementary in nature in that it expresses colors in terms of the primaries, it does not allow you to make "understandable" changes to the R, G or B values to arrive at "similar" colors. With a HSL/HSV model, you will be able to make changes to Brightness/Lightness/Value (L/V) to arrive at colors with varying amounts of gray, or make changes to Hue (H) to obtain similar colors across the spectrum. You can start at full brightness (White) and create darker tones of gray by decreasing the value of L/V and eventually reach the color of no brightness (Black).
A very mild introduction to color theory, for developers is available here.
As to your question, you should express your colors in terms of HSL, with increasing values of Saturation to have a range of colors starting from white to black. Of course, if you want gray tones in between white and black without any other color, you should keep the hue to a minimum.
A short example on how to get a range of colors follows. For brevity, I've populated the colors into an array, but that is not required since you might want to use the color rightaway (besides considering memory requirements).
private Color[] produceColorRange(int steps)
{
float value = 1.0f; //Starting with full brightness
Color[] colors = new Color[steps];
for(int ctr = 0; ctr < steps; ctr++)
{
value = value - (1.0f/steps); //tend to darkness
int rgb = Color.HSBtoRGB(0.7f, 0.0f, value); //create a darker color
//Hue is Blue, not noticeable
//because Saturation is 0
Color color = new Color(rgb);
colors[ctr] = color;
}
return colors;
}
If you use the above method and paint a JFrame, you will be able to get a result similar to the one below (except that I've modified the hue and saturation to get my color range).
Note that if you want a simpler way of getting a color range, initialize a Color object with Color.WHITE and invoke color.darker(). Of course, you will not be able to control the increment.
Yes, scale your values to fit your domain. That depends on how your RGB values are stored. Usually, 8 bits are used for each. Since grey has R = G = B, you want to scale values in range (-9999999,9999999) to (0, 255).
Consider x in the first interval. Since the first range covers also negative numbers, first, do a shift.
x = x + 9999999
Now x is in the interval (0, 19999998). And the next step is to scale it down to (0, 255). Since the colour values grow linearly in that interval, all you have to do is this:
x = x * 255 / 19999998
Now x is in the interval (0, 255) just like you want.
Generally, if your inital values are in an interval (a, b) and want to transform it into (0, c), apply this formula: (Note that a can be negative)
x = (x - a) * c / (b - a)
So if you R, G, B values are 16 bits long, c will be 2**16 = 65536 and the formula:
x = (x + 9999999) * 65536 / 19999998
Hope that helps.
I'm not completely sure I understand your question, but, if I do:
Why not just scale the RGB values to the values in your range (from -9999999 to positive 9999999)? Moreover, set R, G, and B all to the same value so that you're using shades of gray to represent the value.
Like this:
private final int MIN = -9999999;
private final int MAX = 9999999;
public Color getScaledColor(int val) {
int gray = (int) Math.round((double) (val - MIN) / (double) (MAX - MIN)
* 255.0);
Color color = new Color(gray, gray, gray);
return color;
}
Note that this solution will not give unique colors for all the values in the range you specified. But also keep in mind that the human eye can only distinguish between so many shades (and 2 * 9999999 + 1 is probably more than the number of shades than it can distinguish between).
The HSL Color class implements the formulas provided in the Wikipedia link on HSL/HSV provide above.