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).
Related
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
I have a byte array with type TYPE_4BYTE_ABGR, and I know its width and height, I want to change it to BufferedImage, any ideas?
The fastest way to create a BufferedImage from a byte array in TYPE_4BYTE_ABGR form, is to wrap the array in a DataBufferByte and create an interleaved WritableRaster from that. This will make sure there are no additional byte array allocations. Then create the BufferedImage from the raster, and a matching color model:
public static void main(String[] args) {
int width = 300;
int height = 200;
int samplesPerPixel = 4; // This is the *4BYTE* in TYPE_4BYTE_ABGR
int[] bandOffsets = {3, 2, 1, 0}; // This is the order (ABGR) part in TYPE_4BYTE_ABGR
byte[] abgrPixelData = new byte[width * height * samplesPerPixel];
DataBuffer buffer = new DataBufferByte(abgrPixelData, abgrPixelData.length);
WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, samplesPerPixel * width, samplesPerPixel, bandOffsets, null);
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
System.out.println("image: " + image); // Should print: image: BufferedImage#<hash>: type = 6 ...
}
Note however, that this image will be "unmanaged" (some HW accelerations will be disabled), because you have direct access to the pixel array.
To avoid this, create the WritableRaster without the pixels, and copy the pixels into it. This will use twice as much memory, but will keep the image "managed" and thus possible better display performance:
// Skip creating the data buffer
WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height, samplesPerPixel * width, samplesPerPixel, bandOffsets, null);
raster.setDataElements(0, 0, width, height, abgrPixelData);
// ...rest of code as above.
You could even do this (which might be more familiar):
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
WritableRaster raster = image.getRaster();
raster.setDataElements(0, 0, width, height, abgrPixelData);
Might not be very efficient, but a BufferedImage can be converted to another type this way:
public static BufferedImage convertToType(BufferedImage image, int type) {
BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight(), type);
Graphics2D graphics = newImage.createGraphics();
graphics.drawImage(image, 0, 0, null);
graphics.dispose();
return newImage;
}
About the method you want to be implemented, you would have to know the width or height of the image to convert a byte[] to a BufferedImage.
Edit:
One way is converting the byte[] to int[] (data type TYPE_INT_ARGB) and using setRGB:
int[] dst = new int[width * height];
for (int i = 0, j = 0; i < dst.length; i++) {
int a = src[j++] & 0xff;
int b = src[j++] & 0xff;
int g = src[j++] & 0xff;
int r = src[j++] & 0xff;
dst[i] = (a << 24) | (r << 16) | (g << 8) | b;
}
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
image.setRGB(0, 0, width, height, dst, 0, width);
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.
How can I convert a BufferedImage to a Mat in OpenCV?
I'm using the JAVA wrapper for OpenCV(not JavaCV). As I am new to OpenCV I have some problems understanding how Mat works.
I want to do something like this. (Based on Ted W. reply):
BufferedImage image = ImageIO.read(b.getClass().getResource("Lena.png"));
int rows = image.getWidth();
int cols = image.getHeight();
int type = CvType.CV_16UC1;
Mat newMat = new Mat(rows, cols, type);
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
newMat.put(r, c, image.getRGB(r, c));
}
}
Highgui.imwrite("Lena_copy.png", newMat);
This doesn't work. Lena_copy.png is just a black picture with the correct dimensions.
I also was trying to do the same thing, because of need to combining image processed with two libraries. And what I’ve tried to do is to put byte[] in to Mat instead of RGB value. And it worked! So what I did was:
1.Converted BufferedImage to byte array with:
byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
2. Then you can simply put it to Mat if you set type to CV_8UC3
image_final.put(0, 0, pixels);
Edit:
Also you can try to do the inverse as on this answer
Don't want to deal with big pixel array? Simply use this
BufferedImage to Mat
public static Mat BufferedImage2Mat(BufferedImage image) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", byteArrayOutputStream);
byteArrayOutputStream.flush();
return Imgcodecs.imdecode(new MatOfByte(byteArrayOutputStream.toByteArray()), Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
}
Mat to BufferedImage
public static BufferedImage Mat2BufferedImage(Mat matrix)throws IOException {
MatOfByte mob=new MatOfByte();
Imgcodecs.imencode(".jpg", matrix, mob);
return ImageIO.read(new ByteArrayInputStream(mob.toArray()));
}
Note, Though it's very negligible. However, in this way, you can get a reliable solution but it uses encoding + decoding. So you lose some performance. It's generally 10 to 20 milliseconds. JPG encoding loses some image quality also it's slow (may take 10 to 20ms). BMP is lossless and fast (1 or 2 ms) but requires little more memory (negligible). PNG is lossless but a little more time to encode than BMP. Using BMP should fit the most cases I think.
This one worked fine for me, and it takes from 0 to 1 ms to be performed.
public static Mat bufferedImageToMat(BufferedImage bi) {
Mat mat = new Mat(bi.getHeight(), bi.getWidth(), CvType.CV_8UC3);
byte[] data = ((DataBufferByte) bi.getRaster().getDataBuffer()).getData();
mat.put(0, 0, data);
return mat;
}
I use following code in my program.
protected Mat img2Mat(BufferedImage in) {
Mat out;
byte[] data;
int r, g, b;
if (in.getType() == BufferedImage.TYPE_INT_RGB) {
out = new Mat(in.getHeight(), in.getWidth(), CvType.CV_8UC3);
data = new byte[in.getWidth() * in.getHeight() * (int) out.elemSize()];
int[] dataBuff = in.getRGB(0, 0, in.getWidth(), in.getHeight(), null, 0, in.getWidth());
for (int i = 0; i < dataBuff.length; i++) {
data[i * 3] = (byte) ((dataBuff[i] >> 0) & 0xFF);
data[i * 3 + 1] = (byte) ((dataBuff[i] >> 8) & 0xFF);
data[i * 3 + 2] = (byte) ((dataBuff[i] >> 16) & 0xFF);
}
} else {
out = new Mat(in.getHeight(), in.getWidth(), CvType.CV_8UC1);
data = new byte[in.getWidth() * in.getHeight() * (int) out.elemSize()];
int[] dataBuff = in.getRGB(0, 0, in.getWidth(), in.getHeight(), null, 0, in.getWidth());
for (int i = 0; i < dataBuff.length; i++) {
r = (byte) ((dataBuff[i] >> 0) & 0xFF);
g = (byte) ((dataBuff[i] >> 8) & 0xFF);
b = (byte) ((dataBuff[i] >> 16) & 0xFF);
data[i] = (byte) ((0.21 * r) + (0.71 * g) + (0.07 * b));
}
}
out.put(0, 0, data);
return out;
}
Reference: here
I found a solution here.
The solution is similar to Andriys.
Camera c;
c.Connect();
c.StartCapture();
Image f2Img, cf2Img;
c.RetrieveBuffer(&f2Img);
f2Img.Convert( FlyCapture2::PIXEL_FORMAT_BGR, &cf2Img );
unsigned int rowBytes = (double)cf2Img.GetReceivedDataSize()/(double)cf2Img.GetRows();
cv::Mat opencvImg = cv::Mat( cf2Img.GetRows(), cf2Img.GetCols(), CV_8UC3, cf2Img.GetData(),rowBytes );
To convert from BufferedImage to Mat I use the method below:
public static Mat img2Mat(BufferedImage image) {
image = convertTo3ByteBGRType(image);
byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3);
mat.put(0, 0, data);
return mat;
}
Before converting into Mat, I change the type of bufferedImage to TYPE_3BYTE_BGR, because to some types BufferedImages the method ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); may return int[] and that would break the code.
Below is the method for converting to TYPE_3BYTE_BGR.
private static BufferedImage convertTo3ByteBGRType(BufferedImage image) {
BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(),
BufferedImage.TYPE_3BYTE_BGR);
convertedImage.getGraphics().drawImage(image, 0, 0, null);
return convertedImage;
}
When you use as JavaCP wrapper bytedeco library (version 1.5.3) then you can use Java2DFrameUtils.
Simple usage is:
import org.bytedeco.javacv.Java2DFrameUtils;
...
BufferedImage img = ImageIO.read(new File("some/image.jpg");
Mat mat = Java2DFrameUtils.toMat(img);
Note: don't mix different wrappers, bytedeco Mat is different than opencv Mat.
One simple way would be to create a new using
Mat newMat = Mat(rows, cols, type);
then get the pixel values from your BufferedImage and put into newMat using
newMat.put(row, col, pixel);
You can do it in OpenCV as follows:
File f4 = new File("aa.png");
Mat mat = Highgui.imread(f4.getAbsolutePath());
I'm porting a HTML5's Canvas sample to Java, so far so good, until i get on this function call :
Canvas.getContext('2d').getImageData(0, 0, 100, 100).data
I googled for a while and found this page of the canvas specification
http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#pixel-manipulation
After reading it, I created this function below :
public int[] getImageDataPort(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
int[] ret = new int[width * height * 4];
int idx = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int color = image.getRGB(x, y);
ret[idx++] = getRed(color);
ret[idx++] = getGreen(color);
ret[idx++] = getBlue(color);
ret[idx++] = getAlpha(color);
}
}
return ret;
}
public int getRed(int color) {
return (color >> 16) & 0xFF;
}
public int getGreen(int color) {
return (color >> 8) & 0xFF;
}
public int getBlue(int color) {
return (color >> 0) & 0xFF;
}
public int getAlpha(int color) {
return (color >> 24) & 0xff;
}
There is any class on Java Graphics API that has this function built-in or i should use the one that i had created?
I think the closest thing you'll find in the standard Java API is the Raster class. You can get hold of a WritableRaster (used for low-level image manipulation) through BufferedImage.getRaster. The Raster class then provides methods such as getSamples which fills an int[] with image data.
Thanks aioobe, i've looked at the WritableRaster class and found the getPixels function which does exactly what i needed, the final result is :
public int[] getImageDataPort(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
int[] ret = null;
ret = image.getRaster().getPixels(0, 0, width, height, ret);
return ret;
}
The only problem that may happen is when the image.getType isn't a type that supports alpha in comparison with the code of the question, resulting in a smaller int[] ret, but one can simply convert the image type with :
public BufferedImage convertType(BufferedImage image,int type){
BufferedImage ret = new BufferedImage(image.getWidth(), image.getHeight(), type);
ColorConvertOp xformOp = new ColorConvertOp(null);
xformOp.filter(image, ret);
return ret;
}
Try
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(bi, "jpg", baos);
where bi - BufferendImage