Converting RGB byte array to BufferedImage - java

This is the code how I convert BufferedImage to byte array:
public void parseBufferedImage(BufferedImage image) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
Color color = new Color(image.getRGB(x, y), true);
outputStream.write(color.getRed());
outputStream.write(color.getGreen());
outputStream.write(color.getBlue());
outputStream.write(color.getAlpha());
}
}
image.flush();
byte[] data = outputStream.toByteArray();
}
I need to convert byte[] data back to an image, but I have only byte[] array so I do not know image size.
I want to save byte[] as a png, so I would be open to suggestions of another method.

Related

Java - Image BMP header

I have a little problem here if someone could help me I will be really glad.
I'm trying to make the next operations
Read an BMP image
Convert the image into a byte[]
Rotate the image with 90 degree(the byte array)
And write a new image in some folder
My problem is... In the moment when I'm trying to write the new image I have some problem with BMP header and I don't know why. Please give me some advice if anyone know the answear.
Convert the image into byte[]
private static byte[] convertAnImageToPixelsArray(File file) throws FileNotFoundException {
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
try {
for (int readNum; (readNum = fis.read(buf)) != -1; ) {
bos.write(buf, 0, readNum);
}
} catch (IOException ex) {
Logger.getLogger(ConvertImage.class.getName()).log(Level.SEVERE, null, ex);
}
return bos.toByteArray();
}
Rotate
private static byte[] rotate(double angle, byte[] pixels, int width, int height) {
final double radians = Math.toRadians(angle), cos = Math.cos(radians), sin = Math.sin(radians);
final byte[] pixels2 = new byte[pixels.length];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
final int
centerx = width / 2,
centery = height / 2,
m = x - centerx,
n = y - centery,
j = ((int) (m * cos + n * sin)) + centerx,
k = ((int) (n * cos - m * sin)) + centery;
if (j >= 0 && j < width && k >= 0 && k < height)
pixels2[(y * width + x)] = pixels[(k * width + j)];
}
}
arraycopy(pixels2, 0, pixels, 0, pixels.length);
return pixels2;
}
Convert the byte[] into image
private static void convertArrayPixelsIntoImage(byte[] bytes) throws IOException {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
Iterator<?> readers = ImageIO.getImageReadersByFormatName("bmp");
ImageReader reader = (ImageReader) readers.next();
Object source = bis;
ImageInputStream iis = ImageIO.createImageInputStream(source);
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
Image image = reader.read(0, param);
BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = bufferedImage.createGraphics();
g2.drawImage(image, null, null);
File imageFile = new File("Images/Output.bmp");
ImageIO.write(bufferedImage, "bmp", imageFile);
}
Main:
public static void main(String[] args) throws IOException {
File file = new File("Images/Input-1.bmp");
Image img = ImageIO.read(file);
convertArrayPixelsIntoImage(rotate(90,convertAnImageToPixelsArray(file),img.getWidth(null),img.getHeight(null)));
}
Here it's the error message:
Exception in thread "main" javax.imageio.IIOException: Unable to read the image header.
Any suggestions?
The problem is that you're not taking into account the structure of the BMP file when you are rotating the image.
You're just reading a byte[] from the file, as you could from any file - it's just a stream of bytes.
But in your rotate method, you're assuming that the pixel values are:
1 byte per pixel;
Starting at the start of the array.
This isn't the case. Aside from the fact that each pixel will almost certainly be encoded by multiple bytes, the BMP file format starts with a header, and other metadata.
Whilst you obviously could work out how to decode the data correctly, I would strongly discourage it. You're already reading the image using ImageIO (Image img = ImageIO.read(file);), so you've got no need to reinvent the wheel: just use Java's existing image manipulation functionality.
You missed that the BMP image has a header like any other format. When you try to rotate the image, you are changing the bytes sequance, so the header loses in other place instead, of beginning. Try to extract first 54 bytes to another array, rotate others and then write to file header at first and ather bytes at second

Converting image to byte array, then to int array, then to byte array for image output

I'm trying to implement a DCT on an image, and so far I have only been able to successfully read an image, grayscale it, turn it into a byte array and then output the byte array as an image. This works fine. However, in order to work on the image I need to convert the byte array to an int array and then back again, and this is where the problem comes in.
This first part reads the image and converts the image to grayscale.
BufferedImage image = ImageIO.read(new File("C:\\Users\\A00226084\\Desktop\\image.jpg"));
int width = image.getWidth();
int height = image.getHeight();
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
Color c = new Color(image.getRGB(j, i));
int red = (int)(c.getRed() * 0.299);
int green = (int)(c.getGreen() * 0.587);
int blue = (int)(c.getBlue() *0.114);
Color newColor = new Color(red+green+blue,
red+green+blue,red+green+blue);
image.setRGB(j,i,newColor.getRGB());
}
}
This part converts the image to a byte array.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", baos);
byte[] pixels = baos.toByteArray();
This part then converts the byte array to an int array. I have manually done the wrapping because nothing else worked.
for(int i = 0; i < pixels.length; i ++){
if(pixels[i] < 0){
byteConverted[i] = 127 + (128 - (pixels[i] * -1));
}else{
byteConverted[i] = pixels[i];
}
}
This part converts the int array back to a byte array, somewhere between the byte > int and int > byte conversions that everything goes wrong. I have output both the before and after byte array to a file and they are identical, so I don't know why I get 'image == null' instead of the image.
for(int i = 0; i < pixels.length; i ++){
if(byteConverted[i] > 127){
pixels[i] = (byte) ( -128 + (byteConverted[i] - 127));
}else{
pixels[i] = (byte) byteConverted[i];
}
}
write2("final byte array.txt", pixels);
ByteArrayInputStream bais = new ByteArrayInputStream(pixels);
try {
BufferedImage img = ImageIO.read(bais);
System.out.println("Image Out");
System.out.println(pixels.length);
ImageIO.write(img, "jpg", new File("C:\\Users\\A00226084\\Desktop\\newImage.jpg"));
} catch (IOException e) {
e.printStackTrace();
}

Is there a way to control which pixels go black (a threshold) in a TIFF-PNG conversion with bit depth=1 (black&white)

Well, in the proccess of converting an image from TIFF to PNG format, I really want the final image to be the smallest possible so I am setting the bit depth to 1 (black&white), I am working with images of signatures. My problem is that I am "losing" some pixels because in the original image were "too white" and the process of conversion is ignoring them.
What I want is a way to tell the PNG encoder to apply a threshold in the image, for example if the gray level is greater than 240 then it is white, if not is black. Or maybe any other way in wich the image don't lose much of the pixels, because the image is a signature and I have to show it in a browser. Here is a sample:
This is the code I use to convert a image from TIFF to PNG format:
public byte[]tiffToPng(byte[]tiffBytes) throws IOException {
SeekableStream stream = new ByteArraySeekableStream(tiffBytes);
ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", stream, null);
RenderedImage renderedImage = decoder.decodeAsRenderedImage(0);
PNGEncodeParam pngEncodeParam = PNGEncodeParam.getDefaultEncodeParam(renderedImage);
pngEncodeParam.setBitDepth(1);
ByteArrayOutputStream pngBytesStream = new ByteArrayOutputStream();
ImageEncoder encoder = ImageCodec.createImageEncoder("png", pngBytesStream, pngEncodeParam);
encoder.encode(renderedImage);
pngBytesStream.flush();
return pngBytesStream.toByteArray();
}
Well, after some time, I first made the conversion of the RenderedImage to a BufferedImage, then a loop over the RGB values and the thresholding is done. Finally, I encoded the resulting image into a PNG (bit depth=1). This is it:
public byte[]tiffToPng(byte[]tiffBytes) throws IOException {
SeekableStream stream = new ByteArraySeekableStream(tiffBytes);
ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", stream, null);
RenderedImage tiffRenderedImage = decoder.decodeAsRenderedImage(0);
BufferedImage tiffBufferedImage = toBufferedImage(tiffRenderedImage);
for (int y = 0; y < tiffBufferedImage.getHeight(); y++) {
for (int x = 0; x < tiffBufferedImage.getWidth(); x++) {
int rgb = tiffBufferedImage.getRGB(x, y);
int a = (rgb>>24)&0xFF;
int r = (rgb>>16)&0xFF;
int g = (rgb>>8)&0xFF;
int b = (rgb>>0)&0xFF;
if(a == 0xFF && r == g && g == b) {
if(r < 254) {
tiffBufferedImage.setRGB(x, y, 0xFF000000);
} else {
tiffBufferedImage.setRGB(x, y, 0xFFFFFFFF);
}
}
}
}
PNGEncodeParam pngEncodeParam = PNGEncodeParam.getDefaultEncodeParam(tiffBufferedImage);
pngEncodeParam.setBitDepth(1);
ByteArrayOutputStream pngBytesStream = new ByteArrayOutputStream();
ImageEncoder encoder = ImageCodec.createImageEncoder("png", pngBytesStream, pngEncodeParam);
encoder.encode(tiffBufferedImage);
pngBytesStream.flush();
return pngBytesStream.toByteArray();
}
public BufferedImage toBufferedImage(RenderedImage img) {
if (img instanceof BufferedImage) {
return (BufferedImage) img;
}
ColorModel cm = img.getColorModel();
int width = img.getWidth();
int height = img.getHeight();
WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
Hashtable properties = new Hashtable();
String[] keys = img.getPropertyNames();
if (keys != null) {
for (int i = 0; i < keys.length; i++) {
properties.put(keys[i], img.getProperty(keys[i]));
}
}
BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties);
img.copyData(raster);
return result;
}
And this is the final image:
What you want to do is more a image preprocessing thing, I wouldn't expect an encoder to allow for that. Just preprocess the image (brightness/contrast and/or gamma) and save.

Java BMP Header files

I try to make a BMP file from BufferedImage. Here is function with that I try to write the header and pixels in bmp file.
I have a mistake but I can't find that. I need your help.
static void writeToBMP(BufferedImage img, String name)
{
//File header
int bfType = 0x424d;
int bfSize = (img.getHeight()*img.getWidth()*3)+54; // File size
short bfReserved1 = 0; // Reserved
short bfReserved2 = 0;
int bfOfBytes = 54; // Header size
//Header info
int biSize = 40; // Header 2 size
int biWidth = img.getWidth(); // Width in pixels
int biHeight = img.getHeight(); // Height in pixels
short biPlanes = 1; // Nr of planes
short biBitCount = 24; // Nr bites per pixel
int biCompression = 0;
int biSizeImage = (img.getHeight()*img.getWidth()*3); // Image size
int biXPelsPerMeter = 0;
int biYPelsPerMeter = 0;
int biClrUsed = 0;
int biClrImportant = 0;
File file = new File(name);
try {
OutputStream stream = new FileOutputStream(file);
fOut = new DataOutputStream(stream);
fOut.writeShort(bfType);
fOut.writeInt(bfSize);
fOut.writeShort(bfReserved1);
fOut.writeShort(bfReserved2);
fOut.writeInt(bfOfBytes);
//Write Header Info
fOut.writeInt(biSize);
fOut.writeInt(biWidth);
fOut.writeInt(biHeight);
fOut.writeShort(biPlanes);
fOut.writeShort(biBitCount);
fOut.writeInt(biCompression);
fOut.writeInt(biSizeImage);
fOut.writeInt(biXPelsPerMeter);
fOut.writeInt(biYPelsPerMeter);
fOut.writeInt(biClrUsed);
fOut.writeInt(biClrImportant);
for(int x=0; x<img.getWidth(); x++)
{
for(int y=0; y<img.getHeight(); y++)
{
Color c = new Color(img.getRGB(x,y));
fOut.writeByte(c.getRed());
fOut.writeByte(c.getBlue());
fOut.writeByte(c.getGreen());
}
}
fOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
I tried to write only header, and header size = 54 bytes.
I don't know if I calculate correct the bfSize and biSizeImage.
Try using ImageIO
File outImage = new File(name);
ImageIO.write(img, "bmp", outImage);
To resolve my problem I need to use LITTLE_ENDIAN byte order. For this I use:
ByteBuffer buffer = ByteBuffer.allocate(54);
buffer.putInt(bfSize);
buffer.putShort(bfReserved1);
buffer.putShort(bfReserved2);
buffer.putInt(bfOfBytes);
buffer.putInt(biSize);
buffer.putInt(biWidth);
buffer.putInt(biHeight);
buffer.putShort(biPlanes);
buffer.putShort(biBitCount);
buffer.putInt(biSizeImage);
buffer.putInt(biXPelsPerMeter);
buffer.putInt(biYPelsPerMeter);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.flip();

How to make bmp image from pixel byte array in java

I have a byte array containing pixel values from a .bmp file. It was generated by doing this:
BufferedImage readImage = ImageIO.read(new File(fileName));
byte imageData[] = ((DataBufferByte)readImage.getData().getDataBuffer()).getData();
Now I need to recreate the .bmp image. I tried to make a BufferedImage and set the pixels of the WritableRaster by calling the setPixels method. But there I have to provide an int[], float[] or double[] array. Maybe I need to convert the byte array into one of these. But I don't know how to do that. I also tried the setDataElements method. But I am not sure how to use this method either.
Can anyone explain how to create a bmp image from a byte array?
Edit: #Perception
This is what I have done so far:
private byte[] getPixelArrayToBmpByteArray(byte[] pixelData, int width, int height, int depth) throws Exception{
int[] pixels = byteToInt(pixelData);
BufferedImage image = null;
if(depth == 8) {
image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
}
else if(depth == 24){
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
}
WritableRaster raster = (WritableRaster) image.getData();
raster.setPixels(0, 0, width, height, pixels);
image.setData(raster);
return getBufferedImageToBmpByteArray(image);
}
private byte[] getBufferedImageToBmpByteArray(BufferedImage image) {
byte[] imageData = null;
try {
ByteArrayOutputStream bas = new ByteArrayOutputStream();
ImageIO.write(image, "bmp", bas);
imageData = bas.toByteArray();
bas.close();
} catch (Exception e) {
e.printStackTrace();
}
return imageData;
}
private int[] byteToInt(byte[] data) {
int[] ints = new int[data.length];
for (int i = 0; i 
You need to pack three bytes into each integer you make. Depending on the format of the buffered image, this will be 0xRRGGBB.
byteToInt will have to consume three bytes like this:
private int[] byteToInt(byte[] data) {
int[] ints = new int[data.length / 3];
int byteIdx = 0;
for (int pixel = 0; pixel < ints.length) {
int rByte = (int) pixels[byteIdx++] & 0xFF;
int gByte = (int) pixels[byteIdx++] & 0xFF;
int bByte = (int) pixels[byteIdx++] & 0xFF;
int rgb = (rByte << 16) | (gByte << 8) | bByte
ints[pixel] = rgb;
}
}
You can also use ByteBuffer.wrap(arr, offset, length).toInt()
Having just a byte array is not enough. You also need to construct a header (if you are reading from a raw format, such as inside a DICOM file).

Categories