JAVA: copy more than just one new bufferedimage - java

I'm trying to copy in three new BufferedImage the same BufferedImage content, this is my code:
ColorModel cm = image.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = image.copyData(null);
BufferedImage[] images = {
new BufferedImage(cm, raster, isAlphaPremultiplied, null),
new BufferedImage(cm, raster, isAlphaPremultiplied, null),
new BufferedImage(cm, raster, isAlphaPremultiplied, null)
};
Even if I'm editing these images in distinct ways, the result is the same. I'm sure it's all ok because the code work properly if I have only one copy, but not more than one.
How can I manage something like this?

I got it. Even if I the BufferedImages are different, ColorModel and WritableRaster are only reference to the same objects.
If someone has my same problem try something like this:
private BufferedImage copyImage() {
ColorModel cm = image.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = image.copyData(null);
return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}
public BufferedImage[] copyUpdatedImage() {
BufferedImage[] images = {
copyImage(),
copyImage(),
copyImage()
};
}

Related

BufferedImage from 4-bit data buffer

Hello I have a problem with converting my 4-bit data buffer to WritableRaster.
Image resolution: 1024x768 (786432)
Here is description what I'm doing.
1) Create 4-bit BufferedImage
bit4Image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY, MY_BIT_4_COLOR_MODEL);
Graphics graphics = bit4Image.getGraphics();
graphics.drawImage(originalImage, 0, 0, null);
graphics.dispose();
//4-bit BufferedImage created. 4-bit BufferedImage is properly made cause it can be saved to hdd and looks good
2) Get byte array from DataBuffer from 4-bit
byte[] pixelData = ((DataBufferByte) bit4Image.getRaster().getDataBuffer()).getData();
// pixelData length is 393216
3) Now I want to create BufferedImage from this byte array pixelData
BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY, MY_BIT_4_COLOR_MODEL);
DataBufferByte buffer = new DataBufferByte(pixelData, pixelData.length);
WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, width, 1, new int[]{0}, new Point(0, 0));
dest.setData(raster);
Problem is when I call Raster.createInterleavedRaster. Exception:
java.awt.image.RasterFormatException: Data array too small (should be > 786431 )
I also tried something like this
BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY, MY_BIT_4_COLOR_MODEL);
dest.getRaster().setDataElements(0, 0, width, height, pixelData);
But this one gives me similar failure:
java.lang.ArrayIndexOutOfBoundsException: 393216
Could someone give me a hint or show the proper way of setting this 4-bit pixelData to WritableRaster?
Resolved.
I just had to create WritableRaster with giving it SampleModel:
SampleModel sm = MY_BIT_4_COLOR_MODEL.createCompatibleSampleModel(width, height);
WritableRaster wr = Raster.createWritableRaster(sm, buffer, new Point(0,0));
The initial code you had almost worked, the only problem is you tried to create an "interleaved" raster. For palette images (IndexColorModel) you typically have only one sample (the palette index) per pixel, so there's no samples to interleave.
Instead, your pixel data is 4 bit/pixel, stored as two pixels per byte. Storing more samples per storage unit, is often referred to as "packed". This means you need to create a "packed" raster, using one of the Raster.createPackedRaster methods.
Here's a full, runnable sample:
public static void main(String[] args) {
int width = 100;
int height = 100;
// Create initial 4 bit image
IndexColorModel icm = new IndexColorModel(4, 16, new int[16], 0, false, -1, DataBuffer.TYPE_BYTE);
BufferedImage bit4Image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY, icm);
// ...you probably do something in between here
// Get the pixel data
byte[] pixelData = ((DataBufferByte) bit4Image.getRaster().getDataBuffer()).getData();
// ...you probably do something in between here
// Create a data buffer around the array, wrap in raster
DataBufferByte buffer = new DataBufferByte(pixelData, pixelData.length);
WritableRaster raster = Raster.createPackedRaster(buffer, width, height, 4, null);
// Finally create a copy of the image, sharing pixel data
BufferedImage copy = new BufferedImage(icm, raster, icm.isAlphaPremultiplied(), null);
System.out.println("copy: " + copy);
}

getting a bufferedImage from a bytearray

I am trying to get back a bufferedImage from array of bytes, but I am getting an error saying bufferedimage is null. I actually tried several ways, everything ended up in the same way. Here goes my code:
1)
byte[] arr = Base64.decode(base64String);
BufferedImage bImageFromConvert =ImageIO.read(new ByteArrayInputStream(arr));
2)
InputStream in = new ByteArrayInputStream(arr);
BufferedImage bImageFromConvert = ImageIO.read(in);
I am pretty sure my byte array contains data and I think ImageIO.read() is where my code goes wrong.
The error is in your BufferedImage to Base64 encode method as you have posted in the comments.
You are never writing the BufferedImage to the ByteArrayOutputStream. Therefore the Base64 string is empty, and reading the empty string produces a null BufferedImage.
You should use this code to encode your image:
BufferedImage originalImage = ImageIO.read(new File("G:\\a.jpg"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write( originalImage, "jpg", baos );
String base64String=Base64.encode(baos.toByteArray());
To decode the image use this code:
byte[] arr = Base64.decode(base64String);
BufferedImage bImageFromConvert =ImageIO.read(new ByteArrayInputStream(arr));
System.out.println(bImageFromConvert.getWidth());
Try this code.Maybe it works. It worked for me.
byte[] aByteArray = {};
int width = ;
int height = ;
DataBuffer buffer = new DataBufferByte(aByteArray, aByteArray.length);
WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, 3 * width, 3, new int[] {0, 1, 2}, (Point)null);
ColorModel cm = new ComponentColorModel(ColorModel.getRGBdefault().getColorSpace(), false, true, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
BufferedImage image = new BufferedImage(cm, raster, true, null);
Just add the byte, width and height to code and customize it.

How to obtain a resized BufferedImage

Say I have 2 TIF images and I read one of them into a BufferedImage instance:
ImageReader reader = ImageIO.getImageReadersByFormatName("tif").next();
reader.setInput(inputStream, false); // inputStream is the first image.
BufferedImage bufferedImage = reader.read(0);
Now I want to create a new BufferedImage without reading the other image. It should be the same as the previous one, but only different in size. imageType seems to be 0 for TIF images, but the following doesn't work.
BufferedImage largeBufferedImage = new BufferedImage(newWidth, newHeight, 0);
Is there any way to clone the existing BufferedImage and only change its size?
BTW I want to be able to do it for any image format. I don't want to deal with details like imageType if possible.
BufferedImage deepCopy(BufferedImage bi)/*method to clone BufferedImage*/ {
ColorModel cm = bi.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = bi.copyData(null);
return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}
BufferedImage newImg = deepCopy(oldImg);//clone it
Graphics2D g = newImg.createGraphics();
g.drawImage(newImg, 0, 0, width, height, null);//newImg will be resized
When you draw in your paint method, you can add more parameters to stretch and scale image, see g.drawImage at this link.
After some trial and error, I found a working solution for my problem.
private BufferedImage copyAndResize(BufferedImage source, int width, int height)
{
ColorModel cm = source.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = source.copyData(null);
SampleModel sm = raster.getSampleModel().createCompatibleSampleModel(width, height);
WritableRaster newRaster = WritableRaster.createWritableRaster(sm, null);
BufferedImage newBi = new BufferedImage(cm, newRaster, isAlphaPremultiplied, null);
return newBi;
}

java try to convert planarimage to bufferedimage but colormodel is incompatible

I have a planarimage that I convert to black and white via some example code I found. I then need to convert it into a BufferedImage for the next code section. But I get the following exception:
java.lang.IllegalArgumentException: The specified ColorModel is incompatible with the image SampleModel.
at javax.media.jai.PlanarImage.setImageLayout(PlanarImage.java:535)
at javax.media.jai.RenderedOp.createRendering(RenderedOp.java:867)
at javax.media.jai.RenderedOp.getRendering(RenderedOp.java:888)
at javax.media.jai.RenderedOp.createInstance(RenderedOp.java:799)
at javax.media.jai.RenderedOp.createRendering(RenderedOp.java:867)
at javax.media.jai.RenderedOp.copyData(RenderedOp.java:2284)
Here is the code I found (Sun example, I think) that converts to black and white:
ParameterBlock pb = new ParameterBlock();
pb.addSource(input);
ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] {8},
false,
false,
Transparency.OPAQUE,
DataBuffer.TYPE_BYTE);
pb.add(cm);
PlanarImage src = JAI.create("ColorConvert", pb);
pb = new ParameterBlock();
pb.addSource(src);
String opName = null;
opName = "errordiffusion";
LookupTableJAI lut = new LookupTableJAI(new byte[] {(byte)0x00, (byte)0xff});
pb.add(lut);
pb.add(KernelJAI.ERROR_FILTER_FLOYD_STEINBERG);
// Create a layout containing an IndexColorModel which maps
// zero to zero and unity to 255.
ImageLayout layout = new ImageLayout();
byte[] map = new byte[] {(byte)0x00, (byte)0xff};
cm = new IndexColorModel(1, 2, map, map, map);
layout.setColorModel(cm);
// Create a hint containing the layout.
RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT,layout);
// Dither the image.
PlanarImage dst = JAI.create(opName, pb, hints);
Here is what I have tried to convert it to a BufferedImage, in the order I have tried:
//doesn't work
BufferedImage image = dst.getAsBufferedImage();
//thought of using the color model the b&w process did,also doesn't work
byte[] map = new byte[] {(byte)0x00, (byte)0xff};
ColorModel cm = new IndexColorModel(1, 2, map, map, map);
BufferedImage image = new BufferedImage(cm, dst.copyData(), false, null);
//I had the most hope for this one,but same error again
WritableRaster wr = dst.copyData();
ColorModel cm = PlanarImage.createColorModel(wr.getSampleModel());
BufferedImage image = new BufferedImage(cm, wr, false, null);
Can anyone tell me what I am doing wrong?
I have found that even calling dst.getNumBands() will throw this error. Clearly I don't know what I am doing here. More precisely, calling dst.betNumbBands(); will cause this:
java.lang.IllegalArgumentException: The specified ColorModel is incompatible with the image SampleModel.
at javax.media.jai.PlanarImage.setImageLayout(PlanarImage.java:535)
at javax.media.jai.RenderedOp.createRendering(RenderedOp.java:867)
at javax.media.jai.RenderedOp.getRendering(RenderedOp.java:888)
at javax.media.jai.RenderedOp.createInstance(RenderedOp.java:799)
at javax.media.jai.RenderedOp.createRendering(RenderedOp.java:867)
at javax.media.jai.RenderedOp.getSampleModel(RenderedOp.java:2233)
at javax.media.jai.PlanarImage.getNumBands(PlanarImage.java:678)
I think ur problem might be with the colorSpace parameter which dose not match with the source img's colorSpace parameter.
U can try this code which implements "bandCombine" opName.
public void imageToGrayScale()
{
double[][] matrix1 = {{ 1./3, 1./3, 1./3, 0 }};
ParameterBlock pb = new ParameterBlock();
pb.addSource(image);
pb.add(matrix1);
PlanarImage dst = (PlanarImage) JAI.create("bandCombine",pb,null);
BufferedImage img = dst.getAsBufferedImage();
display(img);
}
Here what happens is ,v r just taking the avg of the three color components (RGB) and putting it in a matrix then adding it to a parameterBlock,which gives u the grayscaleImage.
display() method just displays the image inside a JFrame.
matrix explanation:-
The matrix is in the form of :-
double[][] matrix = new double[destBands][sourceBands + 1];
sourceBands is important where it contains RGB values and the extra '+1' is for the constant values each of which is added to the respective band of the destination.
THIS METHORD WORKS ONLY FOR .JPG FILES.

Flipping vertically a raw image in Java

I need to flip in Java a raw image that has her rows inverted. By inverted I mean, the first row of the image is stored at the end of a file.
I managed to achive what I want by reordering the image rows using an auxiliar buffer. I included my code below.
I think this can be optimized by translating the coordinates, avoiding the memory copy. I tried to implement a DataBuffer that would invert the rows, but the raster I'm using requires a DataBufferByte (a final class).
Does anyone knows a more optimized way of doing what I want?
Thank you
...
int w = 640;
int h = 480;
byte[] flippedData = new byte[640*480*4];
int scanLineLength = w*4;
for(int i=0;i!=h; ++i) {
System.arraycopy(originalData, scanLineLength*i, flippedData, scanLineLength*(h-i)-scanLineLength, scanLineLength);
}
DataBuffer db = new DataBufferByte(flippedData,flippedData.length);
WritableRaster raster = Raster.createInterleavedRaster(db, w, h, scanLineLength, 4, new int[]{2,1,0}, new Point(0,0));
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorModel cm = new ComponentColorModel(cs, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
BufferedImage img = new BufferedImage(cm, raster, false, null);
ImageIO.write(img, "JPEG", new File("out.jpg"));
Use java.awt.AffineTransform:
Affine transformations can be constructed using sequences of translations, scales, flips, rotations, and shears.
See this and this to see how is flipping implemented using AffineTransform

Categories