How would I go about writing a javafx.scene.image.Image image to a file. I know you can use ImageIO on BufferedImages but is there any way to do it with a javafx Image?
Just convert it to a BufferedImage first, using javafx.embed.swing.SwingFXUtils:
Image image = ... ; // javafx.scene.image.Image
String format = ... ;
File file = ... ;
ImageIO.write(SwingFXUtils.fromFXImage(image, null), format, file);
Almost 3 years later and I now have the knowledge to do and answer this. Yes the original answer was also valid but it involved first converting the image to a BufferedImage and I ideally wanted to avoid swing entirely. While this does output the raw RGBA version of the image that's good enough for what I needed to do. I actually could just use raw BGRA since I was writing the software to open the result but since gimp can't open that I figure I'd convert it to RGBA.
Image img = new Image("file:test.png");
int width = (int) img.getWidth();
int height = (int) img.getHeight();
PixelReader reader = img.getPixelReader();
byte[] buffer = new byte[width * height * 4];
WritablePixelFormat<ByteBuffer> format = PixelFormat.getByteBgraInstance();
reader.getPixels(0, 0, width, height, format, buffer, 0, width * 4);
try {
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("test.data"));
for(int count = 0; count < buffer.length; count += 4) {
out.write(buffer[count + 2]);
out.write(buffer[count + 1]);
out.write(buffer[count]);
out.write(buffer[count + 3]);
}
out.flush();
out.close();
} catch(IOException e) {
e.printStackTrace();
}
JavaFX has no built-in method to do this.
To solve this problem, I implemented a very small (< 20KiB) library for writing PNG files: https://github.com/Glavo/SimplePNG
Usage:
Image img = new Image("path-to-image.jpg");
try (PNGWriter writer = new PNGWriter(Files.newOutputStream(Path.of("output.png")))) {
writer.write(PNGJavaFXUtils.asArgbImage(img));
}
// Or you can use the shortcut:
// PNGJavaFXUtils.writeImage(img, Path.of("output.png"));
It has no dependencies and can work on the JRE that only have java.base.
I avoid the dependence on Java AWT (java.desktop) through it.
Related
How do i get byte[] from javafx image/imageview class? I want to store my image as a Blob into my database.This is the method that i use for it
public PreparedStatement prepareQuery(HSQLDBConnector connector) {
try {
Blob logoBlob = connector.connection.createBlob();
logoBlob.setBytes(0,logo.getImage());//stuck here
for (int i = 0, a = 1; i < data.length; i++, a++) {
connector.prepStatCreateProfile.setString(a, data[i]);
}
//store LOB
connector.prepStatCreateProfile.setBlob(11, logoBlob);
} catch (SQLException ex) {
ex.printStackTrace();
}
return connector.prepStatCreateProfile;
}
Is there a way to convert from my current object (imageview),image) into byte[]?, or shoud i start to think about using other class for my image/ alternatively point to the location with reference and work with paths/urls?
try this one:
BufferedImage bImage = SwingFXUtils.fromFXImage(logo.getImage(), null);
ByteArrayOutputStream s = new ByteArrayOutputStream();
ImageIO.write(bImage, "png", s);
byte[] res = s.toByteArray();
s.close(); //especially if you are using a different output stream.
should work depending on the logo class
you need to specify a format while writing and reading, and as far as I remember bmp is not supported so you will end up with a png byte array on the database
pure java fx solution trace ( == you will have to fill in missing points :)
Image i = logo.getImage();
PixelReader pr = i.getPixelReader();
PixelFormat f = pr.getPixelFormat();
WriteablePixelFromat wf = f.getIntArgbInstance(); //???
int[] buffer = new int[size as desumed from the format f, should be i.width*i.height*4];
pr.getPixels(int 0, int 0, int i.width, i.height, wf, buffer, 0, 0);
Lorenzo's answer is correct, this answer just examines efficiency and portability aspects.
Depending on the image type and storage requirements, it may be efficient to convert the image to a compressed format for storage, for example:
ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
ImageIO.write(SwingFXUtils.fromFXImage(fxImage, null), "png", byteOutput);
Blob logoBlob = connector.connection.createBlob();
logoBlob.setBytes(0, byteOutput.toByteArray());
Another advantage of doing a conversion to a common format like png before persisting the image is that other programs which deal with the database would be able to read the image without trying to convert it from a JavaFX specific byte array storage format.
I have an Android application and I want to show up a PDF into it (without external applications). The only solution that I thought is to convert the pages of the PDF to images. Someone has experience with that issue? which library do you recommend me?
I tested the next libraries, but I have had troubles:
1) PdfRenderer library does not work with modern PDF
2) jPDFImages not work in Android (works in java desktop application)
Sorry for my English
I'm expanding on the accpeted answer and providing a complete solution.
Using this library: android-pdfview and the following code, you can reliably convert the PDF pages into images (JPG, PNG):
DecodeServiceBase decodeService = new DecodeServiceBase(new PdfContext());
decodeService.setContentResolver(mContext.getContentResolver());
// a bit long running
decodeService.open(Uri.fromFile(pdf));
int pageCount = decodeService.getPageCount();
for (int i = 0; i < pageCount; i++) {
PdfPage page = decodeService.getPage(i);
RectF rectF = new RectF(0, 0, 1, 1);
// do a fit center to 1920x1080
double scaleBy = Math.min(AndroidUtils.PHOTO_WIDTH_PIXELS / (double) page.getWidth(), //
AndroidUtils.PHOTO_HEIGHT_PIXELS / (double) page.getHeight());
int with = (int) (page.getWidth() * scaleBy);
int height = (int) (page.getHeight() * scaleBy);
// you can change these values as you to zoom in/out
// and even distort (scale without maintaining the aspect ratio)
// the resulting images
// Long running
Bitmap bitmap = page.renderBitmap(with, height, rectF);
try {
File outputFile = new File(mOutputDir, System.currentTimeMillis() + FileUtils.DOT_JPEG);
FileOutputStream outputStream = new FileOutputStream(outputFile);
// a bit long running
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.close();
} catch (IOException e) {
LogWrapper.fatalError(e);
}
}
You should do this work in the background i.e. by using an AsyncTask or something similar as quite a few methods take computation or IO time (I have marked them in comments).
You can use MuPDF. Here is a link that describes how to build MuPDF for android: http://mupdf.com/docs/how-to-build-mupdf-for-android
I recommend use this library, android-pdfview
I tried to convert raw data ByteArray to JPEG format using JPEGEncoder but its too slow in mobile (I've tested it on mobile). How can I do the same thing in java? I will send raw data byte to java and encode it to JPEG with java - I tried some of them as JpegImageEncoder under com.sun.* but it's depreciated in jdk7. How can I do this in java Or any suggestions from Flex mobile developers who have done such thing?
UPDATE: I tried the following code but I'm getting a strange result:
public void rawToJpeg(byte[] rawBytes, int width, int height, File outputFile){
try{
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
int count = 0;
for(int h=0;h<height;h++){
for(int w=0;w<width;w++){
bi.setRGB(w, h, rawBytes[count++]);
}
}
Graphics2D ig2 = bi.createGraphics();
Iterator imageWriters = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter imageWriter = (ImageWriter) imageWriters.next();
ImageOutputStream ios = ImageIO.createImageOutputStream(outputFile);
imageWriter.setOutput(ios);
imageWriter.write(bi);
}catch(Exception ex){
ex.printStackTrace();
}
}
RESULT:
P.S It should be my photo btw :)
Why not use a ByteArrayInputStream with ImageIO?
You find more Information about ImageIO in the API.
public static void rawToJpeg(byte[] bytes, File outputFile) {
try {
BufferedImage img = ImageIO.read(new ByteArrayInputStream(bytes));
ImageIO.write(img, "jpg", outputFile);
} catch (IOException e) {
// Handle exception
}
}
bi.setRGB takes a 4 byte "int" value, which is the ARGB 0xAARRGGBB
You then increment your byte offset counter by ONE, so the next pixel will get 0xRRGGBBAA, then 0xGGBBAARR and so forth.
Assuming the byte[] you are passing is in the correct 4 byte format, you need to either be adding 4 to "count" each time, or change what you pass to an int[] (which would actually be more correct, since it really does contain int values).
Hi i was facing same problem, i was setting the width and height values as hardcoded lets say (300,300) causing similar output. then i referenced this link.
Raw byte[] to jpeg image you can ignore the bitmap part in it. I am assuming you are also hardcoding the width and height values.
You could try to replace your for-loops by this
for(int w = 0; w < width; w++)
{
for(int h = 0; h < height; h++)
{
//alpha should be eiter 0 or 255
//if you use the wrong value your image will be transparent
int alpha = 0 << 8*3;
int red = rawBytes[count*3 + 0] << 8*2;
int green = rawBytes[count*3 + 1] << 8*1;
int blue = rawBytes[count*3 + 2] << 8*0;
int color = alpha + red + green + blue;
//color is an int with the format of TYPE_INT_ARGB (0xAARRGGBB)
bi.setRGB(w, h, color);
count += 3;
}
}
Things that may went wrong with your code:
You usually write line by line not row by row
You need to read 3 bytes and build an int instead of writing the bytes directly in your Pixel (TYPE_INT_ARGB)
This link explains TYPE_INT_ARGB: Format of TYPE_INT_RGB and TYPE_INT_ARGB
I hope this helps a bit and isn't too confusing =)
When I try to compress the a jpg image, most of the time it work perfectly, however some jpg image turn green after the compression. Here is my code
public void compressImage(String filename, String fileExtension) {
BufferedImage img = null;
try {
File file = new File(filename);
img = ImageIO.read(file);
if (fileExtension.toLowerCase().equals(".png") || fileExtension.toLowerCase().equals(".gif")) {
//Since there might be transparent pixel, if I dont do this,
//the image will be all black.
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
int rgb = img.getRGB(x, y);
int alpha = (rgb >> 24) & 0xff;
if (alpha != 255) {
img.setRGB(x, y, -1); //set white
}
}
}
}
Iterator iter = ImageIO.getImageWritersByFormatName("jpg");
//Then, choose the first image writer available
ImageWriter writer = (ImageWriter) iter.next();
//instantiate an ImageWriteParam object with default compression options
ImageWriteParam iwp = writer.getDefaultWriteParam();
//Set the compression quality
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(0.8f);
//delete the file. If I dont the file size will stay the same
file.delete();
ImageOutputStream output = ImageIO.createImageOutputStream(new File(filename));
writer.setOutput(output);
IIOImage image = new IIOImage(img, null, null);
writer.write(null, image, iwp);
writer.dispose();
} catch (IOException ioe) {
logger.log(Level.SEVERE, ioe.getMessage());
}
}
Converting the final image from YUV back to RGB, will restore the colors of the image.
This conversion worked for me: cv2.cvtColor(img_file, cv2.COLOR_YUV2RGB)
From experience, I know that green is the color of freshly formatted YUV memory (YV12, in particular). So my guess is some step is failing, and you get luma information but the chroma gets botched. Looks to me like it's failing before it gets to the Cr plane.
Anyway, good luck, that's a tough one. Your code looks strange though--what's with the weird png specific code at the top? AFAIK, if you're using .NET you can pretty much treat any registered image format just as though it's an image without any funny work.
I have the same problem. In my test server run java 7 oracle and work fine. In my production server run openJDK 1.7, and compress images turn green...It´s seems bug in some JAVA versions.
I am trying to save an image to JPEG. The code below works fine when image width is a multiple of 4, but the image is skewed otherwise. It has something to do with padding. When I was debugging I was able to save the image as a bitmap correctly, by padding each row with 0s. However, this did not work out with the JPEG.
Main point to remember is my image is represented as bgr (blue green red 1 byte each) byte array which I receive from a native call.
byte[] data = captureImage(OpenGLCanvas.getLastFocused().getViewId(), x, y);
if (data.length != 3*x*y)
{
// 3 bytes per pixel
return false;
}
// create buffered image from raw data
DataBufferByte buffer = new DataBufferByte(data, 3*x*y);
ComponentSampleModel csm = new ComponentSampleModel(DataBuffer.TYPE_BYTE, x, y, 3, 3*x, new int[]{0,1,2} );
WritableRaster raster = Raster.createWritableRaster(csm, buffer, new Point(0,0));
BufferedImage buff_image = new BufferedImage(x, y, BufferedImage.TYPE_INT_BGR); // because windows goes the wrong way...
buff_image.setData(raster);
//save the BufferedImage as a jpeg
try
{
File file = new File(file_name);
FileOutputStream out = new FileOutputStream(file);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(buff_image);
param.setQuality(1.0f, false);
encoder.setJPEGEncodeParam(param);
encoder.encode(buff_image);
out.close();
// or JDK 1.4
// ImageIO.write(image, "JPEG", out);
}
catch (Exception ex)
{
// Write permissions on "file_name"
return false;
}
I also looked on creating the JPEG in C++ but there was even less material on that, but it is still an option.
Any help greatly apprecieated.
Leon
Thanks for your suggestions, but I have managed to work it out.
To capture the image I was using WINGDIAPI HBITMAP WINAPI CreateDIBSection in C++, then OpenGL would draw to that bitmap. Unbeknown to be, there was padding added to the bitmap automatically the width was not a multiple of 4.
Therefore Java was incorrectly interpreting the byte array.
Correct way is to interpret bytes is
byte[] data = captureImage(OpenGLCanvas.getLastFocused().getViewId(), x, y);
int x_padding = x%4;
BufferedImage buff_image = new BufferedImage(x, y, BufferedImage.TYPE_INT_RGB);
int val;
for (int j = 0; j < y; j++)
{
for (int i = 0; i < x; i++)
{
val = ( data[(i + j*x)*3 + j*x_padding + 2]& 0xff) +
((data[(i + j*x)*3 + j*x_padding + 1]& 0xff) << 8) +
((data[(i + j*x)*3 + j*x_padding + 0]& 0xff) << 16);
buff_image.setRGB(i, j, val);
}
}
//save the BufferedImage as a jpeg
try
{
File file = new File(file_name);
FileOutputStream out = new FileOutputStream(file);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(buff_image);
param.setQuality(1.0f, false);
encoder.setJPEGEncodeParam(param);
encoder.encode(buff_image);
out.close();
}
The JPEG standard is extremely complex. I am thinking it may be an issue with padding the output of the DCT somehow. The DCT is done to transform the content from YCrCb 4:2:2 to signal space with one DCT for each channel, Y,Cr, and Cb. The DCT is done on a "Macroblock" or "minimum coded block" depending on your context. JPEG usually has 8x8 macroblocks. When on the edge and there are not enough pixel it clamps the edge value and "drags it across" and does a DCT on that.
I am not sure if this helps, but it sounds like a non standard conforming file. I suggest you use JPEGSnoop to find out more. There are also several explanations about how JPEG compression works.
One possibility is that the sample rate may be encoded incorrectly. It might be something exotic such as 4:2:1 So you might be pulling twice as many X samples as there really are, thus distorting the image.
it is an image I capture from the screen
Maybe the Screen Image class will be easier to use.