Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert JavaFX Image object to byte array

We can create FX Image object by using

byte [] bytes = ------; //valid image in bytes
javafx.scene.image.Image image = new Image(new ByteArrayInputStream(bytes));

And this can be set to an ImageView.

I need the opposite without converting it first to the BufferedImage (SwingFXUtils.fromFXImage(image,null)).

So that I can directly write the bytes to a file.

I've tried the following:

PixelReader pixelReader = image.getPixelReader();
    int width = (int)image.getWidth();
    int height = (int)image.getHeight();
    byte[] buffer = new byte[width * height * 4];
    pixelReader.getPixels(
            0,
            0,
            width,
            height,
            PixelFormat.getByteBgraInstance(),
            buffer,
            0,
            width * 4
    );

But the file generated by writing the byte [] buffer is not a valid image.

Any insights on this?

EDIT: The solutions given at How to get byte[] from javafx imageView cannot be applied to my question. As I've already mentioned clearly that I do not want to use converting it to BufferedImage using SwingFXUtils. Moreover, I want to convert it to the byte array so that it can be written to an image file.

like image 466
Umar Tahir Avatar asked Jun 29 '16 09:06

Umar Tahir


2 Answers

This is way later, but if anybody is wanting to find out, try this:

// Load the Image into a Java FX Image Object //

Image img = new Image(new FileInputStream("SomeImageFile.png") );

// Cache Width and Height to 'int's (because getWidth/getHeight return Double) and getPixels needs 'int's //

int w = (int)img.getWidth();
int h = (int)img.getHeight();

// Create a new Byte Buffer, but we'll use BGRA (1 byte for each channel) //

byte[] buf = new byte[w * h * 4];

/* Since you can get the output in whatever format with a WritablePixelFormat,
   we'll use an already created one for ease-of-use. */

img.getPixelReader().getPixels(0, 0, w, h, PixelFormat.getByteBgraInstance(), buf, 0, w * 4);

/* Second last parameter is byte offset you want to start in your buffer,
   and the last parameter is stride (in bytes) per line for your buffer. */
like image 158
Raid Avatar answered Nov 17 '22 11:11

Raid


Using ByteArrayInputStream to create a JavaFX Image will only work using the original, unmodified image file bytes in the supported formats (bmp, jpg, gif, png).

After using PixelReader to read an Image, the byte array will contain raw image bytes that can only be written back to a WritableImage using the PixelWriter, which is why using ByteArrayInputStream produces an invalid image.

Below is a reproducible example using this image:

// Original image bytes
File file = new File("F:/Downloads/duke.png");
byte[] fileBytes = Files.readAllBytes(file.toPath());
System.out.println(fileBytes.length);  // 17776 bytes
InputStream in = new ByteArrayInputStream(fileBytes);
Image image = new Image(in);
ImageView view = new ImageView(image);
VBox pane = new VBox();
pane.getChildren().add(view);  // Works
in.close();

// PixelReader generated image bytes
int width = (int) image.getWidth();
int height = (int) image.getHeight();
byte[] pixelBytes = new byte[width * height * 4];
System.out.println(pixelBytes.length);  // 367928 bytes
image.getPixelReader().getPixels(0, 0, width, height,
        PixelFormat.getByteBgraInstance(),
        pixelBytes, 0, width * 4);
Image image2 = new Image(new ByteArrayInputStream(pixelBytes));
ImageView view2 = new ImageView(image2);
pane.getChildren().add(view2);  // Won't work

// PixelWriter generated image
WritableImage image3 = new WritableImage(width, height);
image3.getPixelWriter().setPixels(0, 0, width, height,
        PixelFormat.getByteBgraInstance(),
        pixelBytes, 0, width * 4);
ImageView view3 = new ImageView(image3);
pane.getChildren().add(view3);  // Works

Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();

As a raw image format, the PixelReader image is much larger than the original, so if you are storing these images in a database and are concerned about space then you should try storing the original file bytes instead. Otherwise, the SwingFXUtils class can still be used to convert back to compressed format.

Another issue when writing back PixelReader image bytes is that you must somehow store the width and height, either as separate fields in the database or prepended/appended as extra bytes onto the image byte array.

like image 1
Stephan Avatar answered Nov 17 '22 12:11

Stephan