I am trying to convert the contents of a JPanel into a BufferedImage. After looking around I have got this code.
BufferedImage image = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
this.paint(g);
I iterate through the image looking for pixels that are colored black using the following.
for(int i = 0; i < image.getWidth(); i++){
for(int j = 0; j < image.getHeight(); j++){
Color tempColor = new Color(image.getRGB(i, j));
if(tempColor == Color.BLACK){
System.out.println(tempColor); //Debugging
}
}
}
The JPanel contains many pixels that were painted using Color.BLACK (so yes they are black), although when running this code, it never prints the debugging line.
I believe the error in my code has to do with the way I am copying the contents of the JPanel into the BufferedImage, I can't seem to figure out what I am doing wrong. Any help is greatly appreciated, thanks.
You are performing a reference equality test when testing tempColor == Color.BLACK. But new Color(…) always creates a new object which can never be the same object as the predefined Color.BLACK instance, thus the == check will always be false.
Use equals or simply omit dealing with Color objects at all and just check whether image.getRGB(i, j) == 0 or if you don't want to use zero for black you can also use image.getRGB(i, j) == Color.BLACK.getRGB()
Thanks to #Holger for this answer.
for(int i = 0; i < image.getWidth(); i++){
for(int j = 0; j < image.getHeight(); j++){
Color tempColor = new Color(image.getRGB(i, j));
if(tempColor.equals(Color.BLACK)){ // Error was here
System.out.println(tempColor); //Debugging
}
}
}
Originally I had the code
if(tempColor == Color.BLACK)
instead of
if(tempColor.equals(Color.BLACK))
What I had to begin with will always evaluate to false, which was the error.
Related
This one is for a school assignment.
I am new to Processing software and I want to create a rainbow-filled window exactly like in the picture (at the center) below.
The program on the left is the one I have right now.
The program in the center is what I want it to look like.
On the right is the code I am using. I'll copy-paste it here.
void setup() {
size(255, 255);
}
void draw() {
noStroke();
colorMode(RGB, 255,255,255);
for (int i = 0; i <255; i++) {
for (int j = 0; j < 255; j++) {
stroke(j,i,128);
point(i, j);
}
}
}
Any help, suggestions, adjustments to the code would be greatly appreciated. Thanks in advance.
You would benefit from some pseudocode. Never underestimate the power of pseudocode.
In this image, everything you need to do is written plain as day:
Since we're working in RGB, and that the image tells you what to do with red, green and blue, you're already golden, but to make things more transparent we'll alter the code a little bit. Let's forget about the loops for now. Here's what the picture tells you to do:
R -> vertical slider, the closer to the bottom the more red you have
G -> horizontal slider, left is less and right is more
B -> vertical slider, the opposite to the red slider
Now, knowing that your values are on a [0-255] scale, dans that your image also is a 256 pixels wide square, you just have to use the index of your loops to get your RGB values:
for (int i = 0; i <255; i++) {
for (int j = 0; j < 255; j++) {
int r = j; // up == more red
int g = i; // right == more green
int b = 255 - j; // down == less blue
stroke(color(r, g, b));
point(i, j);
}
}
Also, just for kicks, as this is a static image and not an animation, you can put this code in the setup() method and it'll have the same result:
void setup() {
size(255, 255);
for (int i = 0; i <255; i++) {
for (int j = 0; j < 255; j++) {
int r = j; // up == more red
int g = i; // right == more green
int b = 255 - j; // down == less blue
stroke(color(r, g, b));
point(i, j);
}
}
}
void draw() {} // you still need this method even if it's empty
Which gives you this result:
Have fun!
I am manipulating code of a image renderer that is making output image from Color[] array and my code simply update it with additional stuff right before saving, that is when the original image is actually prepared (all pixels positions prepared to be filled with RGBs in that Color[] array ready for final saving).
Reason why I am doing this is to have ability to insert text describing my render without need of another external graphics program that would do that (I want to have it all in one-go! action without need of another external app).
For that cause - as I have no reach/access for the original prepared BufferedImage (but I have access to actual Color[] that it is created from) I had to make my own class method that:
convert that original Color[] to my own temporary BufferedImage
update that temp. BufferedImage with my stuff via Graphics2D (adding some text to image)
convert my result (temp. BufferedImage with Graphics2D) back to Color[]
send that final Color[] back to the original image rendering method
that would actually make it to be the final image that is rendered out
and saved as png
Now everything works just fine as I expected except one really annoying thing that I cannot get rid off: my updated image looks very bleached-like/pale (almost no depth or shadows presented) compared to the original un-watermarked version...
To me it simply looks like after the image2color[] conversion (using #stacker's solution from here Converting Image to Color array) something goes wrong/is not right so the colors become pale and I do not have any clue why.
Here is the main part of my code that is in question:
BufferedImage sourceImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Color[] to BufferedImage
for (int k = 0; k < multiArrayList.size(); k++) {
// PREPARE...
int x = (int) multiArrayList.get(k)[0];
int y = (int) multiArrayList.get(k)[1];
int w = (int) multiArrayList.get(k)[2];
int h = (int) multiArrayList.get(k)[3];
Color[] data = (Color[]) multiArrayList.get(k)[4];
int border = BORDERS[k % BORDERS.length];
for (int by = 0; by < h; by++) {
for (int bx = 0; bx < w; bx++) {
if (bx == 0 || bx == w - 1) {
if (5 * by < h || 5 * (h - by - 1) < h) {
sourceImage.setRGB(x + bx, y + by, border);
}
} else if (by == 0 || by == h - 1) {
if (5 * bx < w || 5 * (w - bx - 1) < w) {
sourceImage.setRGB(x + bx, y + by, border);
}
}
}
}
// UPDATE...
for (int j = 0, index = 0; j < h; j++) {
for (int i = 0; i < w; i++, index++) {
sourceImage.setRGB(x + i, y + j, data[index].copy().toNonLinear().toRGB());
}
}
}
Graphics2D g2d = (Graphics2D) sourceImage.getGraphics();
// paints the textual watermark
drawString(g2d, text, centerX, centerY, sourceImage.getWidth());
// when saved to png at this point ALL IS JUST FINE
ImageIO.write(sourceImage, "png", new File(imageSavePath));
g2d.dispose();
// BufferedImage to Color array
int[] dt = ((DataBufferInt) sourceImage.getRaster().getDataBuffer()).getData();
bucketFull = new Color[dt.length];
for (int i = 0; i < dt.length; i++) {
bucketFull[i] = new Color(dt[i]);
}
// update and repaint output image - THIS OUTPUT IS ALREADY BLEACHED/PALE
d.ip(0, 0, width, height, renderThreads.length + 1);
d.iu(0, 0, width, height, bucketFull);
// reset objects
g2d = null;
sourceImage = null;
bucketFull = null;
multiArrayList = new ArrayList<>();
I have tested (by saving it to another .png file right after the Graphics2D addition) that before it gets that 2nd conversion it looks absolutely OK 1:1 to the original image incl. my text on that image.
But as I said when it is send for render it becomes bleached/pale that is a problem I am trying to solve.
BTW I first thought it might be that Graphics2D addition so I did try it without it but the result was the same, that is bleached/pale version.
Although my process and code is completely different the output image is basically suffering exactly the same way as in this topic (still not solved) BufferedImage color saturation
Here are my 2 examples - 1st ORIGINAL, 2nd UPDATED (bleached/pale)
As suspected, the problem is that you convert the color values from linear RGB to gamma-corrected/sRGB values when setting the RGB values to the BufferedImage, but the reverse transformation (back to linear RGB) is not done when you put the values back into the Color array.
Either change the line (inside the double for loop):
sourceImage.setRGB(x + i, y + j, data[index].copy().toNonLinear().toRGB());
to
sourceImage.setRGB(x + i, y + j, data[index].toRGB());
(you don't need the copy() any more, as you no longer mutate the values, using toNonLinear()).
This avoids the conversion altogether.
... or you could probably also change the line setting the values back, from:
bucketFull[i] = new Color(dt[i]);
to
bucketFull[i] = new Color(dt[i]).toLinear();
Arguably, this is more "correct" (as AWT treats the values as being in the sRGB color space, regardless), but I believe the first version is faster, and the difference in color is negligible. So I'd probably try the first suggested fix first, and use that unless you experience colors that are off.
I am doing OCR and sometime I have a light text on a dark background. When I encounter this situation I need the program to know that it has to invert the colours. The code I have wrote doesn't work how I want. It is detecting dark colours as light and light colours as dark. Any ideas what I've done wrong?
File input = new File("/Users/unknown1/Desktop/t5.png");
BufferedImage imagegrey = ImageIO.read(input);
toGray(imagegrey);
int width = imagegrey.getWidth();
int height = imagegrey.getHeight();
int light = 0;
int dark = 0;
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
Color color = robot.getPixelColor(j, i);
int grey = ((color.getRed() + color.getBlue() + color.getGreen())/3);
//System.out.println(grey);
if (grey >= 237) {
light++;
}
else {
dark++;
}
}
}
System.out.println(light);
System.out.println(dark);
This as itself should work - pick up the color at location j i on the screen. Now if that location is covered by the image you intend is another issue we [here] dont' know what is in that spot. And if toGray() does some wierd transform that negates the image. So check these
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
Color color = robot.getPixelColor(j, i);
int grey = ((color.getRed() + color.getBlue() + color.getGreen())/3);
//System.out.println(grey);
if (grey >= 237) {
light++;
}
else {
dark++;
}
}
I am making a game. It is a 2D game, and the map is drawn by a double loop like this:
for(int i = 0; i < mapArray.length; i++){
for(int j = 0; j < mapArray[1].length; j++){
//if statement for if its on the screen
g.drawImage(tiles.get(mapArray[i][j]).getImage(), j * textureSize, i * textureSize, null);
}
}
They are 32x32 images, and I was wondering stitching them all together to create one large image at the beginning would be more efficient in drawing. The resulting image would only be around 1500x1500.
I was thinking of making it stitched into one image (Especially since I am planning on making the images smaller which would make the loop need ever more time.) so that it didn't have to run through the double for loop every time it renders (shooting for 60 FPS). But I don't know how to do this, and would it actually improve the performance if I did?
Also, I could just stitch them into rows, and only render the rows that are on the screen(to remove the large image problem) So it would still be much less intensive than that crazy loop I've got right now.
Edit: And one last thing, if you can provide an example of how to do this without extra libraries that would be optimal.
I currently have this code for stitching:
Edit: now works. Leaving this here for future readers:
public void stitchImages(){
BufferedImage temp = new BufferedImage( <Width> , <height> , BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) temp.getGraphics();
for (int b = 0; b < mapArray.length; b++) {
for (int a = 0; a < mapArray[b].length; a++) {
g.drawImage(tiles.get(mapArray[b][a]).getImage(),
(int) mapX + a * textureSize, (int) mapY + b
* textureSize, null);
}
}
mapImage = temp;
}
Create a new Image to encapsulate all of your images. Draw your images when you load up, then just draw that in paintComponent()
BufferedImage im = new BufferedImage(1500,1500,BufferedImage.TYPE_INT_RGB);
private void init() {
Graphics g = im.getGraphics();
for(int i = 0; i < mapArray.length; i++){
for(int j = 0; j < mapArray[1].length; j++){
//if statement for if its on the screen
g.drawImage(tiles.get(mapArray[i][j]).getImage(), j * textureSize, i * textureSize, null);
}
}
}
public void paintCompoent(Graphics g) {
super.paintComponent(g);
g.drawImage(im,0,0,null);
}
EDIT:
As for your idea about just painting the lines that are on the screen, you can do that by creating an Image the size of the window and just drawing to that. But in general, it's not a big problem to paint a big Image (as long as the Image fits in memory and you don't get an OutOfMemoryException) as your GPU's capabilities smoke your CPU's
I'm attempting to take a picture as input, then manipulate said picture (I specifically want to make it greyscale) and then output the new image. This is a snippet of the code that I'm editing in order to do so but I'm getting stuck. Any ideas of what I can change/do next. Greatly appreciated!
public boolean recieveFrame (Image frame) {
int width = frame.width();
int height = frame.height();
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
Color c1 = frame.get(i, j);
double greyScale = (double) ((Color.red *.3) + (Color.green *.59) + (Color.blue * .11));
Color newGrey = Color.greyScale(greyScale);
frame.set(i, j, newGrey);
}
}
boolean shouldStop = displayImage(frame);
return shouldStop;
}
I'm going to try to stick as close as possible to what you already have. So, I'll assume that you are looking for how to do pixel-level processing on an Image, rather than just looking for a technique that happens to work for converting to greyscale.
The first step is that you need the image to be a BufferedImage. This is what you get by default from ImageIO, but if you have some other type of image, you can create a BufferedImage and paint the other image into it first:
BufferedImage buffer = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = buffer.createGraphics();
g.drawImage(image, 0, 0);
g.dispose()
Then, you can operate on the pixels like this:
public void makeGrey(BufferedImage image) {
for(int x = 0; x < image.getWidth(); ++x) {
for(int y = 0; y < image.getHeight(); ++y) {
Color c1 = new Color(image.getRGB(x, y));
int grey = (int)(c1.getRed() * 0.3
+ c1.getGreen() * 0.59
+ c1.getBlue() * .11
+ .5);
Color newGrey = new Color(grey, grey, grey);
image.setRGB(x, y, newGrey.getRGB());
}
}
}
Note that this code is horribly slow. A much faster option is to extract all the pixels from the BufferedImage into an int[], operate on that, and then set it back into the image. This uses the other versions of the setRGB()/getRGB() methods that you'll find in the javadoc.