Constructing Image From 2D Array in Java - java

I want to create the Image from 2D Array.I used BufferImage concept to construct Image.but there is difference betwwen original Image and constructed Image is displayed by image below
I am using the following code
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
/**
*
* #author pratibha
*/
public class ConstructImage{
int[][] PixelArray;
public ConstructImage(){
try{
BufferedImage bufferimage=ImageIO.read(new File("D:/q.jpg"));
int height=bufferimage.getHeight();
int width=bufferimage.getWidth();
PixelArray=new int[width][height];
for(int i=0;i<width;i++){
for(int j=0;j<height;j++){
PixelArray[i][j]=bufferimage.getRGB(i, j);
}
}
///////create Image from this PixelArray
BufferedImage bufferImage2=new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
for(int y=0;y<height;y++){
for(int x=0;x<width;x++){
int Pixel=PixelArray[x][y]<<16 | PixelArray[x][y] << 8 | PixelArray[x][y];
bufferImage2.setRGB(x, y,Pixel);
}
}
File outputfile = new File("D:\\saved.jpg");
ImageIO.write(bufferImage2, "jpg", outputfile);
}
catch(Exception ee){
ee.printStackTrace();
}
}
public static void main(String args[]){
ConstructImage c=new ConstructImage();
}
}

You get a ARGB value from getRGB and the setRGB takes a ARGB value, so doing this is enough:
bufferImage2.setRGB(x, y, PixelArray[x][y]);
From the API of BufferedImage.getRGB:
Returns an integer pixel in the default RGB color model (TYPE_INT_ARGB) and default sRGB colorspace.
...and from the API of BufferedImage.setRGB:
Sets a pixel in this BufferedImage to the specified RGB value. The pixel is assumed to be in the default RGB color model, TYPE_INT_ARGB, and default sRGB color space.
On the other hand I would recommend you do paint the image instead:
Graphics g = bufferImage2.getGraphics();
g.drawImage(g, 0, 0, null);
g.dispose();
Then you wouldn't need to worry about any color model etc.

Related

Quickly Render Bytes to Canvas with a Color Palette

While working on a Java application which requires rendering sprites, I thought that, instead of loading a .png or .jpg file as an Image or BufferedImage, I could load up a byte[] array containing indices for a color palette(16 colors per palette, so two pixels per byte), then render that.
The method I currently have generates a BufferedImage from the byte[] array and color palette while initializing, taking extra time to initialize but running smoothly after that, which works fine, but there are only 4 sprites in the program so far. I'm worried that when there are 100+ sprites, storing all of them as BufferedImages will be too taxing on the memory. And not only would that mean 1 BufferedImage per sprite, but actually 1 image for each sprite/palette combination I'd want to use.
This function creates the BufferedImage:
protected BufferedImage genImage(ColorPalette cp, int width, int height){ //Function to generate BufferedImage to render from the byte[]
BufferedImage ret = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); //Create the Image to return
for(int j=0; j<height; j++){ //Run a for loop for each pixel
for(int i=0; i<width; i++){
int index = (j * width + i)/2; //Get the index of the needed byte
int value = image[index] & 0x00ff; //Convert to "unsigned byte", or int
byte thing; //declare actual color index as byte
if(i % 2 == 0)thing = (byte)((value & 0b11110000) >>> 4); //If it's an even index(since it starts with 0, this includes the 1st one), get the first 4 bits of the value
else thing = (byte)(value & 0b00001111); //If it's odd, get the last four bits
ret.setRGB(i, j, cp.getColor(thing & 0x00ff).getRGB()); //Set the pixel in the image to the value in the Color Palette
}
}
return ret;
}
And this one actually renders it to the screen:
public void render(Graphics g, int x, int y){ //Graphics to render to and x/y coords
g.drawImage(texture, x, y, TILE_WIDTH, TILE_HEIGHT, null); //Render it
}
I've experimented with another method that renders from the byte[] directly w/o the need for a BufferedImage, which should theoretically succeed in saving memory by avoiding use of a BufferedImage for each sprite, but it ended up being very, very slow. It took several seconds to render each frame w/ at most 25 sprites to render on the screen! Note that g is a Graphics object.
private void drawSquare(int x, int y, int scale, Color c){ //Draw each "pixel" to scale
if(g == null){ //If null, quit
return;
}
g.setColor(c); //Set the color
for(int i=x; i<x+scale; i++){ //Loop through each pixel
if(i<0)continue;
for(int j=y; j<y+scale; j++){
if(j<0)continue;
g.fillRect(x, y, scale, scale); //Fill the rect to make the "pixel"
}
}
}
public void drawBytes(byte[] image, int x, int y, int width, int height, int scale, ColorPalette palette){ //Draw a byte[] image with given byte[], x/y coords, width/height, scale, and color palette
if(image.length < width * height / 2){ //If the image is too small, exit
return;
}
for(int j=0; j<height; j++){ //Loop through each pixel
for(int i=0; i<width; i++){
int index = (j * width + i)/2; //Get index
int value = image[index]; //get the byte
byte thing; //get the high or low value depending on even/odd
if(i % 2 == 0)thing = (byte)((value & 0b11110000) >>> 4);
else thing = (byte)(value & 0b00001111);
drawSquare((int)(x + scale * i), (int)(y + scale * j), scale, palette.getColor(thing)); //draw the pixel
}
}
}
So is there a more efficient way to render these byte[] arrays w/o the need for BufferedImage's? Or will it really not be problematic to have several hundred BufferdImage's loaded into memory?
EDIT: I've also tried doing the no-BufferedImage methods, but with g as the one large BufferedImage to which everything is rendered, and is then rendered to the Canvas. The primary difference is that g.fillRect(... is changed to g.setRGB(... in that method, but it was similarly slow.
EDIT: The images I'm dealing with are 16x16 and 32x32 pixels.
If memory usage is your main concern, I'd use BufferedImages with IndexColorModel (TYPE_BYTE_BINARY). This would perfectly reflect your byte[] image and ColorPalette, and waste very little memory. They will also be reasonably fast to draw.
This approach will use about 1/8th of the memory used by the initial use of TYPE_INT_RGB BufferedImages, because we retain the 4 bits per pixel, instead of 32 bits (an int is 32 bits) per pixel (plus some overhead for the palette, of course).
public static void main(String[] args) {
byte[] palette = new byte[16 * 3]; // 16 color palette without alpha
byte[] pixels = new byte[(16 * 16 * 4) / 8]; // 16 * 16 * 4 bit
Random random = new Random(); // For test purposes, just fill arrays with random data
random.nextBytes(palette);
random.nextBytes(pixels);
// Create ColorModel & Raster from palette and pixels
IndexColorModel cm = new IndexColorModel(4, 16, palette, 0, false, -1); // -1 for no transparency
DataBufferByte buffer = new DataBufferByte(pixels, pixels.length);
WritableRaster raster = Raster.createPackedRaster(buffer, 16, 16, 4, null);
// Create BufferedImage from CM and Raster
final BufferedImage image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
System.out.println("image: " + image); // "image: BufferedImage#...: type = 12 ..."
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(new JLabel(new ImageIcon(image)));
frame.pack();
frame.setVisible(true);
}
});
}
The above code will create fully opaque (Transparency.OPAQUE) images, that will occupy the entire 16 x 16 pixel block.
If you want bitmask (Transparency.BITMASK) transparency, where all pixels are either fully opaque or fulle transparent, just change the last parameter in the IndexColorModel to the palette index you want to be fully transparent.
int transparentIndex = ...;
IndexColorModel cm = new IndexColorModel(4, 16, palette, 0, false, transparentIndex);
// ...everything else as above
This will allow your sprites to have any shape you want.
If you want translucent pixels (Transparency.TRANSLUCENT), where pixels can be semi-transparent, you can also have that. You will then have to change the palette array to 16 * 4 entries, and include a sample for the alpha value as the 4th sample for each entry (quadruple). Then invoke the IndexColorModel constructor with the last parameter set to true (hasAlpha):
byte[] palette = new byte[16 * 4]; // 16 color palette with alpha (translucency)
// ...
IndexColorModel cm = new IndexColorModel(4, 16, palette, 0, true); // true for palette with alpha samples
// ...everything else as above
This will allow smoother gradients between the transparent and non-transparent parts of the sprites. But with only 16 colors in the palette, you won't have many entries available for transparency.
Note that it is possible to re-use the Rasters and IndexColorModels here, in all of the above examples, to save further memory for images using the same palette, or even images using the same image data with different palettes. There's one caveat though, that is the images sharing rasters will be "live views" of each other, so if you make any changes to one, you will change them all. But if your images are never changed, you could exploit this fact.
That said, the above really is a compromise between saving memory and having "reasonable" performance. If performance (ie. frames per second) is more important, just ignore the memory usage, and create BufferedImages that are compatible with your graphics card's/the OS's native pixel layout. You can do this, by using component.createCompatibleImage(...) (where component is a JComponent subclass) or gfxConfig.createCompatibleImage(...) (where gfxConfig is a GraphicsConfiguration obtained from the local GraphicsEnvironment).

How do I test a pixel of a bufferedimage for a certain color in Java

I'm trying to take a screenshot and then look through it for a pixel that has a certain color. Firstly, I tried to just print the color of an image at a certain xy coordinate but I could not even do that. What am I doing wrong?
static int ScreenWidth;
static int ScreenHeight;
static Robot robot;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic
callibrateScreenSize();
findSquares();
//takeScreenShot();
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void callibrateScreenSize() {
try {
Rectangle captureSize = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
ScreenWidth = captureSize.width;
ScreenHeight = captureSize.height;
System.out.println("Width is " + ScreenWidth);
System.out.println("Height is " + ScreenHeight);
} catch (Exception e) {
e.printStackTrace();
}
//return null;
}
public static BufferedImage takeScreenShot() {
Rectangle captureSize = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage image = robot.createScreenCapture(captureSize);
return image;
}
public static void findSquares() {
System.out.println(takeScreenShot().getRGB(5,5));
}
Thanks!
You can use BufferedImage#getRGB or byte[] pixels = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData() to get the pixel data. getRBG is more convenient, but is typically slower than getting the pixel array
getRGB packs the pixel data into an int, getData will return the RGB(A) in each entry of the array (R = n; G = n+1; B=n+2(, A=n+3)), so will need to process this yourself
You can use java.awt.Color, which allows you to access the RGB values of the color, pack it as a int value or convert an int to a Color
Color color = new Color(bufferedImage.getRGB(0, 0), true);
int redColor = Color.RED.getRGB();
The answer to this question provides an example of dealing with the byte[] pixel data
Basically, you will need to loop over the data, comparing the values in the image to the value you are after, either directly (comparing the red, green and blue values) or indirectly, comparing the packed int or Color values.
Personally, I'd grab the pixel data, convert each element to an int and compare it with a previously packed int from a Color object, this creates the less number of short lived objects and should be reasonably efficient
You can take a look at this answer which use getRGB to get the red, green, blue values from a given pixel
Here's something I wrote a while ago using the Robot class. It returns an array of the screen wherever the screen is white, it was not very computationally expensive for my application, but I found probing the values individually using robot was. At first I didn't even read your question, but looking back, I think this will help you A LOT. Good luck. And then I saw the original post date...
public boolean[][] raster() throws AWTException, IOException{
boolean[][] filled= new boolean[720][480];
BufferedImage image = new Robot().createScreenCapture(new Rectangle(0,0,720,480));
//accepts (xCoord,yCoord, width, height) of screen
for (int n =0; n<720; n++){
for (int m=0; m<480; m++){
if(new Color(image.getRGB(n, m)).getRed()<254){
//can check any rgb value, I just chose red in this case to check for white pixels
filled[n][m]=true;
}
}
}
return filled;
}

Is it possible to modify colors of an image in java Swing?

I am animating a simulation, and the images I'm using for my entities are small PNG files that are in black and white (if that makes any difference). I would like to know if there is any way to change the color of the image (e.g. change the black to red, or overlay a red filter over the black) once it's been imported as a BufferedImage? Or is my best bet to rather create separate color versions of the icon outside of Java and importing them as separate images?
you can use the
java.awt.image.RGBImageFilter
class
I personally haven`t played with that, I went another way, which is getting the icon into an array of pixels(ints) and manipulating this on my own.
With that , you can create an instance of ImageSourceInt (as I call it) passing an ImageIcon, fiddling around with it
(canvasData is the pixels-array in ARGB-int format - 1byte per channel each = 4 bytes = 1 int)
and retrieve a BufferedImage (a view, not a copy) back for use with Swing.
For the latter, call the getReferenceImage() method.
By the way, if you dont know about scanSize (common term in imaging) the just consider it as the width of the image.
(Sorry for the bad code formatting, I`m new here)
public class ImageSourceInt implements ImageSource {
int[] canvasData;
int width;
int height;
int scanSize;
int lineCount;
/**
* #param source make sure it is loaded or this ImageSource will be empty.<br>
* sizeIncrementWidth and sizeIncrementHeight are set to 1
*/
public ImageSourceInt(ImageIcon source){
if (source == null) {
this.canvasData = new int[0];
return;
}
this.width = source.getIconWidth();
this.height = source.getIconHeight();
this.scanSize = source.getIconWidth();
this.lineCount = source.getIconHeight();
this.canvasData = new int[this.width*this.height];
// PixelGrabber(Image img, int x, int y, int w, int h, int[] pix, int
// off, int scansize)
PixelGrabber grabber = new PixelGrabber(source.getImage(), 0,
0, this.width, this.height, this.canvasData, 0, this.scanSize);
try {
grabber.grabPixels();
} catch (InterruptedException e) {
e.printStackTrace();// must not be...
}
}
/**
* #return a BufferedImage with the data of this ImageSource (referenced)<br>
* IMPORTANT: if the size changed, the BufferedImage will get invalid, causing strange effects or exceptions
*/
public BufferedImage getReferenceImage(){
DataBuffer buf = new DataBufferInt(this.canvasData, this.canvasData.length);
WritableRaster wRaster = Raster.createPackedRaster(buf, this.getWidth(), this.getHeight(), this.getScanSize(),
new int[]{0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000}, new Point());
BufferedImage bi = new BufferedImage(ColorModel.getRGBdefault(), wRaster, false, null);
return bi;
}
}

Why ImageIO.write() method modifies pixel values?

I am trying to run a simple Java program that tries to do the following : Extract pixel data from a given image. Then use this data to create a new image of the same type. The problem is that when I read the pixel data of this created image, the pixel values differ from the ones I have written into it. This happens not only or .jpg images but also for some .png images(so it's not even restricted to image type).
Here is my code:
package com.alex;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Test {
public static void main(String[] args) {
try{
// Read source image
BufferedImage img = ImageIO.read(new File("D:/field.png"));
int width = img.getWidth();
int height = img.getHeight();
int[] imagePixels = new int[width*height];
img.getRGB(0, 0, width, height, imagePixels, 0, width);
// Create copy image
BufferedImage destImg = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
destImg.setRGB(0, 0, img.getWidth(), img.getHeight(), imagePixels, 0, img.getWidth());
File out = new File("D:/test.png");
ImageIO.write(destImg, "png", out);
// Extract copy image pixels
BufferedImage copy = ImageIO.read(new File("D:/test.png"));
int width1 = copy.getWidth();
int height1 = copy.getHeight();
int[] extractedPixels = new int[width1*height1];
copy.getRGB(0, 0, width1, height1, extractedPixels, 0, width1);
System.out.println("The 2 dimensions are " + imagePixels.length + " " + extractedPixels.length );
// Compare the piels from the 2 images
int k=0;
for(int i=0; i<imagePixels.length; i++) {
if(imagePixels[i] != extractedPixels[i]) {
k++;
}
}
System.out.println("Number of different pixels was: " + k);
}catch(IOException e) {
System.out.println("Exception was thrown during reading of image: " + e.getMessage());
}
}
}
Unfortunately quite often and impredictable the 2 images pixel data differ. Could someone please help me find a method so that, at least for an image type, the values don't get modiied ?
Edit Here is an image that fails in the above process
Make sure you are using the correct color model for reading and writing.
According to the BufferedImage.getRGB() documentation,
Returns an array of integer pixels in the default RGB color model (TYPE_INT_ARGB) and default sRGB color space, from a portion of the image data. Color conversion takes place if the default model does not match the image ColorModel. There are only 8-bits of precision for each color component in the returned data when using this method. With a specified coordinate (x, y) in the image, the ARGB pixel can be accessed in this way:
pixel = rgbArray[offset + (y-startY)*scansize + (x-startX)];
[Edit]
You need to use the constructor BufferedImage(width, height, type, ColorModel), as indicated in the Javadoc for your image type (TYPE_BYTE_BINARY):
When this type is used as the imageType argument to the BufferedImage constructor that takes an imageType argument but no ColorModel argument, a 1-bit image is created with an IndexColorModel with two colors in the default sRGB ColorSpace: {0, 0, 0} and {255, 255, 255}.
Images with 2 or 4 bits per pixel may be constructed via the BufferedImage constructor that takes a ColorModel argument by supplying a ColorModel with an appropriate map size.
(emphasis mine)
Try this
First print same index in two array
If the result is not same means your problem in your color mode, But if same means your comparision is not work.

Making every pixel of an image having a specific color transparent

This question is a duplicate of this question:
Making every pixel of an image having a specific color transparent
But I need a Java equivalent. And I need a image-type (like PNG, BMP, ...) which can hold this color with full transparency (alpha = 0). And of course a way to save it as a file.
import java.awt.*;
import java.awt.image.*;
public class Transparency {
public static Image makeColorTransparent
(Image im, final Color color) {
ImageFilter filter = new RGBImageFilter() {
// the color we are looking for... Alpha bits are set to opaque
public int markerRGB = color.getRGB() | 0xFF000000;
public final int filterRGB(int x, int y, int rgb) {
if ( ( rgb | 0xFF000000 ) == markerRGB ) {
// Mark the alpha bits as zero - transparent
return 0x00FFFFFF & rgb;
}
else {
// nothing to do
return rgb;
}
}
};
ImageProducer ip = new FilteredImageSource(im.getSource(), filter);
return Toolkit.getDefaultToolkit().createImage(ip);
}
}
Modified the code to make each pixel transparent
Source :http://www.rgagnon.com/javadetails/java-0265.html
Use ImageIO.read for reading a file and ImageIO.write for writing. Use the getRGB and setRGB methods of BufferedImage to change the colours.
You can use a LookupOp with a four-component LookupTable that sets the alpha component to zero for colors that match the background. Examples may be found in Using the Java 2D LookupOp Filter Class to Process Images and Image processing with Java 2D.

Categories