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.
Related
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);
I'm trying to analyze an image-based 3digit number captcha from an online resource. The numbers do not move at all. I use BufferedImage's getSubimage(...) method to extract each number from the captcha. I have saved (0-9) for each of the ones, tens and hundreds place. (So 30 numbers in total)
I read the bytes of the online image into a byte[] and then create a BufferedImage object like this:
BufferedImage captcha = ImageIO.read(new ByteArrayInputStream(captchaBytes));
Then I compare this image to a list of images on my drive:
BufferedImage[] nums = new BufferedImage[10];
//Load images into the array here... The code is removed.
for(int i = 0; i < nums.length; i++) {
double x;
System.out.println(x = bufferedImagesEqualConfidence(nums[i], firstNumberImage));
if(x > 0.98) {
System.out.println("equal to image " + i + ".jpeg");
isNewEntry = false;
break;
}
}
This is how I compare two images:
static double bufferedImagesEqualConfidence(BufferedImage img1, BufferedImage img2) {
double difference = 0;
int pixels = img1.getWidth() * img1.getHeight();
if (img1.getWidth() == img2.getWidth() && img1.getHeight() == img2.getHeight()) {
for (int x = 0; x < img1.getWidth(); x++) {
for (int y = 0; y < img1.getHeight(); y++) {
int rgbA = img1.getRGB(x, y);
int rgbB = img2.getRGB(x, y);
int redA = (rgbA >> 16) & 0xff;
int greenA = (rgbA >> 8) & 0xff;
int blueA = (rgbA) & 0xff;
int redB = (rgbB >> 16) & 0xff;
int greenB = (rgbB >> 8) & 0xff;
int blueB = (rgbB) & 0xff;
difference += Math.abs(redA - redB);
difference += Math.abs(greenA - greenB);
difference += Math.abs(blueA - blueB);
}
}
} else {
return 0.0;
}
return 1-((difference/(double)pixels) / 255.0);
}
The image is loaded completely from a HttpURLConnection object wrapped in my own HttpGet object. And so I do: byte[] captchaBytes = hg.readAndGetBytes(); Which I know works because when I save BufferedImage captcha = ImageIO.read(new ByteArrayInputStream(captchaBytes));, it saves as a valid image on my drive.
However, even though 2 images are actually the same, the result shows they are not similar at all. BUT, when I save the image I downloaded from the online resource first, re-read it, and compare, it shows they are equal. This is what I'm doing when I say I save it and re-read it:
File temp = new File("temp.jpeg");
ImageIO.write(secondNumberImage, "jpeg", temp);
secondNumberImage = ImageIO.read(temp);
Image format: JPEG
I know this may have something to do with compression from ImageIO.write(...), but how can I make it so that I don't have to save the image?
The problem was within my bufferedImagesEqualConfidence method. Simply comparing RGB was not enough. I had to compare individual R/G/B values.
My initial bufferedImagesEqualConfidence that didn't work was:
static double bufferedImagesEqualConfidence(BufferedImage img1, BufferedImage img2) {
int similarity = 0;
int pixels = img1.getWidth() * img1.getHeight();
if (img1.getWidth() == img2.getWidth() && img1.getHeight() == img2.getHeight()) {
for (int x = 0; x < img1.getWidth(); x++) {
for (int y = 0; y < img1.getHeight(); y++) {
if (img1.getRGB(x, y) == img2.getRGB(x, y)) {
similarity++;
}
}
}
} else {
return 0.0;
}
return similarity / (double)pixels;
}
(Source: Java Compare one BufferedImage to Another)
The bufferedImagesEqualConfidence that worked is:
static double bufferedImagesEqualConfidence(BufferedImage img1, BufferedImage img2) {
double difference = 0;
int pixels = img1.getWidth() * img1.getHeight();
if (img1.getWidth() == img2.getWidth() && img1.getHeight() == img2.getHeight()) {
for (int x = 0; x < img1.getWidth(); x++) {
for (int y = 0; y < img1.getHeight(); y++) {
int rgbA = img1.getRGB(x, y);
int rgbB = img2.getRGB(x, y);
int redA = (rgbA >> 16) & 0xff;
int greenA = (rgbA >> 8) & 0xff;
int blueA = (rgbA) & 0xff;
int redB = (rgbB >> 16) & 0xff;
int greenB = (rgbB >> 8) & 0xff;
int blueB = (rgbB) & 0xff;
difference += Math.abs(redA - redB);
difference += Math.abs(greenA - greenB);
difference += Math.abs(blueA - blueB);
}
}
} else {
return 0.0;
}
return 1-((difference/(double)pixels) / 255.0);
}
(Source: Image Processing in Java)
I guess to find similarity between two images you have to compare the individual R/G/B values for each pixel rather than just the whole RGB value.
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.
I am new to Java and want to count red pixels in a given image. I have below code so far but not sure of what condition to add to check if pixel is red. I have below code so far. Thanks in advance.
public static int countRedPixels(Picture v){
BufferedImage image = (v.getBufferedImage());
int width = image.getWidth();
int height = image.getHeight();
int redCount = 0;
int pixelCount = 0;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height ; y++) {
int rgb = image.getRGB(x, y);
//get rgbs
//int alpha = (rgb >>> 24) & 0xFF;
int red = (rgb >>> 16) & 0xFF;
int green = (rgb >>> 8) & 0xFF;
int blue = (rgb >>> 0) & 0xFF;
if (red == 255 && green == 0 && blue == 0 || image.getRGB(x, y) == 0xFFFF0000) {
redCount++;
}
pixelCount++;
}
}
System.out.println("Red Pixel Count:" + redCount);
System.out.println("Pixel Count:" + pixelCount);
return redCount;
}
Not sure if I'm missing something or you're really not seeing the forest for the trees. Anyway, comment turned answer:
Given that red means (255, 0, 0), you could do:
if (image.getRGB(x, y) == 0xFFFF0000) {
++redCount;
}
Or alternatively, if you don't care about alpha:
if (red == 255 && green == 0 && blue == 0) {
++redCount;
}
I need to toggle on/off RGB channels of an image, but I am stuck and my code is buggy.
Can you help me figure out how to do this the right way? This is my code:
The function channels is called when 1 of 3 checkboxes has changed its state and provides the arguments which are true == selected
public void channels(boolean red, boolean green, boolean blue) {
if (this.img != null) {// checks if the image is set
char r = 0xFF, g = 0xFF, b = 0xFF;
if (red == false) {
r = 0x00;
}
if (green == false) {
g = 0x00;
}
if (blue == false) {
b = 0x00;
}
BufferedImage tmp = new BufferedImage(
img.getWidth(),
img.getHeight(),
BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < img.getWidth(); i++) {
for (int j = 0; j < img.getHeight(); j++) {
int rgb = img.getRGB(i, j);
int red = (rgb >> 16) & r;
int green = (rgb >> 8) & g;
int blue = (rgb >> 0) & b;
int gbr = (red << 16) | (green << 8) | blue;// EDITED
tmp.setRGB(i, j, gbr);
}
}
img = tmp;
repaint();
} else {
//show error
}
}
Thank you for your help!
How about this optimized version, with a lot less bit shifting?
public void channels(boolean showRed, boolean showGreen, boolean showBlue) {
if (this.origImg!= null) {// checks if the image is set
int channelMask = 0xff << 24 | (showRed ? 0xff : 0) << 16 | (showGreen ? 0xff : 0) << 8 | (showBlue ? 0xff : 0);
BufferedImage tmp = new BufferedImage(origImg.getWidth(), origImg.getHeight(), BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < origImg.getWidth(); i++) {
for (int j = 0; j < origImg.getHeight(); j++) {
int rgb = origImg.getRGB(i, j);
tmp.setRGB(i, j, rgb & channelMask);
}
}
img = tmp;
repaint();
} else {
//show error
}
}
A faster approach yet, would probably be to use a channeled Raster, or at least a Raster configuration that allows band sub-sampling (see Raster.createChild(...) method, especially the last parameter).
LookupOp, as mentioned by #trashgod is also a good idea, and probably faster than the getRGB()/setRGB() approach.
It looks like you're shifting in the bits wrong. Shouldn't it be: int gbr = (red << 16) | (green << 8) | blue;? You basically want to shift back in the same order as how you shifted out to begin with.
Also, once you have cleared the corresponding colour, there's no way for you to get it back. You'll need to store a copy of the original image somewhere. When it's time to turn the channel back on, simply copy the original pixel from the original image back.
Assuming that you have the original image stored somewhere as origImg, I would modify your for loop so that if the channel is toggled on, copy from the original image.
for (int i = 0; i < img.getWidth(); i++) {
for (int j = 0; j < img.getHeight(); j++) {
int rgb = img.getRGB(i, j);
int origRGB = origImg.getRGB(i, j);
int redPixel = red ? (origRGB >> 16) & r : (rgb >> 16) & r;
int greenPixel = green ? (origRGB >> 8) & g : (rgb >> 8) & g;
int bluePixel = blue ? origRGB & b : rgb & b;
int gbr = (redPixel << 16) | (greenPixel << 8) | bluePixel;
tmp.setRGB(i, j, gbr);
}
}