Programmatically find complement of colors? - java

Is there a way to find the complement of a color given its RGB values? Or can it only be found for certain colors? How would someone go about doing this in Java?

This is what I come up: (Conjecture)
Let r, g, and b be RGB components of the original color.
Let r', g', and b' be RGB components of the complementary color.
Then:
r' = max(r,b,g) + min(r,b,g) - r
b' = max(r,b,g) + min(r,b,g) - b
g' = max(r,b,g) + min(r,b,g) - g
I see that this gives the same answer to what the websites (i.e. www.color-hex.com) give, but I will still prove it. :)

For a rough approximation, you can do this by converting RGB to HSL (Hue, Saturation, Lightness).
With the HSL value, shift the hue 180 degrees to get a color on the opposite of the color wheel from the original value.
Finally, convert back to RGB.
I've written a JavaScript implementation using a hex value here - https://stackoverflow.com/a/37657940/4939630
For RGB, simply remove the hex to RGB and RGB to hex conversions.

A more general and simple solution for finding the opposite color is:
private int getComplementaryColor( int color) {
int R = color & 255;
int G = (color >> 8) & 255;
int B = (color >> 16) & 255;
int A = (color >> 24) & 255;
R = 255 - R;
G = 255 - G;
B = 255 - B;
return R + (G << 8) + ( B << 16) + ( A << 24);
}

None of the answers above really give a way to find the complimentary color, so here's my version written in Processing:
import java.awt.Color;
color initial = color(100, 0, 100);
void setup() {
size(400, 400);
fill(initial);
rect(0, 0, width/2, height);
color opposite = getOppositeColor(initial);
fill(opposite);
rect(width/2, 0, width/2, height);
}
color getOppositeColor(color c) {
float[] hsv = new float[3];
Color.RGBtoHSB(c>>16&0xFF, c>>8&0xFF, c&0xFF, hsv);
hsv[0] = (hsv[0] + 0.5) % 1.0;
// black or white? return opposite
if (hsv[2] == 0) return color(255);
else if (hsv[2] == 1.0) return color(0);
// low value? otherwise, adjust that too
if (hsv[2] < 0.5) {
hsv[2] = (hsv[2] + 0.5) % 1.0;
}
return Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]);
}

Just focusing on the 3D RGB cube. Here's a function that does a binary search in that cube to avoid having to figure out how to cast vectors in a direction and find the closest point on the cube boundary that intersects it.
def farthestColorInRGBcubeFrom(color):
def pathsAt(r, g, b):
paths = [(r, g, b)]
if r < 255:
paths.append((int((r + 255)/2), g, b))
if r > 0:
paths.append((int(r/2), g, b))
if g < 255:
paths.append((r, int((g + 255)/2), b))
if g > 0:
paths.append((r, int(g/2), b))
if b < 255:
paths.append((r, g, int((b + 255)/2)))
if b > 0:
paths.append((r, g, int(b/2)))
return paths
r = color.red(); g = color.green(); b = color.blue();
#naive guess:
r0 = 255 - r; g0 = 255 - g; b0 = 255 - b;
paths = pathsAt(r0, g0, b0)
maxPath = None
while paths != []:
for p in paths:
d = (r - p[0])**2 + (g - p[1])**2 + (b - p[0])**2
if maxPath != None:
if d > maxPath[0]:
maxPath = (d, p)
else:
maxPath = (d, p)
p = maxPath[1]
paths = pathsAt(p[0], p[1], p[2])
c = maxPath[1]
return QColor(c[0], c[1], c[2], color.alpha())

You might find more information at:
Programmatically choose high-contrast colors
public static int complimentaryColour(int colour) {
return ~colour;
}

it should just be math
the total of the r values of the 2 colors should be 255
the total of the g values of the 2 colors should be 255
the total of the b values of the 2 colors should be 255

Related

Color code to int (NumberFormatExeption) [duplicate]

So in a BufferedImage, you receive a single integer that has the RGB values represented in it. So far I use the following to get the RGB values from it:
// rgbs is an array of integers, every single integer represents the
// RGB values combined in some way
int r = (int) ((Math.pow(256,3) + rgbs[k]) / 65536);
int g = (int) (((Math.pow(256,3) + rgbs[k]) / 256 ) % 256 );
int b = (int) ((Math.pow(256,3) + rgbs[k]) % 256);
And so far, it works.
What I need to do is figure out how to get an integer so I can use BufferedImage.setRGB(), because that takes the same type of data it gave me.
I think the code is something like:
int rgb = red;
rgb = (rgb << 8) + green;
rgb = (rgb << 8) + blue;
Also, I believe you can get the individual values using:
int red = (rgb >> 16) & 0xFF;
int green = (rgb >> 8) & 0xFF;
int blue = rgb & 0xFF;
int rgb = ((r&0x0ff)<<16)|((g&0x0ff)<<8)|(b&0x0ff);
If you know that your r, g, and b values are never > 255 or < 0 you don't need the &0x0ff
Additionaly
int red = (rgb>>16)&0x0ff;
int green=(rgb>>8) &0x0ff;
int blue= (rgb) &0x0ff;
No need for multipling.
if r, g, b = 3 integer values from 0 to 255 for each color
then
rgb = 65536 * r + 256 * g + b;
the single rgb value is the composite value of r,g,b combined for a total of 16777216 possible shades.
int rgb = new Color(r, g, b).getRGB();
To get individual colour values you can use Color like following for pixel(x,y).
import java.awt.Color;
import java.awt.image.BufferedImage;
Color c = new Color(buffOriginalImage.getRGB(x,y));
int red = c.getRed();
int green = c.getGreen();
int blue = c.getBlue();
The above will give you the integer values of Red, Green and Blue in range of 0 to 255.
To set the values from RGB you can do so by:
Color myColour = new Color(red, green, blue);
int rgb = myColour.getRGB();
//Change the pixel at (x,y) ti rgb value
image.setRGB(x, y, rgb);
Please be advised that the above changes the value of a single pixel. So if you need to change the value entire image you may need to iterate over the image using two for loops.

java.lang.IllegalArgumentException: Width (-2147483647) and height (-2147483647) cannot be <= 0

This question is linked to the following method for auto-cropping : https://stackoverflow.com/a/12645803/7623700
I get the following exception when attempting to run the following Java code in Eclipse:
Exception in thread "main" java.lang.IllegalArgumentException: Width (-2147483647) and height (-2147483647) cannot be <= 0
at java.awt.image.DirectColorModel.createCompatibleWritableRaster(Unknown Source)
at java.awt.image.BufferedImage.<init>(Unknown Source)
at getCroppedImage.getCropImage(getCroppedImage.java:44)
at getCroppedImage.<init>(getCroppedImage.java:16)
at getCroppedImage.main(getCroppedImage.java:79)
The code has worked for others, so i suppose the problem may lie with the way I'm attempting to use it, here is my code:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class getCroppedImage {
getCroppedImage() throws IOException{
String imagePath = "C:\\Users\\lah\\workspace\\Testing\\images";
BufferedImage image = ImageIO.read(new File("C:\\Users\\lah\\workspace\\Testing\\images\\image1.jpg"));
BufferedImage resultImage1 = getCropImage(image,2.0);
File outFile1 = new File(imagePath, "croppedimage.png");
ImageIO.write(resultImage1, "PNG", outFile1);
}
//start of original method in the above link
public BufferedImage getCropImage(BufferedImage source, double tolerance) {
// Get our top-left pixel color as our "baseline" for cropping
int baseColor = source.getRGB(0, 0);
int width = source.getWidth();
int height = source.getHeight();
//System.out.println(width+" "+height);
int topY = Integer.MAX_VALUE, topX = Integer.MAX_VALUE;
int bottomY = -1, bottomX = -1;
for(int y=0; y<height; y++) {
for(int x=0; x<width; x++) {
if (colorWithinTolerance(baseColor, source.getRGB(x, y), tolerance)) {
if (x < topX) topX = x;
if (y < topY) topY = y;
if (x > bottomX) bottomX = x;
if (y > bottomY) bottomY = y;
}
}
}
BufferedImage destination = new BufferedImage( (bottomX-topX+1),
(bottomY-topY+1), BufferedImage.TYPE_INT_ARGB);
destination.getGraphics().drawImage(source, 0, 0,
destination.getWidth(), destination.getHeight(),
topX, topY, bottomX, bottomY, null);
return destination;
}
private boolean colorWithinTolerance(int a, int b, double tolerance) {
int aAlpha = (int)((a & 0xFF000000) >>> 24); // Alpha level
int aRed = (int)((a & 0x00FF0000) >>> 16); // Red level
int aGreen = (int)((a & 0x0000FF00) >>> 8); // Green level
int aBlue = (int)(a & 0x000000FF); // Blue level
int bAlpha = (int)((b & 0xFF000000) >>> 24); // Alpha level
int bRed = (int)((b & 0x00FF0000) >>> 16); // Red level
int bGreen = (int)((b & 0x0000FF00) >>> 8); // Green level
int bBlue = (int)(b & 0x000000FF); // Blue level
double distance = Math.sqrt((aAlpha-bAlpha)*(aAlpha-bAlpha) +
(aRed-bRed)*(aRed-bRed) +
(aGreen-bGreen)*(aGreen-bGreen) +
(aBlue-bBlue)*(aBlue-bBlue));
// 510.0 is the maximum distance between two colors
// (0,0,0,0 -> 255,255,255,255)
double percentAway = distance / 510.0d;
return (percentAway > tolerance);
}
// end of original method in the above link
public static void main(String[] args) throws IOException {
getCroppedImage ci = new getCroppedImage();
}
}
What could be causing the error? Any help is much appreciated.
I have ran your code and getting the error in the following line.
BufferedImage destination = new BufferedImage((bottomX - topX + 1),
(bottomY - topY + 1), BufferedImage.TYPE_INT_ARGB);
When I add a print statement before this line as follows.
System.out.println((bottomX - topX + 1) + " " + (bottomY - topY + 1));
It printed - -2147483647 -2147483647. Then I saw, you have initialized topY and topX as follows.
int topY = Integer.MAX_VALUE, topX = Integer.MAX_VALUE;
Actually none of the variables - topX, topY, bottomX and bottomY are not getting updated because the following if condition never evaluates to True.
if (colorWithinTolerance(baseColor, source.getRGB(x, y), tolerance)) {
// your code goes here
}
When I checked your colorWithinTolerance() function, I found the following problem.
private boolean colorWithinTolerance(int a, int b, double tolerance) {
// your code
return (percentAway > tolerance);
}
This condition should be - percentAway < tolerance. Because if percentAway is greater than tolerance, then your function should return False as you are checking if the distance is in range of the given tolerance limit.
I wrote my debugging process so that you can debug your code of your own from next time.
An explanation of the algorithm:
the algorithm takes the top-left point as being white and finds all pixels that are at least tolerance% different (away >) from it: this will result in the area that is different from white and since you want to crop out the white that should be your valid area.
The only problem is that tolerance should be a percent since
percentAway = distance / 510.0d
cannot be more than one (it's a normalized quantity of the distance of colors).
Therefore if you define tolerance as a decimal
for example
BufferedImage resultImage1 = getCropImage(image, 0.2);
should work.
(with the condition kept at percentAway > tolerance )

RGB to Philips Hue (HSB)

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
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)
When setting these calculated values in the Philips Hue no matter what song is playing the color is always redish or white.
Did something go wrong with the conversion between RGB to 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
{
"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"
}
}
A special thanks to StackOverflow user, Gee858eeG, to notice my typo and Erickson for the great tips and links.
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
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.
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;
}
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..
I would suggest setting the saturation to the maximum, 255, and vary only the hue.
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.
This post was about the only good hit I got when googling this 8 years later. Here's the Python version of Philips' Meethue Developer doc conversion routine (gamma settings are a bit different I think):
def rgb2xyb(r,g,b):
r = ((r+0.055)/1.055)**2.4 if r > 0.04045 else r/12.92
g = ((g+0.055)/1.055)**2.4 if g > 0.04045 else g/12.92
b = ((b+0.055)/1.055)**2.4 if b > 0.04045 else b/12.92
X = r * 0.4124 + g * 0.3576 + b * 0.1805
Y = r * 0.2126 + g * 0.7152 + b * 0.0722
Z = r * 0.0193 + g * 0.1192 + b * 0.9505
return X / (X + Y + Z), Y / (X + Y + Z), int(Y*254)
It also returns brightness information in the range 0..254 as used by
Hue Bridge API.
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.
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):
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.
For those struggling with this issue and looking for a solution in Javascript, I convereted the top answer's response w/reference to #error454's python adaptation and confirmed it works with HUE bulbs and the API :D
function EnhanceColor(normalized) {
if (normalized > 0.04045) {
return Math.pow( (normalized + 0.055) / (1.0 + 0.055), 2.4);
}
else { return normalized / 12.92; }
}
function RGBtoXY(r, g, b) {
let rNorm = r / 255.0;
let gNorm = g / 255.0;
let bNorm = b / 255.0;
let rFinal = EnhanceColor(rNorm);
let gFinal = EnhanceColor(gNorm);
let bFinal = EnhanceColor(bNorm);
let X = rFinal * 0.649926 + gFinal * 0.103455 + bFinal * 0.197109;
let Y = rFinal * 0.234327 + gFinal * 0.743075 + bFinal * 0.022598;
let Z = rFinal * 0.000000 + gFinal * 0.053077 + bFinal * 1.035763;
if ( X + Y + Z === 0) {
return [0,0];
} else {
let xFinal = X / (X + Y + Z);
let yFinal = Y / (X + Y + Z);
return [xFinal, yFinal];
}
};
https://gist.github.com/NinjaBunny9000/fa81c231a9c205b5193bb76c95aeb75f

Incorrect result of image subtraction

I wanted to subtract two images pixel by pixel to check how much they are similar. Images have the same size one is little darker and beside brightness they don't differ. But I get those little dots in the result. Did I subtract those two images rigth? Both are bmp files.
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class Main2 {
public static void main(String[] args) throws Exception {
int[][][] ch = new int[4][4][4];
BufferedImage image1 = ImageIO.read(new File("1.bmp"));
BufferedImage image2 = ImageIO.read(new File("2.bmp"));
BufferedImage image3 = new BufferedImage(image1.getWidth(), image1.getHeight(), image1.getType());
int color;
for(int x = 0; x < image1.getWidth(); x++)
for(int y = 0; y < image1.getHeight(); y++) {
color = Math.abs(image2.getRGB(x, y) - image1.getRGB(x, y));
image3.setRGB(x, y, color);
}
ImageIO.write(image3, "bmp", new File("image.bmp"));
}
}
Image 1
Image 2
Result
The problem here is that you can't subtract the colors direcly. Each pixel is represented by one int value. This int value consists of 4 bytes. These 4 bytes represent the color components ARGB, where
A = Alpha
R = Red
G = Green
B = Blue
(Alpha is the opacity of the pixel, and always 255 (that is, the maximum value) in BMP images).
Thus, one pixel may be represented by
(255, 0, 254, 0)
When you subtract another pixel from this one, like (255, 0, 255, 0), then the third byte will underflow: It would become -1. But since this is part of ONE integer, the resulting color will be something like
(255, 0, 254, 0) -
(255, 0, 255, 0) =
(255, 255, 255, 0)
and thus, be far from what you would expect in this case.
The key point is that you have to split your color into the A,R,G and B components, and perform the computation on these components. In the most general form, it may be implemented like this:
int argb0 = image0.getRGB(x, y);
int argb1 = image1.getRGB(x, y);
int a0 = (argb0 >> 24) & 0xFF;
int r0 = (argb0 >> 16) & 0xFF;
int g0 = (argb0 >> 8) & 0xFF;
int b0 = (argb0 ) & 0xFF;
int a1 = (argb1 >> 24) & 0xFF;
int r1 = (argb1 >> 16) & 0xFF;
int g1 = (argb1 >> 8) & 0xFF;
int b1 = (argb1 ) & 0xFF;
int aDiff = Math.abs(a1 - a0);
int rDiff = Math.abs(r1 - r0);
int gDiff = Math.abs(g1 - g0);
int bDiff = Math.abs(b1 - b0);
int diff =
(aDiff << 24) | (rDiff << 16) | (gDiff << 8) | bDiff;
result.setRGB(x, y, diff);
Since these are grayscale images, the computations done here are somewhat redundant: For grayscale images, the R, G and B components are always equal. And since the opacity is always 255, it does not have to be treated explicitly here. So for your particular case, it should be sufficient to simplify this to
int argb0 = image0.getRGB(x, y);
int argb1 = image1.getRGB(x, y);
// Here the 'b' stands for 'blue' as well
// as for 'brightness' :-)
int b0 = argb0 & 0xFF;
int b1 = argb1 & 0xFF;
int bDiff = Math.abs(b1 - b0);
int diff =
(255 << 24) | (bDiff << 16) | (bDiff << 8) | bDiff;
result.setRGB(x, y, diff);
You did not "subtract one pixel from the other" correctly. getRGB returns "an integer pixel in the default RGB color model (TYPE_INT_ARGB)". What you are seeing is an "overflow" from one byte into the next, and thus from one color into the next.
Suppose you have colors 804020 - 404120 -- this is 3FFF00; the difference in the G component, 1 gets output as FF.
The correct procedure is to split the return value from getRGB into separate red, green, and blue, subtract each one, make sure they fit into unsigned bytes again (I guess your Math.abs is okay) and then write out a reconstructed new RGB value.
I found this which does what you want. It does seem to do the same thing and it may be more "correct" than your code. I assume it's possible to extract the source code.
http://tutorial.simplecv.org/en/latest/examples/image-math.html
/Fredrik Wahlgren

How to get pixel value of Black and White Image?

I making App in netbeans platform using java Swing and JAI. In this i want to do image processing. I capture .tiff black and white image using X-Ray gun. after that i want to plot histogram of that Black and White image. so, for plot to histogram , first we have to get gray or black and white image pixel value. then we can plot histogram using this pixel value.so, how can i get this pixel value of black and white image?
This should work if you use java.awt.image.BufferedImage.
Since you want to create a histogram, I suppose you will loop through all the pixels. There is the method for returning a single pixel value.
int getRGB(int x, int y)
However, since looping will take place I suppose you'd want to use this one:
int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize)
When you get the array, use:
int alpha = (pixels[i] >> 24) & 0x000000FF;
int red = (pixels[i] >> 16) & 0x000000FF;
int green = (pixels[i] >>8 ) & 0x000000FF;
int blue = pixels[i] & 0x000000FF;
To extract the channel data. Not sure if the variables can be declared as byte (we are using only one byte of the integer in the array, although byte is signed and different arithmetic takes place - two's complement form), but you can declare them as short.
Then preform some maths on these values, for example:
int average = (red + green + blue) / 3;
This will return the average for the pixel, giving you a point you can use in a simple luminosity histogram.
EDIT:
Regarding histogram creation, I have used this class. It takes the image you want the histogram of as an argument to its setImage(BufferedImage image) method. Use updateHistogram() for array populating. The drawing data is in paintComponent(Graphics g). I must admit, it is sloppy, especially when calculating the offsets, but it can be easily simplified.
Here is the whole class:
class HistogramCtrl extends JComponent
{
BufferedImage m_image;
int[] m_histogramArray = new int[256]; //What drives our histogram
int m_maximumPixels;
public HistogramCtrl(){
m_maximumPixels = 0;
for(short i = 0; i<256; i++){
m_histogramArray[i] = 0;
}
}
void setImage(BufferedImage image){
m_image = image;
updateHistogram();
repaint();
}
void updateHistogram(){
if(m_image == null) return;
int[] pixels = m_image.getRGB(0, 0, m_image.getWidth(), m_image.getHeight(), null, 0, m_image.getWidth());
short currentValue = 0;
int red,green,blue;
for(int i = 0; i<pixels.length; i++){
red = (pixels[i] >> 16) & 0x000000FF;
green = (pixels[i] >>8 ) & 0x000000FF;
blue = pixels[i] & 0x000000FF;
currentValue = (short)((red + green + blue) / 3); //Current value gives the average //Disregard the alpha
assert(currentValue >= 0 && currentValue <= 255); //Something is awfully wrong if this goes off...
m_histogramArray[currentValue] += 1; //Increment the specific value of the array
}
m_maximumPixels = 0; //We need to have their number in order to scale the histogram properly
for(int i = 0; i < m_histogramArray.length;i++){ //Loop through the elements
if(m_histogramArray[i] > m_maximumPixels){ //And find the bigges value
m_maximumPixels = m_histogramArray[i];
}
}
}
protected void paintComponent(Graphics g){
assert(m_maximumPixels != 0);
Rectangle rect = g.getClipBounds();
Color oldColor = g.getColor();
g.setColor(new Color(210,210,210));
g.fillRect((int)rect.getX(), (int)rect.getY(), (int)rect.getWidth(), (int)rect.getHeight());
g.setColor(oldColor);
String zero = "0";
String thff = "255";
final short ctrlWidth = (short)rect.getWidth();
final short ctrlHeight = (short)rect.getHeight();
final short activeWidth = 256;
final short activeHeight = 200;
final short widthSpacing = (short)((ctrlWidth - activeWidth)/2);
final short heightSpacing = (short)((ctrlHeight - activeHeight)/2);
Point startingPoint = new Point();
final int substraction = -1;
startingPoint.x = widthSpacing-substraction;
startingPoint.y = heightSpacing+activeHeight-substraction;
g.drawString(zero,widthSpacing-substraction - 2,heightSpacing+activeHeight-substraction + 15);
g.drawString(thff,widthSpacing+activeWidth-substraction-12,heightSpacing+activeHeight-substraction + 15);
g.drawLine(startingPoint.x, startingPoint.y, widthSpacing+activeWidth-substraction, heightSpacing+activeHeight-substraction);
g.drawLine(startingPoint.x,startingPoint.y,startingPoint.x,heightSpacing-substraction);
double factorHeight = (double)activeHeight / m_maximumPixels; //The height divided by the number of pixels is the factor of multiplication for the other dots
Point usingPoint = new Point(startingPoint.x,startingPoint.y);
usingPoint.x+=2; //I want to move this two points in order to be able to draw the pixels with value 0 a bit away from the limit
Point tempPoint = new Point();
for(short i = 0; i<256; i++){
tempPoint.x = usingPoint.x;
tempPoint.y = (int)((heightSpacing+activeHeight-substraction) - (m_histogramArray[i] * factorHeight));
if((i!=0 && (i % 20 == 0)) || i == 255){
oldColor = g.getColor();
g.setColor(oldColor.brighter());
//Draw horizontal ruler sections
tempPoint.x = widthSpacing + i;
tempPoint.y = heightSpacing+activeHeight-substraction+4;
g.drawLine(tempPoint.x,tempPoint.y,widthSpacing + i,heightSpacing+activeHeight-substraction-4);
if(i <= 200){
//Draw vertical ruler sections
tempPoint.x = widthSpacing - substraction - 3;
tempPoint.y = heightSpacing+activeHeight-substraction-i;
g.drawLine(tempPoint.x,tempPoint.y,widthSpacing - substraction + 4, heightSpacing+activeHeight-substraction-i);
}
tempPoint.x = usingPoint.x;
tempPoint.y = usingPoint.y;
g.setColor(oldColor);
}
g.drawLine(usingPoint.x, usingPoint.y, tempPoint.x, tempPoint.y);
usingPoint.x++; //Set this to the next point
}
}
}

Categories