In my Java program I need to analyze a color of a pixel in given coordinates. Because of the fact that I need to do it often, first I capture a part of the screen, and then get a pixel color. I am doing this with:
BufferedImage bi = robot.createScreenCapture(new Rectangle(0,0,100,100));
...
pxcolor = GetPixelColor(bi,x,y);
...
ImageIO.write(bi, "bmp", new File("myScreenShot.bmp"));
The GetPixelColor function is quite obvious:
public Color GetPixelColor(BufferedImage b, int x, int y)
{
int pc = b.getRGB(x, y);
Color ccc = new Color(pc,true);
int red = (pc & 0x00ff0000) >> 16; // for testing
int green = (pc & 0x0000ff00) >> 8; // for testing
int blue = pc & 0x000000ff; // for testing
return ccc;
}
For testing purposes I have created a pure pink picture (RGB(255,0,255)). The problem is that
even if the pixel is pure pink, the function returns something like RGB(250,61,223) as well as testing variables red, green and blue there. Also, the saved file (the myScreenShot.bmp) looks quite different.
What am I doing wrong. Could it be somehow ColorModel related?
UPD: getting the DataBuffer from bi doesn't seems to produce right results - the
first element of produced DataBuffer is equal to "-2105371". I don't know from where came minus sign, but if I transform it to HEX I will get something like "FFFFFFFFFFDFDFE5". The real pixel RGB is (E5,E5,EB), and the buffer is already corrupted, having instead RGB(DF,DF,E5). This drives me nuts already.
It is most likely due to the color model.
According to this code it uses a DirectColorModel (see below) regardless of your color depth of your screen.
/*
* Fix for 4285201
* Create a DirectColorModel equivalent to the default RGB ColorModel,
* except with no Alpha component.
*/
screenCapCM = new DirectColorModel(24,
/* red mask */ 0x00FF0000,
/* green mask */ 0x0000FF00,
/* blue mask */ 0x000000FF);
Related
I'm working on an Android app and would like to locate the edges of an image taken by the phone camera. I have figured that my best bet at locating these edges is by looking for the pixel in between two pixels that are two significant colors. For instance a shade of green and shade of black.
How would I come across this pixel? Is there a range of numbers that correlate with the various colors and there shades? I.e. 100-200 is red, 200-300 is blue, etc.?
First, you can use android.graphics.Bitmap.
If your image is from the device camera or device media, you can do this:
Bitmap bitmap = MediaStore.Images.Media.getBitmap(activity.getContentResolver(), uri);
You may also want a scaled down version of the image so there are less total pixels in the Bitmap; this will help a lot with performance:
bitmap = createScaledBitmap(bitmap, bitmap.getWidth()/scaleFactor, bitmap.getHeight()/scaleFactor, false);
Second, to get the pixels you can do this:
int [] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
Third, to evaluate red, blue, green, black, white, etc, use the following convention:
black = 0x000000
red = 0xff0000
green = 0x00ff00
blue = 0x0000ff
white = 0xffffff
It is a matter of your exact needs what range you want to use qualify a particular color. I would suggest the a significant change would be calculated by the following:
final static int SIGNIFICANT_CHANGE_AMOUNT = 0xff;
int pixelA = pixels[i];
int pixelB = pixels[i+1];
boolean sigChange = false;
int changeInRed = Math.abs(((pixelA & 0xff0000) >> 16) - ((pixelB & 0xff0000) >> 16));
int changeInGreen = Math.abs(((pixelA & 0x00ff00) >> 8) - ((pixelB & 0x00ff00) >> 8));
int changeInBlue = Math.abs(((pixelA & 0x0000ff) >> 0) - ((pixelB & 0x0000ff) >> 0));
int overallChange = changeInRed + changeInGreen + changeInBlue;
if (overallChange > SIGNIFCANT_CHANGE_AMOUNT) {
sigChange = true;
}
You'll still have to write an algorithm to detect an area but I think if you follow the Flood Fill Algorithm wiki it will help a lot. I have used the queue-based implementation since the recursive one is not really feasible.
Also note that when you getPixels from a bitmap that has been used as an android view you will want to mask out the transparency byte... you can see this post
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.
I cannot find a conversion routine for converting 8-bit indexed color to RGB. For some background details, I am using POI to read an xlsx file and one of the cells has a background color indexed as value 64. When I attempt to create a PdfPCell in iText with this value for the background BaseColor, I get a Navy Blue and the correct color should be Black. So I need a routine that will convert the value of 64 to rgb(0, 0, 0).
This is the code that sets the background to Navy Blue
short c = ((XSSFColor) color).getIndexed();
BaseColor base = new BaseColor(c);
I found a similar question here on SO but the "packed" routine failed with "Color value outside range 0-255".
short packed = ((XSSFColor) color).getIndexed();
log.debug("Indexed {}", packed);
int r = (packed >> 5) * 32;
int g = ((packed >> 2) << 3) * 32;
int b = (packed << 6) * 64;
BaseColor base = new BaseColor(r, g, b);
Update 1: It seems that there is a Palette somewhere in the document, in my case an XSSFPalette. Once I find the answer I'll update it here.
Update 2: XSSFWorkbook doesn't provide access to the palette, hence my follow question: Access to the color palette in an XSSFWorkbook
There isn't a mathematical relationship between color index and RGB values. It's a lookup.
Eight-bit indexed color means that each pixel's color is represented by the number 0-255. What those colors actually are depends on your pallette (just like a painter would use!) The eight bits, therefore, allow you to have 256 separate colors in your picture.
If your image displays in color, then you have a pallette somewhere that will tell you what index corresponds to what RGB triplet.
http://en.wikipedia.org/wiki/Indexed_color
I have found this code on JavaDoc, but I can't seem to understand it.
output.setRGB(x, y, (image.getRGB(x, y) & 0xff00ff00)
| ((image.getRGB(x, y) & 0xff0000) >> 16)
| ((image.getRGB(x, y) & 0xff) << 16));
All I know that this code turns blue color to red in a BufferedImage.
but what if I want to replace blue with white or some other color and vice-versa?
I would appreciate any help.
Colors are stored like this, in hexadecimal:
RRGGBBAA
Red, green, blue, alpha. Now let's take a look at one of the lines:
(image.getRGB(x, y) & 0xff0000) >> 16
image.getRGB(x, y) would return an RRGGBBAA value, and this line is bitmasking it with 0xff0000. Here is a visual:
RRGGBBAA
&
00FF0000
=
00GG0000
Therefore, it transforms the RRGGBBAA value into GG0000.
Then, there is a bitshift 16 binary bits to the right. Java can't shift bits in hexadecimal, but we are visualizing the colors in hexadecimal right now. Therefore, we must convert the 16 binary shifts into 4 hex shifts, because hexadecimal is base-16. Binary is base-2, and 2^4 is 16, the base of hexadecimal.
Therefore, you must shift right 4 bits. This would turn GG0000 into GG, since the bits are being shifted 4 places to the right.
Therefore, we now have the value for the amount of green in our color.
You can apply similar logic to the other lines to see how they work.
When I work with color I use different idea:
BufferedImage image = //create Buffered image
int rgb = image.getRGB(x,y); //get Rgb color value
Color color = new Color(rgb); // create color with this value
Color resultColor = new Color(color.getRed(), color.getBlue(), color.getGreen()); //create new color change blue and green colors values
image.setRGB(x,y,resultColor.getRGB()); //set color
I think this idea is easier to understand.
if you want to get white color use this :
BufferedImage image = new BufferedImage();
Color color = new Color(255,255,255);
image.setRGB(x,y,color.getRGB());
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.