Compare Pixel Colour - Java - java

How can I find similar coloured pixels using colour objects? I know to see if two colors are equal you can use:
a.equals(b);
where a and b are colour objects, but what if I want to find similar shades of blue for example?

Comparing colours is not a trivial problem, there are a variety of different metrics. I can't find a library (easily) that does it but if you have a thorough search I'm sure there'll be something out there Have a look at this class. In the meantime do some reading!

When it comes to programmatically tweaking color values you have lots of options. A very easy solution would be to simply offset the channel values of the color object randomly. Think of it as mutating colors - just take the color you'd like to mutate and generate a few more colors from it:
Color mutateColor(int range){
int r = a.getRed() + (int)(Math.random() * 2 * range - range);
r = Math.min(255, Math.max(0, r));
int g = a.getGreen() + (int)(Math.random() * 2 * range - range);
g = Math.min(255, Math.max(0, g));
int b = a.getBlue() + (int)(Math.random() * 2 * range - range);
b = Math.min(255, Math.max(0, b));
return new Color(r, g, b);
}
This is the simplest example, a range is given and each channel is offset by that same range, resulting in something like this:
This was done with a range value of 10. For added control you could add three arguments to the mutateColor function (offsets for each individual channel). You could also take one range but alter it based on the values already in the channel. For instance:
range = 0.25
red = 100
green = 10
blue = 0
redRange = 100 + rand(-25, 25)
greenRange = 10 + rand(-2.5, 2.5);
etc...
That's just one of many other possibilities.
If you were looking to compare two colors with a tolerance, I ported the code from fredley's link and it works nicely for getting the difference between two colors:
double colorDist(Color e1, Color e2){
long rmean = ( (long)e1.getRed() + (long)e2.getRed() ) / 2;
long r = (long)e1.getRed() - (long)e2.getRed();
long g = (long)e1.getGreen() - (long)e2.getGreen();
long b = (long)e1.getBlue() - (long)e2.getBlue();
return Math.sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8));
}

what if I want to find similar shades of blue for example?
I have no idea what a technical definition for this might mean. However you might be able to use HSL Color to create your own definition.
Basically, I guess you could start by making sure the the Hue of each color is +/- a fixed number of degrees. Then you could narrow it down further by checking if the Saturation and/or Luminosity is within your desired range.

Related

Set text to random color & opacity javaFX

I need a JavaFX program to set text to a random color and opacity I'm not sure on how to do it.
Here is a sample of my code:
Text text1 = new Text();
text1.setText("Java");
text1.setFont(Font.font("Times New Roman", FontWeight.BOLD, FontPosture.ITALIC, 22));
text1.setRotate(90);
gridpane.add(text1, 3, 1);
You can use Math.random() to generate a Double in the range [0,1), so you need to do:
text.setOpacity(Math.random());
Color took a little more digging through the docs, but can be accomplished with:
text.setFill(Color.color(Math.random(), Math.random(), Math.random());
setFill comes from Shape, which Text inherits from. setFill takes a Paint, which Color is the simplest implementation of. Color.color(double, double, double) takes the rgb value with doubles in the range [0,1].
Learn how to navigate through the docs and you'll be able to find these sorts of things on your own quickly in the future!
Note: opacity/rgb color all take doubles of the range [0,1] where Math.random() produces in the range [0,1). If you're unfamiliar with this notation, this means Math.random() will NEVER produce 1, only a number smaller than 1 by possible accuracy. This means that you will never have a 100% fully opaque/r/g/b with this method but in reality you probably can't tell the difference, so it's better to use the less complicated method.
Note 2: javafx.scene.paint.Color#color actually provides a four-argument constructor that includes opacity, but I would recommend setting the opacity of the Text node itself as above rather than the opacity of the Paint.
Like this:
Text randomColorText(String txt) {
Text t = new Text(txt);
Random rng = new Random();
int c = rng.nextInt();
int r = c & 255;
int g = (c >>> 8) & 255;
int b = (c >>> 16) & 255;
double op = (c >>> 24) / 255.0;
t.setFill(Color.rgb(r, g, b, op));
// or use only r,g,b above and set opacity of the Text shape: t.setOpacity(op);
return t;
}
Note that the other answer that mentions how Random will never return a double == 1.0, says you won't get the full range of colours is wrong. Color RGB values do not have the same range as double - typically they end up as 8-bit values in the range 0-255 at some point, on some high end applications you might use 16-bits per channel. You will get the full range of colours using the doubles from Random.
You will note that I avoided calling into the random number generator multiple times for what is usually represented by a 32-bit value. (Micro-optimization: Calling nextInt does half the work of nextDouble, and we only need to call it once. I would typically save the instance of Random as a static variable rather than create one every time that method is called. java.util.Random is threeadsafe.)

java: represent range of float values on a color scale

I'm trying to create a function that will translate float values to a color. I created a simple linear scale:
float value;
float maxValue;
float scaleStep = maxValue / 5;
if (value < scaleStep) {
color = blue
}
if (value > scaleStep && value <= scaleStep * 2) {
color = green
}
if (value > scaleStep * 2 && value <= scaleStep * 3) {
color = yellow
}
if (value > scaleStep * 3 && value <= scaleStep * 4) {
color = orange
}
if (value > scaleStep * 4 && value <= scaleStep * 5) {
color = red
}
but since most (but not all) of the values from the sets that I'm trying to represent are in close proximity from one particular value, graphical representation using linear scale isn't very useful (almost everything is translated to one color).
How can I create a non-linear scale so that differences between the values are more visible?
Interpolation is what you want. Interpolation generates samples between known samples in a dataset.
Here, your known samples are your colors; blue, green, yellow, orange, and red. The colors between those known colors are what you're looking for.
Here's a link to a nice visualizer of interpolation functions.
And here's a few interpolation functions for your convenience. Play with them, find the one that works best for you!
public float linearInterpolation(float start, float end, float normalizedValue) {
return start + (end - start) * normalizedValue;
}
public float sinInterpolation(float start, float end, float normalizedValue){
return (start+(end-start)* (1 - Math.cos(normalizedValue * Math.PI)) / 2;
}
//usage
linearInterpolation(red, green, .5f);//halfway between red and green.
//same with other demonstrations.
Edit:
Here, start and end refer to a starting and ending sample. The normalizedValue is some value between [0, 1] inclusive (that means it can equal exactly 0 or 1, or any value in between 0 and 1. That's what the term normalized means typically.)
So, for you, start and end will be two colors, and normalizedValue will represent how near you are to the starting or ending color.
Take linearInterpolation for example.
red = 1;
green = 2;
float midway = 1 + (2 - 1) * .5;
//midway = 1.5, which is halfway between red and green.
float allRed = 1 + (2 - 1) * 0;
//allRed = 1, which is the value of red (or start)
float allGreen = 1 + (2 - 1) * 1;
//allGreen = 2, which is the value of green (or end)
So, for linear interpolation, the closer the normalizedValue is to 1, the nearer the returned value it is to end. The closer normalizedValue is to 0, the closer the returned value is to start.
This isn't necessarily true for other interpolation functions. You can think linear interpolation as a simple line segment connecting values. Want a value halfway between those segments? Use a normalized value of .5, viola!
Other functions might have steeper slopes, or even oscillate between start and end!
Try and stop thinking in terms of color, and start thinking more abstractly. Colors are a certain distance apart. Interpolation helps you define what values lie in the distance between them.
Since the float values are in a set, you know how many there are and can calculate a color interval. You can then iterate over them, assigning colors and incrementing by the color interval.
Edit: The downside of this approach is that the same float value will not map to the same color when the number of values changes.
I suggest a logarithmic scale. If you use base 10 logs, the range will be from -39 to +39.
Depending on your distribution, a double or triple log can be better. I made a very quick test, and for the sample { 1.00, 1.20, 1.10, 1.05, 1.15, 9.70, 1.20, 2.00, 1.01, 1.03, 1.16, 1.02, 9.00, 1.20, 1.10, 1.50, 1.05, 1.15, 2.00, 3.00 }, function
int f(float x) {
return (int)(Math.log(Math.log(x)*100+1)*2.5) ;
}
Produces the following distribution:
f(x) color count
0 blue 4
1 green 4
2 yellow 6
3 orange 3
4 red 3
Not bad for 5 minutes of work. However, if you post a reasonable sample of numbers (say 100), a distribution graph, or, much better, a distribution histogram, we could help you better. The trick is to find the distribution function of the data. From that function it is very easy to come with a second function that makes the distribution uniform ("flat").
A second alternative in your case (which is relatively simple as you want to use just a few colors), is to use scaleSteps of different "width".
if( value < greenMin ) color= blue ;
else if( value < yellowMin ) color= green ;
else if( value < orangeMin ) color= yellow ;
else if( value < redMin ) color = orange ;
else color= red ;
I took the liberty of condensing the code a bit. Let me know if it's not clear. You need to determine the values of greenMin, yellowMin, orangeMin, and redMin, of course. For that, grab a big, representative data sample, sort it, and divide it in 5 groups of equal size. The first value of the second group is greenMin, first value of the third is yellowMin, and so on. You can use an office spreadsheet program to do this, as it's a one-time activity.

YCbCr at LipMap detection

I'm confused with converting the RGB values to YCbCr color scheme. I used this equation:
int R, G, b;
double Y = 0.229 * R + 0.587 * G + 0.144 * B;
double Cb = -0.168 * R - 0.3313 * G + 0.5 * B + 128;
double Cr = 0.5 * R - 0.4187 * G - 0.0813 * B + 128;
The expected output of YCbCr is normalized between 0-255, I'm confused because one of my source says it is normalized within the range of 0-1.
And it is going well, But I am having problem when getting the LipMap to isolate/detect the lips of the face, I implemented this:
double LipMap = Cr*Cr*(Cr*Cr-n*(Cr/Cb))*(Cr*Cr-n*(Cr/Cb));
n returns 0-255, the equation for n is: n=0.95*(summation(Cr*Cr)/summation(Cr/Cb))
but another sources says: n = 0.95*(((1/k)*summation(Cr*Cr))/((1/k)*summation(Cr/Cb)))
where k is equal to the number of pixels in the face image.
It say's from my sources that it will return a result of 0-255, but in my program it always returns large numbers always, not even giving me 0-255.
So can anyone help me implement this and solve my problem?
From the sources you linked in your comments, it looks like either the equations or the descriptions in the first source are wrong:
If you use RGB values in the Range [0,255] and the given conversion (your Cb conversion differs from that btw.) you should get Cr and Cb values in the same range.
Now if you calculate n = 0.95 * (ΣCr2/Σ(Cr/Cb)) you'll notice that the values for Cr2 range from [0,65025] whereas Cr/Cb is in the range [0,255] (assuming Cb=0 is not possible and thus the highest value would be 255/1 = 255).
If you further assume an image with quite high red and low blue components, you'll get way higher values for n than what is stated in that paper:
Constant η fits final value in range 0..255
The second paper states this, which makes much more sense IMHO (although I don't know whether they normalize Cr and Cb to range [0,1] before the calculation or if they normalize the result which might result in a higher difference between Cr2 and Cr/Cb):
Where (Cr) 2,(Cr/Cb) all are normalized to the
range [0 1].
Note that in order to normalize Cr and Cb to range [0,1] you'd either need to divide the result of your equations by 255 or simply use RGB in range [0,1] and add 0.5 instead of 128:
//assumes RGB are in range [0,1]
double Cb = -0.168 * R - 0.3313 * G + 0.5 * B + 0.5;
double Cr = 0.5 * R - 0.4187 * G - 0.0813 * B + 0.5;

How would I change an equation to modulate a number on a line, to one that that modulates on a sine curve?

I'm trying to modulate an alpha value in a Java application I'm building on Android. Right now it goes like this:
if (goingUp) {
newAlpha = oldAlpha + rateOfChange;
if (newAlpha > maxAlpha) {
newAlpha = maxAlpha;
goingUp = false;
}
} else {
newAlpha = oldAlpha - rateOfChange;
if (newAlpha < minAlpha) {
newAlpha = minAlpha;
goingUp = true;
}
}
Where rateOfChange is an arbitrary int that cannot be greater than maxAlpha. The equation is evaluate every tick in a thread and is independent of time.
Is there a way using only the variables given + Math.PI and other Math elements (I'm assuming Math.Sine will be in there) to get newAlpha to be a number on a Sine?
I'm thinking min and max would be the amp of the wave and rateOfChange would be a product of the Sine function, I just can't figure out how it all goes together.
Your equation will look like this:
y is vertical position at time t, A is the amplitude, f is the frequency, and t is the time (or ticks of your Android clock).
Why don't you consider this?
...at the top of your class definition, include:-
import java.lang.*;
...and within your function, after the assignment to newAlpha,
newAlpha = Math.sin(newAlpha%(2*Math.PI));
if you would like newAlpha to be in the range [-1,1] as the sin() function is
OR
...within your function, after the assignment to newAlpha,
newAlpha = Math.asin(newAlpha%3 - 1);
if you would like newAlpha to be in the range [-1,1] as the sin() function is
I'm not sure what datatype your newAlpha is, but I'm going to assume it will not affect the answer for this expression - like newAlpha is of type double.
It is generally the way to get a number within a certain range that you apply modulus to whatever expression you have ie. expr%N results in a number in range [0,N-1].
Hope this helps.
Based on duffymo's general equation I had to go all the way back to my TI-83 days (literally, put the app on my phone). But I was able to put all the pieces together so it ended up looking like this:
newAlpha = (int)((alphaMax - alphaMin) * 0.5 * Math.sin(rateOfChange * ticks + randomPhaseOffset) + (alphaMin + (alphaMax - alphaMin) * 0.5))
FMI: http://en.wikipedia.org/wiki/Sine_wave

Get Bytes from an int to avoid bit shifting fun - Java (Median Filtering)

I'm trying to perform a Median filter on an image in Java but it's terribly slow. Firstly, if any of you know of a standalone implementation I could use it would be fantastic if you could let me know. I'm implementing on Android, trying to replicate a small part of the JAI.
In my method I take each pixel, extract the R,G & B values using
r = pixel >> 16 & 0xFF
Or similar, find the median for the kernel and finish with
pixel = a | r <<16 | g << 8 | b
Is there any way I can grab the bytes from an int in such a way that this would be faster?
Kind regards,
Gavin
EDIT: Full code to help diagnose my low performance upon request
For the actual source file please go here that's where my implementation of medianFilter can be found.
width and height variables are for the size of dest and are available as class member variables. The pixels are linearized into a one dimensional array.
private void medianFilterSquare(int[] source, int[] dest, int rWidth,
int rHeight, int radius) {
// Source has been reflected into a border of size radius
// This makes it radius * 2 pixels wider and taller than the dest
int r,g,b;
int destOffset, rOffset, kOffset;
// The first offset into the source to calculate a median for
// This corresponds to the first pixel in dest
int rFirst = radius + (rWidth*radius);
// We use a square kernel with the radius passed
int neighbours = (radius+radius+1)*(radius+radius+1);
int index;
// Arrays to accumulate the values for median calculation
int[] rs = new int[neighbours];
int[] gs = new int[neighbours];
int[] bs = new int[neighbours];
// Declaring outside the loop helps speed? I'm sure this is done for me
// by the compiler
int pixel;
// Iterate over the destination pixels
for(int x = 0; x < height; x++){
for(int y = 0; y < width; y++){
// Offset into destination
destOffset = x + (y * width);
// Offset into source with border size radius
rOffset = destOffset + rFirst + (y * (radius *2));
index = 0;
// Iterate over kernel
for(int xk = -radius; xk < radius ; xk ++){
for(int yk = -radius; yk < radius ; yk ++){
kOffset = rOffset + (xk + (rWidth*yk));
pixel = source[kOffset];
// Color.red is equivalent to (pixel>>16) & 0xFF
rs[index] = Color.red(pixel);
gs[index] = Color.green(pixel);
bs[index] = Color.blue(pixel);
index++;
}
}
r = medianFilter(rs);
g = medianFilter(gs);
b = medianFilter(bs);
dest[destOffset] = Color.rgb(r, g, b);
}
}
}
As others have said, it's possible that it's the bit in between which is causing the problem. One thing I would say (which may be obvious, but anyway) - don't just profile the application on a desktop VM and assume that the bottleneck will be in the same place. I wouldn't be at all surprised to find entirely different bottlenecks within Dalvik.
Is it possible for you to work with the values still shifted? For instance, if you were to just mask for different colours:
int r = pixel & 0xff0000;
int g = pixel & 0xff00;
int b = pixel & 0xff;
could you tweak your processing algorithm accordingly?
One final thought: I always find the precedence of shift operators confusing. I'd strongly recommend that from a readability point of view, you bracket them:
r = (pixel >> 16) & 0xFF;
pixel = a | (r <<16) | (g << 8) | b;
Irrelevant to performance, but if I were a maintainer I'd certainly appreciate it :)
The fastet way to get your r,g,b values should be
new byte[] {
(byte)(value >>> 24),
(byte)(value >>> 16),
(byte)(value >>> 8),
(byte)value
};
Concentrating on how to do the bit operations is a distraction. It only matters how you're doing these operations because you're needlessly processing the same pixel over and over.
You're calling median filter for every pixel three times, and you're getting multiple pixels around the pixel per pixel. Which means you're doing all that bit work for the same pixel multiple times. You have for loops nested four deep!
If your radius is 5, you're processing 121 pixels. Then you move down by one and process 121 pixels again, and you've already converted all but 11 of them! You do the same thing for every pixel down, then move to the right one pixel. For a radius of five, you're doing two orders of magnitude as many rgb conversions as you have to.
I'd suggest keeping your image in or converting your image to separate red, blue, and green arrays first.
If radius is large, you could keep the red, blue, and green sums as you move along, subtracting out the pixels from the top and adding in the pixels from the bottom as you crawl down the bitmap, but that would make the code a touch more complicated. Whether you add code to optimize further depends on your requirements.
Also, you have a bunch of little things that could be optimized. I'm not sure if the compiler is taking care of them or not. For instance, you could do some strength reduction. You don't need any of the multiplications you have in the lines that calculate neighbors, destOffset, rOffset, or kOffset. Addition is all you need for those if you refactor the code a bit.
You can occasionally get away with doing arithmetic on the red & blue components simultaneously in a single int:
int average(int rgb1, int rgb2)
{
int rb = (((rgb1 & 0xFF00FF) + (rgb2 & 0xFF00FF)) >> 1) & 0xFF00FF;
int g = (((rgb1 & 0xFF00) + (rgb2 & 0xFF00)) >> 1) & 0xFF00;
return (rb | g);
}
because the red and blue components are separated by 8 bits, they don't interfere with each other.
I've never seen a significant (more than 5-10%) speedup from this though.

Categories