java get difference between two images in percent - java

I have the following code:
private static int pixelDiff(int rgb1, int rgb2) {
int r1 = (rgb1 >> 16) & 0xff;
int g1 = (rgb1 >> 8) & 0xff;
int b1 = rgb1 & 0xff;
int r2 = (rgb2 >> 16) & 0xff;
int g2 = (rgb2 >> 8) & 0xff;
int b2 = rgb2 & 0xff;
return Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2);
}
and it works without a problem, but it takes to long and i don't know how it optimize it.
So the basic is, that i want to compare two images and get the percentage of difference.
Therefor I load the RGB of both images and compare them with this code.
My question: Is it possible to optimize this code, or do you have any idea to compare two images(not only that they are equal)
UPDATE:
here is the full code:
private double getDifferencePercent(BufferedImage img1, BufferedImage img2) {
int width = img1.getWidth();
int height = img1.getHeight();
int width2 = img2.getWidth();
int height2 = img2.getHeight();
if (width != width2 || height != height2) {
throw new IllegalArgumentException(String.format("Images must have the same dimensions: (%d,%d) vs. (%d,%d)", width, height, width2, height2));
}
long diff = 0;
for (int y = height - 1; y >= 0; y--) {
for (int x = width - 1; x >= 0; x--) {
diff += pixelDiff(img1.getRGB(x, y), img2.getRGB(x, y));
}
}
long maxDiff = 765L * width * height;
return 100.0 * diff / maxDiff;
}
private static int pixelDiff(int rgb1, int rgb2) {
int r1 = (rgb1 >> 16) & 0xff;
int g1 = (rgb1 >> 8) & 0xff;
int b1 = rgb1 & 0xff;
int r2 = (rgb2 >> 16) & 0xff;
int g2 = (rgb2 >> 8) & 0xff;
int b2 = rgb2 & 0xff;
return Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2);
}
I checked this with a profiler and it shows that pixelDiff() is very slow.

Related

How to recolorize an image in JavaFX

I have the following constructor for a RecoloredImage that takes an old image, and replaces every old colored pixel with a new colored pixel. However, the image doesn't actually change. The code between the comments is purely for testing purposes, and the resulting printed line is not at all the new color I want.
public RecoloredImaged(Image inputImage, Color oldColor, Color newColor) {
int width = (int) inputImage.getWidth();
int height = (int) inputImage.getHeight();
WritableImage outputImage = new WritableImage(width, height);
PixelReader reader = inputImage.getPixelReader();
PixelWriter writer = outputImage.getPixelWriter();
// -- testing --
PixelReader newReader = outputImage.getPixelReader();
// -- end testing --
int ob = (int) oldColor.getBlue() * 255;
int or = (int) oldColor.getRed() * 255;
int og = (int) oldColor.getGreen() * 255;
int nb = (int) newColor.getBlue() * 255;
int nr = (int) newColor.getRed() * 255;
int ng = (int) newColor.getGreen() * 255;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int argb = reader.getArgb(x, y);
int a = (argb >> 24) & 0xFF;
int r = (argb >> 16) & 0xFF;
int g = (argb >> 8) & 0xFF;
int b = argb & 0xFF;
if (g == og && r == or && b == ob) {
r = nr;
g = ng;
b = nb;
}
argb = (a << 24) | (r << 16) | (g << 8) | b;
writer.setArgb(x, y, argb);
// -- testing --
String s = Integer.toHexString(newReader.getArgb(x, y));
if (!s.equals("0"))
System.out.println(s);
// -- end testing --
}
}
image = outputImage;
}
The cast operator has a higher precedence than the multiplication operator. Your calculations for the or, ..., nb values are therefore compiled to the same bytecode as this code:
int ob = ((int) oldColor.getBlue()) * 255;
int or = ((int) oldColor.getRed()) * 255;
int og = ((int) oldColor.getGreen()) * 255;
int nb = ((int) newColor.getBlue()) * 255;
int nr = ((int) newColor.getRed()) * 255;
int ng = ((int) newColor.getGreen()) * 255;
Just add brackets to tell java to do the multiplication before casting. Otherwise you'll only get values 0 or 255 as results.
int ob = (int) (oldColor.getBlue() * 255);
int or = (int) (oldColor.getRed() * 255);
int og = (int) (oldColor.getGreen() * 255);
int nb = (int) (newColor.getBlue() * 255);
int nr = (int) (newColor.getRed() * 255);
int ng = (int) (newColor.getGreen() * 255);

get average image of a set of images in java

I have a set of meteorological RGB type BufferedImages. I want to get average image of them. By that, I mean get average value of each pixel and make a new image out of those values. What I tried is this:
public void getWaveImage(BufferedImage input1, BufferedImage input2){
// images are of same size that's why i'll use first one's width and height
int width = input1.getWidth(), height = input1.getHeight();
BufferedImage output = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
int[] rgb1 = input1.getRGB(0, 0, width, height, new int[width * height], 0, width);
int[] rgb2 = input2.getRGB(0, 0, width, height, new int[width * height], 0, width);
for(int i=0; i<width; i++){
for(int j=0; j<height; j++){
int rgbIndex = i * width + j;
rgb1[rgbIndex] = (rgb1[rgbIndex] + rgb2[rgbIndex]) / 2;
}
}
output.setRGB(0, 0, width, height, rgb1, 0, width);
return output;
}
What am I doing wrong? Thank you in advance.
input1:
input2:
output:
You want the average of each component of the colour, average red, average green, average blue.
Instead you are averaging the whole int.
Color c1 = new Color(rgb1[rgbIndex]);
Color c2 = new Color(rgb2[rgbIndex]);
Color cA = new Color((c1.getRed() + c2.getRed())/2,
(c1.getGreen() + c2.getGreen())/2,
(c1.getBlue() + c2.getBlue())/2);
rgb1[rgbIndex] = cA.getRGB();
This may not be the most efficient due to creating so many objects, so a more direct approach is like so:
public static int average(int argb1, int argb2){
return (((argb1 & 0xFF) + (argb2 & 0xFF)) >> 1) | //b
(((argb1 >> 8 & 0xFF) + (argb2 >> 8 & 0xFF)) >> 1) << 8 | //g
(((argb1 >> 16 & 0xFF) + (argb2 >> 16 & 0xFF)) >> 1) << 16 | //r
(((argb1 >> 24 & 0xFF) + (argb2 >> 24 & 0xFF)) >> 1) << 24; //a
}
Usage:
rgb1[rgbIndex] = average(rgb1[rgbIndex], rgb2[rgbIndex]);
If you have:
int rgb1, rgb2; //the rgb value of a pixel in image 1 and 2 respectively
The "average" color would be:
int r = (r(rgb1) + r(rgb2)) / 2;
int g = (g(rgb1) + g(rgb2)) / 2;
int b = (b(rgb1) + b(rgb2)) / 2;
int rgb = ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | ((b & 0xFF) << 0);
with the following "helper" methods:
private static int r(int rgb) { return (rgb >> 16) & 0xFF; }
private static int g(int rgb) { return (rgb >> 8) & 0xFF; }
private static int b(int rgb) { return (rgb >> 0) & 0xFF; }
Alternatively you can use the Color class if you don't want to deal with bitwise operations.
another solution can be to replace
rgb1[rgbIndex] = (rgb1[rgbIndex] + rgb2[rgbIndex]) / 2;
with
rgb1[rgbIndex] = ((rgb1[rgbIndex]>>1)&0x7f7f7f7f)+((rgb2[rgbIndex]>>1)&0x7f7f7f7f)+(rgb1[rgbIndex]&rgb2[rgbIndex]&0x01010101);
binary right shift to divide by 2, last member of the sum to handle the case of two odd numbers.

Java : Mix two colors in a realistic way

Given two colors A and B, I would like to get the resulting color C, that is the most possible realistic natural mix of the A and B.
Example :
Red + Yellow = Orange
Blue + Yellow = Green
Red + Blue = Purple
Blue + White = Light Blue
Blue + Black = Dark Blue
etc...
Can I get it with ARGB representation of the given colors?
We can call a function which returns result array when give two arrays as parameters. But Arrays should be same sizes.
public int getAvgARGB(int[] clr1, int[] clr2){
int[] returnArray = new int[clr1.length];
for(int i=0; i<clr1.length;i++){
int a1[i] = (clr1[i] & 0xFF000000) >>> 24;
int r1[i] = (clr1[i] & 0x00FF0000) >> 16;
int g1[i] = (clr1[i] & 0x0000FF00) >> 8;
int b1[i] = (clr1[i] & 0x000000FF) ;
int a2[i] = (clr2[i] & 0xFF000000) >>> 24;
int r2[i] = (clr2[i] & 0x00FF0000) >> 16;
int g2[i] = (clr2[i] & 0x0000FF00) >> 8;
int b2[i] = (clr2[i] & 0x000000FF) ;
int aAvg = (a1[i] + a2[i]) / 2;
int rAvg = (r1[i] + r2[i]) / 2;
int gAvg = (g1[i] + g2[i]) / 2;
int bAvg = (b1[i] + b2[i]) / 2;
int returnArray[i] = (aAvg << 24) + (rAvg << 16) + (gAvg << 8) + bAvg;
}
return returnArray;
}

Image difference in Java

public float calculateDifference(BufferedImage b1, BufferedImage b2){
float error = 0;
for(int y = 0; y < sizeY; y++){
for(int x = 0; x < sizeX; x++){
Color c1 = new Color(b1.getRGB(x, y));
Color c2 = new Color(b2.getRGB(x, y));
error += Math.abs(c1.getRed() - c2.getRed());
error += Math.abs(c1.getGreen() - c2.getGreen());
error += Math.abs(c1.getBlue() - c2.getBlue());
error += Math.abs(c1.getAlpha() - c2.getAlpha());
}
}
return error;
}
I have this function that compares two bufferedimages. It returns a higher error if the two images are more different. The only problem is it runs really slowly so is there any more efficient way to do this? Any way to lower the runtime would really help.
Yes you can optimize the internal for loop.
Don't create new Color objects. Use directly the int value of RGB. This will limit the number of Objects created and the frequency of call to Garbage collection.
int color1 = b1.getRGB(x, y);
int alpha1 = (color1 >> 24) & 0xFF;
int red1 = (color1 >> 16) & 0xFF;
int green1 = (color1 >> 8) & 0xFF;
int blue1 = (color1 >> 0) & 0xFF;
int color2 = b2.getRGB(x, y);
int alpha2 = (color2 >> 24) & 0xFF;
int red2 = (color2 >> 16) & 0xFF;
int green2 = (color2 >> 8) & 0xFF;
int blue2 = (color2 >> 0) & 0xFF;
error += Math.abs(red1 - red2);
error += Math.abs(green1 - green2);
error += Math.abs(blue1 - blue2);
error += Math.abs(alpha1 - alpha2);

Reading in a JPEG image and calculate image coordinates

I want to read in a JPEG image with a uniform gray background with several colored balls on it of the same size. I want a program which can take this image and record the coordinates of each ball. What's the best way to do this?
I agree with James. I used the following program once to find red boxes in an image (before most of the red boxes were recolored by the community):
/**
* #author karnokd, 2008.11.07.
* #version $Revision 1.0$
*/
public class RedLocator {
public static class Rect {
int x;
int y;
int x2;
int y2;
}
static List<Rect> rects = new LinkedList<Rect>();
static boolean checkRect(int x, int y) {
for (Rect r : rects) {
if (x >= r.x && x <= r.x2 && y >= r.y && y <= r.y2) {
return true;
}
}
return false;
}
public static void main(String[] args) throws Exception {
BufferedImage image = ImageIO.read(new File("fallout3worldmapfull.png"));
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int c = image.getRGB(x,y);
int red = (c & 0x00ff0000) >> 16;
int green = (c & 0x0000ff00) >> 8;
int blue = c & 0x000000ff;
// check red-ness
if (red > 180 && green < 30 && blue < 30) {
if (!checkRect(x, y)) {
int tmpx = x;
int tmpy = y;
while (red > 180 && green < 30 && blue < 30) {
c = image.getRGB(tmpx++,tmpy);
red = (c & 0x00ff0000) >> 16;
green = (c & 0x0000ff00) >> 8;
blue = c & 0x000000ff;
}
tmpx -= 2;
c = image.getRGB(tmpx,tmpy);
red = (c & 0x00ff0000) >> 16;
green = (c & 0x0000ff00) >> 8;
blue = c & 0x000000ff;
while (red > 180 && green < 30 && blue < 30) {
c = image.getRGB(tmpx,tmpy++);
red = (c & 0x00ff0000) >> 16;
green = (c & 0x0000ff00) >> 8;
blue = c & 0x000000ff;
}
Rect r = new Rect();
r.x = x;
r.y = y;
r.x2 = tmpx;
r.y2 = tmpy - 2;
rects.add(r);
}
}
}
}
}
}
Might give you some hints. The image originates from here.
You can use the ImageIO library to read in an image by using one of the read() methods. This produces a BufferedImage which you can analyze for the separate colors. getRGB() is probably the best way to do this. You can compare this to the getRGB() of a Color object if you need to. That should be enough to get you started.

Categories

Resources