Convert JPanel to binary array - java

I programmed something like paint. I have JPanel and I can draw on it.I'm using only black line. I wanna convert it to binary array, where 1 is when pixel is black, 0 when is white (background). It's possible? How to do this?

In a nutshell, create a BufferedImage with the same dimensions as your JPanel and paint the panel to the image. Then you can iterate over the image raster to get the sequence of pixel color values corresponding to black and white. For example
// Paint the JPanel to a BufferedImage.
Dimension size = jpanel.getSize();
int imageType = BufferedImage.TYPE_INT_ARGB;
BufferedImage image = BufferedImage(size.width, size.height, imageType);
Graphics2D g2d = image.createGraphics();
jpanel.paint(g2);
// Now iterate the image in row-major order to test its pixel colors.
for (int y=0; y<size.height; y++) {
for (int x=0; ix<size.width; x++) {
int pixel = image.getRGB(x, y);
if (pixel == 0xFF000000) {
// Black (assuming no transparency).
} else if (pixel == 0xFFFFFFFF) {
// White (assuming no transparency).
} else {
// Some other color...
}
}
}

Related

Java recoloring BufferedImage not working with an image of a larger height

I have a program that is supposed to take the RGB values of an image and then multiply them by some constants, and then draw the new image on a JPanel. The problem is that if my image is over a certain height, specifically over 187 pixels, the new colored image is different than an image with a height of less than 187px.
The JPanel shows this: example.
Notice how the longer recolored image is different than the shorter one. I'm sure that the shorter image's colors are correct, and I have no idea how it's getting messed up.
public class RecolorImage extends JPanel {
public static int scale = 3;
public static BufferedImage walk, walkRecolored;
public static BufferedImage shortWalk, shortWalkRecolored;
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(200*scale, 400*scale);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new RecolorImage());
walk = ImageLoader.loadImage("/playerWalk.png");
walkRecolored = recolor(walk);
shortWalk = ImageLoader.loadImage("/playerWalkShort.png");
shortWalkRecolored = recolor(shortWalk);
frame.setVisible(true);
}
#Override
public void paint(Graphics graphics) {
Graphics2D g = (Graphics2D) graphics;
g.scale(scale, scale);
g.drawImage(walk, 10, 10, null);
g.drawImage(walkRecolored, 40, 10, null);
g.drawImage(shortWalk, 70, 10, null);
g.drawImage(shortWalkRecolored, 100, 10, null);
}
The recolor method:
public static BufferedImage recolor(BufferedImage image) {
BufferedImage outputImage = deepCopy(image);
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int rgb = image.getRGB(x, y);
Color c = new Color(rgb);
int r = c.getRed();
int g = c.getGreen();
int b = c.getBlue();
r *= 0.791;
g *= 0.590;
b *= 0.513;
int newRGB = (rgb & 0xff000000) | (r << 16) | (g << 8) | b;
outputImage.setRGB(x, y, newRGB);
}
}
return outputImage;
}
How I load the images and make deep copies:
public static BufferedImage loadImage(String path) {
try {
return ImageIO.read(ImageLoader.class.getResource(path));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static BufferedImage deepCopy(BufferedImage image) {
ColorModel colorModel = image.getColorModel();
boolean isAlphaPremultiplied = colorModel.isAlphaPremultiplied();
WritableRaster raster = image.copyData(null);
return new BufferedImage(colorModel, raster, isAlphaPremultiplied, null);
}
My original images: the tall image and short image. Thanks for any help!
Your source images have different color models:
the short image uses 4 bytes per pixel (RGB and alpha)
the tall image uses 1 byte per pixel (index into a palette of 256 colors)
Your recolored images use the same color model as the source images (thanks to the deepCopy method), therefore the recolored image for the tall image also uses the same color palette as the source image, meaning that it cannot contain all the colors you want.
Since your recoloring code overwrites each pixel of the output image anyway the deep copy operation is unnecessary. Instead you would better create a full color image as target image like this:
public static BufferedImage recolor(BufferedImage image) {
BufferedImage outputImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
//... code as before
}

Why isn't my method for removing this color working?

I'm trying to make a Mario game clone, and right now, in my constructor, I have a method that is supposed to make a certain color transparent instead of the current pinkish (R: 255, G: 0, B: 254). According to Photoshop, the hex value is ff00fe. My method is:
public Mario(){
this.state = MarioState.SMALL;
this.x = 54;
this.y = 806;
URL spriteAtLoc = getClass().getResource("sprites/Mario/SmallStandFaceRight.bmp");
try{
sprite = ImageIO.read(spriteAtLoc);
int width = sprite.getWidth();
int height = sprite.getHeight();
int[] pixels = new int[width * height];
sprite.getRGB(0, 0, width, height, pixels, 0, width);
for (int i = 0; i < pixels.length; i++) {
if (pixels[i] == 0xFFff00fe) {
pixels[i] = 0x00ff00fe; //this is supposed to set alpha value to 0 and make the target color transparent
}
}
} catch(IOException e){
System.out.println("sprite not found");
e.printStackTrace();
}
}
it runs and compiles, but sprite comes out exactly the same when I render it. (edit: perhaps of note I do not have super.paintComponent(g) in my paintComponent(g) method. The sprites are .bmps.
You are only retrieving the pixels using BufferedImage.getRGB. That returns a copy of the data in a certain area of the BufferedImage.
Any change you make to the int[] returned is not automatically reflected back into the image.
To update the image, you need to call BufferedImage.setRGB after you change the int[]:
sprite.setRGB(0, 0, width, height, pixels, 0, width);
Another change you should probably make (and this involves a little guesswork as I don't have your bmp to test with) - the BufferedImage returned by ImageIO.read may have type BufferedImage.TYPE_INT_RGB - meaning that it doesn't have an alpha channel. You can verify by printing sprite.getType(), if that prints 1 it's TYPE_INT_RGB without an alpha channel.
To get an alpha channel, create a new BufferedImage of the right size and then set the converted int[] on that image, then use the new image from then on:
BufferedImage newSprite = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
newSprite.setRGB(0, 0, width, height, pixels, 0, width);
sprite = newSprite; // Swap the old sprite for the new one with an alpha channel
BMP images don't provide an alpha channel, you have to set it manually (as you do in your code)...
when you check your pixel to have a certain color you have to check without alpha (BMP has no alpha it's always 0x0).
if (pixels[i] == 0x00ff00fe) { //THIS is the color WITHOUT alpha
pixels[i] = 0xFFff00fe; //set alpha to 0xFF to make this pixel transparent
}
so in short: you did all right but mixed it up a bit ^^
This works:
private BufferedImage switchColors(BufferedImage img) {
int w = img.getWidth();
int h = img.getHeight();
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
// top left pixel is presumed to be BG color
int rgb = img.getRGB(0, 0);
for (int xx=0; xx<w; xx++) {
for (int yy=0; yy<h; yy++) {
int rgb2 = img.getRGB(xx, yy);
if (rgb2!=rgb) {
bi.setRGB(xx, yy, rgb2);
}
}
}
return bi;
}

Why am I getting a black image after replacing all the pixels of a BufferedImage object with magenta colored ones?

I am following a Java Game development tutorial, and I hit a roadblock because I am unable to figure out what is wrong with my code. I am supposed to render a new image by changing each pixel of the buffered image into the color I want. Then I copy that array over to the array in my Game class and I draw the rendered image. The relevant code is directly below. I discuss what I have tried directly after the code snippets.
This code snippet is from my Game class:
private Screen screen;
private BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Converts image into array of integers
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
public Game() {
Dimension size = new Dimension(width * scale, height * scale);
setPreferredSize(size);
screen = new Screen(width, height);
frame = new JFrame();
pixels = new int[width * height];
}
// Displays images to the screen
public void render() {
BufferStrategy bs = getBufferStrategy();
// Checks if the buffer strategy exists. If it doesn't create a triple buffer strategy
if(bs == null) {
// Triple buffering
createBufferStrategy(3);
return;
}
screen.render();
// Copies array in Screen class to pixels array in (this) class
for (int i = 0; i < pixels.length; i++) {
pixels[i] = screen.pixels[i];
}
// Must be in chronological order
Graphics g = bs.getDrawGraphics();
g.setColor(Color.CYAN);
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
g.dispose();
// Display next available buffer
bs.show();
}
This code snippet is from my Screen class:
private int width;
private int height;
public int[] pixels;
public Screen(int width, int height) {
this.width = width;
this.height = height;
// Size of the pixels array reserves one element for each pixel
pixels = new int[width * height];
}
public void render() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pixels[x + y * width] = 0xff00ff;
}
}
}
I have tried to "debug" by checking to see if both of my pixels arrays in the Game and the Screen class are being loaded with the magenta color. I simply did:
for (int i = 0; i < pixels.length; i++) {
pixels[i] = screen.pixels[i];
System.out.println(pixels[i]);
}
and I also tried:
for (int i = 0; i < pixels.length; i++) {
pixels[i] = screen.pixels[i];
System.out.println(screen.pixels[i]);
}
In both cases, the decimal representation of magenta (FF00FF in Hex) gets printed out until I close the window.
Another thing I tried is using the image.setRGBmethod to change the color of the image.
I added this line
image.setRGB(20, 20, 16711935); into the render method as follows:
// Displays images to the screen
public void render() {
BufferStrategy bs = getBufferStrategy();
// Checks if the buffer strategy exists. If it doesn't create a triple buffer strategy
if(bs == null) {
// Triple buffering
createBufferStrategy(3);
return;
}
screen.render();
// Copies array in Screen class to pixels array in Game (this) class
/* for (int i = 0; i < pixels.length; i++) {
pixels[i] = screen.pixels[i];
}
*/
// Must be in chronological order
Graphics g = bs.getDrawGraphics();
g.setColor(Color.CYAN);
g.fillRect(0, 0, getWidth(), getHeight());
image.setRGB(20, 20, 16711935);
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
g.dispose();
// Display next available buffer
bs.show();
}
A magenta dot showed up in my frame, and I am also able to turn the entire frame into a solid magenta color by modifying the render method in the Screen class. Now, I am wondering why my "original" code isn't working. From what I understand, the setRGB method is much slower than what I originally intended on using to render images, so I don't want to just give up and settle with using setRGB.
I have spent practically the entire day going over all the spelling in my code, making sure those nested for loops in the Screen class are correct, and etc. I am at a loss here. I don't understand what the issue is.
If the code snippets I included are not enough, I have created a Gist of my code in its entirety here.
This code gets the color int for the input rgb colors
public static int getIRGB(int Red, int Green, int Blue){
Red = (Red << 16) & 0x00FF0000; //Shift red 16-bits and mask out other stuff
Green = (Green << 8) & 0x0000FF00; //Shift Green 8-bits and mask out other stuff
Blue = Blue & 0x000000FF; //Mask out anything not blue.
return 0xFF000000 | Red | Green | Blue; //0xFF000000 for 100% Alpha. Bitwise OR everything together.
}
I suspect you are drawing a magenta color with 0% opacity

Read in Bufferedimage pixel values then manipulate each one and write to file

I am currently trying to read in an image pixel by pixel and change each colored pixel to the rgb value of (100,100,100). For whatever reason when I check the values of each pixel one the image is saved it has all the colored pixels as (46,46,46) instead.
Here is the original image
After running my program this is the image it gives to me
Here is the code
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Cmaps {
public static void main(String[] args){
File originalImage = new File("C:\\Users\\Me\\Desktop\\0005.bmp");
BufferedImage img = null;
try{
img = ImageIO.read(originalImage);
for(int i = 0; i < img.getHeight(); i++){
for(int j = 0; j < img.getWidth(); j++){
//get the rgb color of the image and store it
Color c = new Color(img.getRGB(i, j));
int r = c.getRed();
int g = c.getGreen();
int b = c.getBlue();
//if the pixel is white then leave it alone
if((r == 255) && (g == 255) && (b == 255)){
img.setRGB(i, j, c.getRGB());
continue;
}
//set the colored pixel to 100,100,100
r = 100;// red component 0...255
g = 100;// green component 0...255
b = 100;// blue component 0...255
int col = (r << 16) | (g << 8) | b;
img.setRGB(i, j, col);
}
}
File f = new File("C:\\Users\\Me\\Desktop\\2\\1.png");
try {
ImageIO.write(img, "PNG", f);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (IOException e){
e.printStackTrace();
}
}
}
I have no clue why it doesn't set the pixels to the expected rgb value. I eventually want to be able to basically increment the rgb color as I move down rows and columns in the x and y so what the final image will look like is it will start off dark in the top left corner and then have a fade out effect as you get from that side to the bottom right corner.
Okay, based on the comments:
If the BufferedImage has an IndexColorModel (palette based color model), using setRGB to set a pixel to an arbitrary RGB value will not work. Instead, the color will be looked up, and the pixel will get the color that is considered the closest match in the palette.
Formats like BMP, GIF and PNG may all use IndexColorModel when read using ImageIO.
To convert the image to "true color" (either DirectColorModel or ComponentColorModel in Java will do), you can use:
BufferedImage img; // your original palette image
int w = img.getWidth();
int h = img.getHeight();
BufferedImage trueColor = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = trueColor.createGraphics();
try {
g.drawImage(img, 0, 0, null);
}
finally {
g.dispose();
}
img = trueColor;
After this, getRGB(x, y) should return what you specify, using setRGB(x, y, argb).

java color correction on giant image on the fly

I have to handle VERY large (1-2GB) Tiff files, and only need to do some RGB manipulations on pixels, where I only make local corrections (color of a modified pixel is only depending on its old values, but not on e.g. neighbor pixels).
Is their (JAVA) a way to read the file as some kind of pixel stream, make adjustments on the RGB values, and write the stuff immediately to another file? I will not have enough memory to store the entire file in RAM (or at least I hope I could avoid it)
Thx for any hints...
THX
-Marco
Well, I don't actually know what a tiff file is 😅, but if it is a file, which you can store in a BufferedImage, it should be relatively easy.
I would do something like:
public BufferedImage correctRGB()
{
BufferedImage b = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//width and height are the width and height of the original image
Graphics g = b.getGraphics();
for(int x = 0; x < b.getHeight(); x++)
{
for(int y = 0; y < b.getWidth(); y++)
{
//loop through all the pixels in the image ONCE to spare RAM
int pixels = b.getRGB(x, y);
int alpha = (pixels >> 24) &0xff;
int red = (pixels >> 16) &0xff;
int green = (pixels >> 8) &0xff;
int blue = pixels &0xff;
//in here you play around with the values
g.setColor(new Color(red, green, blue, alpha));
g.fillRect(x, y, 1, 1);
}
}
g.dispose();
return b;
}
you can basically do everything you want with the argb values now.
For example, you could turn the whole image negative by doing red = 255 - red and so on.
or turn the whole image into grayscale by doing
int average = (red + green + blue) / 3;
g.setColor(new Color(average, average, average, alpha));

Categories

Resources