Convert int array into bufferedImage in java - java

I was wondering how i could construct an int array in java into a buffered image. I know you can get an int array in java by doing
int[] srcpixels = ((DataBufferInt)in.getRaster().getDataBuffer()).getData();
but i dont know how to do it the other way. I need this to apply a fisheye effect to a buffered image which i found out how to do here http://popscan.blogspot.com/2012/04/fisheye-lens-equation-simple-fisheye.html but it only works with int arrays. Please help, thanks.

Use a WritableRaster:
final int w = bitmap.getWidth();
final int h = bitmap.getHeight();
final WritableRaster wr = bitmap.getData();
int []data = wr.getPixels(0, 0, w, h, data);
// do processing here
wr.setPixels(0, 0, w, h, data);

Related

Convert 8bit Grayscale image byte array to a BufferedImage

I have a byte array containing data of the raw grayscale 8bit image, which I need to convert to a BufferedImage. I've tried doing:
BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
However, the resulting image object is null which means I'm doing something wrong here.
What's the correct way of making such a conversion?
There are two good ways to do this, depending on your use case.
Either create a new, gray image, and copy the data into it. This will keep the image "managed", which may lead to better rendering performance (ie. on screen). But it will need twice as much memory, and copy the data from your input to the image.
The other, is to create the gray image directly "around" your existing pixel data. This will be faster, and use almost no extra heap, as it avoids copying the pixel data. But the image will not be managed (as the backing array is exposed and mutable).
Both options are demonstrated below:
int w = 640;
int h = 480;
byte[] imageBytes = new byte[w * h];
// 1 Keeps the image "managed" at the expense of twice the memory + a large array copy
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
image.getRaster().setDataElements(0, 0, w, h, imageBytes);
System.out.println("image: " + image);
// 2 Faster, and uses less memory, but will make the image "unmanaged"
ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
WritableRaster raster = Raster.createInterleavedRaster(new DataBufferByte(imageBytes, imageBytes.length), w, h, w, 1, new int[]{0}, null);
BufferedImage image2 = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
System.out.println("image2: " + image2);
If the image data isn't in linear gray color space, one could use an IndexColorModel to map the input into whatever range you want:
// Alternate, using IndexColorModel, if your input isn't in linear gray color space
int[] cmap = new int[256]; // TODO: Add ARGB packed colors here...
IndexColorModel icm = new IndexColorModel(8, 256, cmap, 0, false, -1, DataBuffer.TYPE_BYTE);
// As 1
BufferedImage image3 = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_INDEXED, icm);
image3.getRaster().setDataElements(0, 0, w, h, imageBytes);
System.out.println("image3: " + image3);
// As 2
BufferedImage image4 = new BufferedImage(icm, raster, cm.isAlphaPremultiplied(), null);
System.out.println("image4: " + image4);
I've managed to did the conversion for the 640x480 resolution the following way:
BufferedImage image = new BufferedImage(640,480,BufferedImage.TYPE_BYTE_INDEXED);
int i = 0;
for(int y = 0; y < 480; y++)
{
for(int x = 0; x < 640; x++)
{
int g = imageBytes[i++] & 0xFF;
image.setRGB(x,y,new Color(g,g,g).getRGB());
}
}
EDIT: removed useless code (thanks to Marco13)
Java
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
image.getRaster().setDataElements(0, 0, width, height, array));
Kotlin
val image = BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY)
image.raster.setDataElements(0, 0, width, height, byteArray )

Pixel data of a 16-bit DICOM image to BufferedImage

I've got a byte array storing 16-bit pixel data from an already-deconstructed DICOM file. What I need to do now is convert/export that pixel data somehow into a TIFF file format. I'm using the imageio-tiff-3.3.2.jar plugin to handle the tiff conversion/header data. But now I need to pack that image data array into a BufferedImage of the original image dimensions so it can be exported to TIFF. But it seems that BufferedImage doesn't support 16-bit images. Is there a way around this problem, such as an external library? Is there another way I can pack that image data into a TIFF image of the original DICOM dimensions? Keep in mind, this process has to be completely lossless. I've looked around and tried out some things for the last few days, but so far nothing has worked for me.
Let me know if you have any questions or if there's anything I can do to clear up any confusion.
EDIT: Intended and Current image
Given your input data of a raw byte array, containing unsigned 16 bit image data, here's two ways to create a BufferedImage.
The first one will be slower, as it involves copying the byte array into a short array. It will also need twice the amount of memory. The upside is that it creates a standard TYPE_USHORT_GRAY BufferedImage, which may be faster to display and may be more compatible.
private static BufferedImage createCopyUsingByteBuffer(int w, int h, byte[] rawBytes) {
short[] rawShorts = new short[rawBytes.length / 2];
ByteBuffer.wrap(rawBytes)
// .order(ByteOrder.LITTLE_ENDIAN) // Depending on the data's endianness
.asShortBuffer()
.get(rawShorts);
DataBuffer dataBuffer = new DataBufferUShort(rawShorts, rawShorts.length);
int stride = 1;
WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, w, h, w * stride, stride, new int[] {0}, null);
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
}
A variant that is much faster (previous version takes 4-5x more time) to create, but results in a TYPE_CUSTOM image, that might be slower to display (it does seem to perform reasonable though, in my tests). It's much faster, and uses very little extra memory, as it does no copying/conversion of the input data at creation time.
Instead, it uses a custom sample model, that has DataBuffer.TYPE_USHORT as transfer type, but uses DataBufferByte as data buffer.
private static BufferedImage createNoCopy(int w, int h, byte[] rawBytes) {
DataBuffer dataBuffer = new DataBufferByte(rawBytes, rawBytes.length);
int stride = 2;
SampleModel sampleModel = new MyComponentSampleModel(w, h, stride);
WritableRaster raster = Raster.createWritableRaster(sampleModel, dataBuffer, null);
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
}
private static class MyComponentSampleModel extends ComponentSampleModel {
public MyComponentSampleModel(int w, int h, int stride) {
super(DataBuffer.TYPE_USHORT, w, h, stride, w * stride, new int[] {0});
}
#Override
public Object getDataElements(int x, int y, Object obj, DataBuffer data) {
if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
throw new ArrayIndexOutOfBoundsException("Coordinate out of bounds!");
}
// Simplified, as we only support TYPE_USHORT
int numDataElems = getNumDataElements();
int pixelOffset = y * scanlineStride + x * pixelStride;
short[] sdata;
if (obj == null) {
sdata = new short[numDataElems];
}
else {
sdata = (short[]) obj;
}
for (int i = 0; i < numDataElems; i++) {
sdata[i] = (short) (data.getElem(0, pixelOffset) << 8 | data.getElem(0, pixelOffset + 1));
// If little endian, swap the element order, like this:
// sdata[i] = (short) (data.getElem(0, pixelOffset + 1) << 8 | data.getElem(0, pixelOffset));
}
return sdata;
}
}
If your image looks strange after this conversion, try flipping the endianness, as commented in the code.
And finally, some code to exercise the above:
public static void main(String[] args) {
int w = 1760;
int h = 2140;
byte[] rawBytes = new byte[w * h * 2]; // This will be your input array, 7532800 bytes
ShortBuffer buffer = ByteBuffer.wrap(rawBytes)
// .order(ByteOrder.LITTLE_ENDIAN) // Try swapping the byte order to see sharp edges
.asShortBuffer();
// Let's make a simple gradient, from black UL to white BR
int max = 65535; // Unsigned short max value
for (int y = 0; y < h; y++) {
double v = max * y / (double) h;
for (int x = 0; x < w; x++) {
buffer.put((short) Math.round((v + max * x / (double) w) / 2.0));
}
}
final BufferedImage image = createNoCopy(w, h, rawBytes);
// final BufferedImage image = createCopyUsingByteBuffer(w, h, rawBytes);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(new JScrollPane(new JLabel(new ImageIcon(image))));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
Here's what the output should look like (scaled down to 1/10th):
The easiest thing to do is to create a BufferedImage of type TYPE_USHORT_GRAY, which is type to use for 16 bits encoding.
public BufferedImage Convert(short[] array, final int width, final int height)
{
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY) ;
short[] sb = ((DataBufferUShort) image.getRaster().getDataBuffer()).getData() ;
System.arraycopy(array, 0, sb, 0, array.length) ;
return image ;
}
Then you can use Java.imageio to save your image as a TIFF or a PNG. I think that the Twelve Monkey Project allows a better TIFF support for imageio, but you have to check first.
[EDIT] In your case because you deal with huge DICOM images that cannot be stored into a regular BufferedImage, you have to create your own type using the Unsafe class to allocated the DataBuffer.
Create a new class DataBufferLongShort that will allocate the needed array/DataBuffer using the Unsafe class. Then you can use Long indexes instead of Integer
Create a new class DataBuffer that extends the classical DataBuffer in order to add a type TYPE_LONG_USHORT
Then you can create the ColorModel with the new DataBuffer.

How to generate and bind a texture in OpenGL

I have inputted an image from file and stored it in a int array
private int[] dataBuffInt;
BufferedImage image;
image = ImageIO.read(getClass().getResource("/res/" + type + ".png"));
int w = image.getWidth();
int h = image.getHeight();
this.dataBuffInt = image.getRGB(0, 0, w, h, null, 0, w);
I now want to take that array and pass it over to generate and bind my texture, however I am stuck as GLuint does not work for me.
GL11.glGenTextures(tex);
for (int i = 0; i < tex.length; i++) {
GL11.glBindTexture(GL_TEXTURE_2D, tex[i]); }
This is where I am at I assume the for loop is completely wrong, I feel I need to get the colour from the int[] using Color c = new Color(dataBuffInt[100]) with c.getRed() etc but I am lost.
I hope someone understands my intentions, I am simply trying to read an image from a file and place it on screen.
There are no exceptions for reading the file in and the values are being successfully printed out for RGBA. I assume the Gen/Bind process has gone wrong.

JavaFx Image to byte[] array (Closed)

I need a fast way to convert a JavaFX Image to an byte array.
The way with "BufferedImage bImage = SwingFXUtils.fromFXImage(i, null);" is to slow.
I thinks its better to not convert the Image first to the awt.BufferedImage.
So what I have so far is:
PixelReader pr = img.getPixelReader();
WritablePixelFormat<ByteBuffer> pixelformat = WritablePixelFormat.getByteBgraInstance();
int w = (int) img.getWidth();
int h = (int) img.getHeight();
int offset = 0;
int scanlineStride = w * 4;
byte[] buffer = new byte[w * h * 4];
pr.getPixels(0, 0, w, h, pixelformat, buffer, offset, scanlineStride);
But this is not working as excepted.
Seems like the byte[] is empty or so?
you can use toByteArray() method of IOUtils class from org.apache.commons package. see here

Java / OpenGL: Getting the image of a Canvas as a BufferedImage

I've got some code that initializes OpenGL to render to a java.awt.Canvas.
The problem is, I can't figure out how I can get the buffer of the canvas and turn it into a BufferedImage.
I've tried overriding getGraphics(), cloning the Raster, and replacing the CanvasPeer with a custom one.
I'm guessing OpenGL doesn't use java graphics in any way then, so how can I get OpenGL's buffer and convert it into a BufferedImage?
I am using LWJGL's code for setting parent:
Display.setParent(display_parent);
Display.create();
You need to copy data from OpenGL buffer. I was using this method:
FloatBuffer grabScreen(GL gl)
{
int w = SCREENWITDH;
int h = SCREENHEIGHT;
FloatBuffer bufor = FloatBuffer.allocate(w*h*4); // 4 = rgba
gl.glReadBuffer(GL.GL_FRONT);
gl.glReadPixels(0, 0, w, h, GL.GL_RGBA, GL.GL_FLOAT, bufor); //Copy the image to the array imageData
return bufor;
}
You need to use something similar according to your OpenGL wrapper. This is JOGL example.
And here for LWJGL wrapper:
private static synchronized byte[] grabScreen()
{
int w = screenWidth;
int h = screenHeight;
ByteBuffer bufor = BufferUtils.createByteBuffer(w * h * 3);
GL11.glReadPixels(0, 0, w, h, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, bufor); //Copy the image to the array imageData
byte[] byteimg = new byte[w * h * 3];
bufor.get(byteimg, 0, byteimg.length);
return byteimg;
}
EDIT
This may be useful also (it's not fully mine, should be tuned too):
BufferedImage toImage(byte[] data, int w, int h)
{
if (data.length == 0)
return null;
DataBuffer buffer = new DataBufferByte(data, w * h);
int pixelStride = 3; //assuming r, g, b, skip, r, g, b, skip...
int scanlineStride = 3 * w; //no extra padding
int[] bandOffsets = { 0, 1, 2 }; //r, g, b
WritableRaster raster = Raster.createInterleavedRaster(buffer, w, h, scanlineStride, pixelStride, bandOffsets,
null);
ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
boolean hasAlpha = false;
boolean isAlphaPremultiplied = true;
int transparency = Transparency.TRANSLUCENT;
int transferType = DataBuffer.TYPE_BYTE;
ColorModel colorModel = new ComponentColorModel(colorSpace, hasAlpha, isAlphaPremultiplied, transparency,
transferType);
BufferedImage image = new BufferedImage(colorModel, raster, isAlphaPremultiplied, null);
AffineTransform flip;
AffineTransformOp op;
flip = AffineTransform.getScaleInstance(1, -1);
flip.translate(0, -image.getHeight());
op = new AffineTransformOp(flip, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
image = op.filter(image, null);
return image;
}
I don't think this is possible for your situation, and here's why:
LWJGL doesn't draw directly to the canvas (at least not in Windows). The canvas is only used to obtain a window handle to provide as the parent window to OpenGL. As such, the canvas is never directly drawn to. To capture the contents, you'll probably have to resort to a screen capture.

Categories

Resources