Changing .png to work with JpegImagesToMovie.java

I am using JpegImagesToMovie.java to convert images to a .mov file. I was wondering if I could edit this to work with .png files, since the video quality is not very good, and changing it would improve it.

ImageIO.write(img, "png", new File("C:\\Users\\user\\Desktop\\tmp\\" + System.currentTimeMillis() + ".png")); ImageIO.write(img, "jpeg", new File("C:\\Users\\user\\Desktop\\tmp\\" + System.currentTimeMillis() + ".png")); ImageIO.write(img, "png", new File("C:\\Users\\user\\Desktop\\tmp\\" + System.currentTimeMillis() + ".jpeg")); 

All three of them will create a video (without errors through the program), but the video will not play only open and finished images.

I also tried editing JpegImagesToMovie.java

 if (!filePath.toLowerCase().endsWith(".png") && !filePath.toLowerCase().endsWith(".png")) { continue; 

But this did not work, and I can not find anywhere to edit. How can I get this to work with .png images?

Last update:

Here is my updated JpegImagesToMovies class

batch maple;

 /* * @(#)JpegImagesToMovie.java 1.3 01/03/13 * * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. * * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use, * modify and redistribute this software in source and binary code form, * provided that i) this copyright notice and license appear on all copies of * the software; and ii) Licensee does not utilize the software in a manner * which is disparaging to Sun. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * This software is not designed or intended for use in on-line control of * aircraft, air traffic, aircraft navigation or aircraft communications; or in * the design, construction, operation or maintenance of any nuclear * facility. Licensee represents and warrants that it will not use or * redistribute the Software for such purposes. */ import java.io.*; import java.util.*; import java.awt.Dimension; import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import javax.media.*; import javax.media.control.*; import javax.media.protocol.*; import javax.media.datasink.*; import javax.media.format.RGBFormat; import javax.media.format.VideoFormat; /** * This program takes a list of JPEG image files and convert them into a * QuickTime movie. */ public class JpegImagesToMovie implements ControllerListener, DataSinkListener { static private Vector<String> getImageFilesPathsVector( String imagesFolderPath) { File imagesFolder = new File(imagesFolderPath); String[] imageFilesArray = imagesFolder.list(); Vector<String> imageFilesPathsVector = new Vector<String>(); for (String imageFileName : imageFilesArray) { if (!imageFileName.toLowerCase().endsWith("png")) continue; imageFilesPathsVector.add(imagesFolder.getAbsolutePath() + File.separator + imageFileName); } return imageFilesPathsVector; } public boolean doIt(int width, int height, int frameRate, Vector<String> inFiles, MediaLocator outML) { ImageDataSource ids = new ImageDataSource(width, height, frameRate, inFiles); Processor p; try { System.err .println("- create processor for the image datasource ..."); p = Manager.createProcessor(ids); } catch (Exception e) { System.err .println("Yikes! Cannot create a processor from the data source."); return false; } p.addControllerListener(this); // Put the Processor into configured state so we can set // some processing options on the processor. p.configure(); if (!waitForState(p, Processor.Configured)) { System.err.println("Failed to configure the processor."); return false; } // Set the output content descriptor to QuickTime. p.setContentDescriptor(new ContentDescriptor( FileTypeDescriptor.QUICKTIME));// FileTypeDescriptor.MSVIDEO // Query for the processor for supported formats. // Then set it on the processor. TrackControl tcs[] = p.getTrackControls(); Format f[] = tcs[0].getSupportedFormats(); if (f == null || f.length <= 0) { System.err.println("The mux does not support the input format: " + tcs[0].getFormat()); return false; } tcs[0].setFormat(f[0]); System.err.println("Setting the track format to: " + f[0]); // We are done with programming the processor. Let just // realize it. p.realize(); if (!waitForState(p, Controller.Realized)) { System.err.println("Failed to realize the processor."); return false; } // Now, we'll need to create a DataSink. DataSink dsink; if ((dsink = createDataSink(p, outML)) == null) { System.err .println("Failed to create a DataSink for the given output MediaLocator: " + outML); return false; } dsink.addDataSinkListener(this); fileDone = false; System.err.println("start processing..."); // OK, we can now start the actual transcoding. try { p.start(); dsink.start(); } catch (IOException e) { System.err.println("IO error during processing"); return false; } // Wait for EndOfStream event. waitForFileDone(); // Cleanup. try { dsink.close(); } catch (Exception e) { } p.removeControllerListener(this); System.err.println("...done processing."); return true; } /** * Create the DataSink. */ DataSink createDataSink(Processor p, MediaLocator outML) { DataSource ds; if ((ds = p.getDataOutput()) == null) { System.err .println("Something is really wrong: the processor does not have an output DataSource"); return null; } DataSink dsink; try { System.err.println("- create DataSink for: " + outML); dsink = Manager.createDataSink(ds, outML); dsink.open(); } catch (Exception e) { System.err.println("Cannot create the DataSink: " + e); return null; } return dsink; } Object waitSync = new Object(); boolean stateTransitionOK = true; /** * Block until the processor has transitioned to the given state. Return * false if the transition failed. */ boolean waitForState(Processor p, int state) { synchronized (waitSync) { try { while (p.getState() < state && stateTransitionOK) waitSync.wait(); } catch (Exception e) { } } return stateTransitionOK; } /** * Controller Listener. */ public void controllerUpdate(ControllerEvent evt) { if (evt instanceof ConfigureCompleteEvent || evt instanceof RealizeCompleteEvent || evt instanceof PrefetchCompleteEvent) { synchronized (waitSync) { stateTransitionOK = true; waitSync.notifyAll(); } } else if (evt instanceof ResourceUnavailableEvent) { synchronized (waitSync) { stateTransitionOK = false; waitSync.notifyAll(); } } else if (evt instanceof EndOfMediaEvent) { evt.getSourceController().stop(); evt.getSourceController().close(); } } Object waitFileSync = new Object(); boolean fileDone = false; boolean fileSuccess = true; /** * Block until file writing is done. */ boolean waitForFileDone() { synchronized (waitFileSync) { try { while (!fileDone) waitFileSync.wait(); } catch (Exception e) { } } return fileSuccess; } /** * Event handler for the file writer. */ public void dataSinkUpdate(DataSinkEvent evt) { if (evt instanceof EndOfStreamEvent) { synchronized (waitFileSync) { fileDone = true; waitFileSync.notifyAll(); } } else if (evt instanceof DataSinkErrorEvent) { synchronized (waitFileSync) { fileDone = true; fileSuccess = false; waitFileSync.notifyAll(); } } } public static void main(String args[]) { // changed this method a bit if (args.length == 0) prUsage(); // Parse the arguments. int i = 0; int width = -1, height = -1, frameRate = -1; Vector<String> inputFiles = new Vector<String>(); String rootDir = null; String outputURL = null; while (i < args.length) { if (args[i].equals("-w")) { i++; if (i >= args.length) prUsage(); width = new Integer(args[i]).intValue(); } else if (args[i].equals("-h")) { i++; if (i >= args.length) prUsage(); height = new Integer(args[i]).intValue(); } else if (args[i].equals("-f")) { i++; if (i >= args.length) prUsage(); // new Integer(args[i]).intValue(); frameRate = Integer.parseInt(args[i]); } else if (args[i].equals("-o")) { i++; if (i >= args.length) prUsage(); outputURL = args[i]; } else if (args[i].equals("-i")) { i++; if (i >= args.length) prUsage(); rootDir = args[i]; } else { System.out.println("."); prUsage(); } i++; } if (rootDir == null) { System.out .println("Since no input (-i) forder provided, assuming this JAR is inside JPEGs folder."); rootDir = (new File(".")).getAbsolutePath(); } inputFiles = getImageFilesPathsVector(rootDir); if (inputFiles.size() == 0) prUsage(); if (outputURL == null) { outputURL = (new File(rootDir)).getAbsolutePath() + File.separator + "pngs2movie.mov"; } if (!outputURL.toLowerCase().startsWith("file:///")) { outputURL = "file:///" + outputURL; } // Check for output file extension. if (!outputURL.toLowerCase().endsWith(".mov")) { prUsage(); outputURL += ".mov"; System.out .println("outputURL should be ending with mov. Making this happen.\nNow outputURL is: " + outputURL); } if (width < 0 || height < 0) { prUsage(); System.out.println("Trying to guess movie size from first image"); BufferedImage firstImageInFolder = getFirstImageInFolder(rootDir); width = firstImageInFolder.getWidth(); height = firstImageInFolder.getHeight(); System.out.println("width = " + width); System.out.println("height = " + height); } // Check the frame rate. if (frameRate < 1) frameRate = 30; // Generate the output media locators. MediaLocator oml; if ((oml = createMediaLocator(outputURL)) == null) { System.err.println("Cannot build media locator from: " + outputURL); System.exit(0); } JpegImagesToMovie imageToMovie = new JpegImagesToMovie(); imageToMovie.doIt(width, height, frameRate, inputFiles, oml); System.exit(0); } private static BufferedImage getFirstImageInFolder(String rootDir) { File rootFile = new File(rootDir); String[] list = (rootFile).list(); BufferedImage bufferedImage = null; for (String filePath : list) { if (!filePath.toLowerCase().endsWith(".png") && !filePath.toLowerCase().endsWith(".png")) { continue; } try { bufferedImage = ImageIO.read(new File(rootFile .getAbsoluteFile() + File.separator + filePath)); break; } catch (IOException e) { e.printStackTrace(); } } return bufferedImage; } static void prUsage() { System.err .println("Usage: java JpegImagesToMovie [-w <width>] [-h <height>] [-f <frame rate>] [-o <output URL>] -i <input JPEG files dir Path>"); // System.exit(-1); } /** * Create a media locator from the given string. */ @SuppressWarnings("unused") public static MediaLocator createMediaLocator(String url) { MediaLocator ml; if (url.indexOf(":") > 0 && (ml = new MediaLocator(url)) != null) return ml; if (url.startsWith(File.separator)) { if ((ml = new MediaLocator("file:" + url)) != null) return ml; } else { String file = "file:" + System.getProperty("user.dir") + File.separator + url; if ((ml = new MediaLocator(file)) != null) return ml; } return null; } // ///////////////////////////////////////////// // // Inner classes. // ///////////////////////////////////////////// /** * A DataSource to read from a list of JPEG image files and turn that into a * stream of JMF buffers. The DataSource is not seekable or positionable. */ class ImageDataSource extends PullBufferDataSource { ImageSourceStream streams[]; ImageDataSource(int width, int height, int frameRate, Vector<String> images) { streams = new ImageSourceStream[1]; streams[0] = new PngImageSourceStream(width, height, frameRate, images); } public void setLocator(MediaLocator source) { } public MediaLocator getLocator() { return null; } /** * Content type is of RAW since we are sending buffers of video frames * without a container format. */ public String getContentType() { return ContentDescriptor.RAW; } public void connect() { } public void disconnect() { } public void start() { } public void stop() { } /** * Return the ImageSourceStreams. */ public PullBufferStream[] getStreams() { return streams; } /** * We could have derived the duration from the number of frames and * frame rate. But for the purpose of this program, it not necessary. */ public Time getDuration() { return DURATION_UNKNOWN; } public Object[] getControls() { return new Object[0]; } public Object getControl(String type) { return null; } } /** * The source stream to go along with ImageDataSource. */ class ImageSourceStream implements PullBufferStream { Vector<String> images; int width, height; VideoFormat format; int nextImage = 0; // index of the next image to be read. boolean ended = false; public ImageSourceStream(int width, int height, int frameRate, Vector<String> images) { this.width = width; this.height = height; this.images = images; format = new VideoFormat(VideoFormat.JPEG, new Dimension(width, height), Format.NOT_SPECIFIED, Format.byteArray, (float) frameRate); } /** * We should never need to block assuming data are read from files. */ public boolean willReadBlock() { return false; } /** * This is called from the Processor to read a frame worth of video * data. */ public void read(Buffer buf) throws IOException { // Check if we've finished all the frames. if (nextImage >= images.size()) { // We are done. Set EndOfMedia. System.err.println("Done reading all images."); buf.setEOM(true); buf.setOffset(0); buf.setLength(0); ended = true; return; } String imageFile = (String) images.elementAt(nextImage); nextImage++; System.err.println(" - reading image file: " + imageFile); // Open a random access file for the next image. RandomAccessFile raFile; raFile = new RandomAccessFile(imageFile, "r"); byte data[] = null; // Check the input buffer type & size. if (buf.getData() instanceof byte[]) data = (byte[]) buf.getData(); // Check to see the given buffer is big enough for the frame. if (data == null || data.length < raFile.length()) { data = new byte[(int) raFile.length()]; buf.setData(data); } // Read the entire JPEG image from the file. raFile.readFully(data, 0, (int) raFile.length()); System.err.println(" read " + raFile.length() + " bytes."); buf.setOffset(0); buf.setLength((int) raFile.length()); buf.setFormat(format); buf.setFlags(buf.getFlags() | Buffer.FLAG_KEY_FRAME); // Close the random access file. raFile.close(); } /** * Return the format of each video frame. That will be JPEG. */ public Format getFormat() { return format; } public ContentDescriptor getContentDescriptor() { return new ContentDescriptor(ContentDescriptor.RAW); } public long getContentLength() { return 0; } public boolean endOfStream() { return ended; } public Object[] getControls() { return new Object[0]; } public Object getControl(String type) { return null; } } class PngImageSourceStream extends ImageSourceStream { public PngImageSourceStream(int width, int height, int frameRate, Vector<String> images) { super(width, height, frameRate, images); // configure the new format as RGB format format = new RGBFormat(new Dimension(width, height), Format.NOT_SPECIFIED, Format.byteArray, frameRate, 24, // 24 bits per pixel 1, 2, 3); // red, green and blue masks when data are in the form of byte[] } public void read(Buffer buf) throws IOException { // Check if we've finished all the frames. if (nextImage >= images.size()) { // We are done. Set EndOfMedia. System.err.println("Done reading all images."); buf.setEOM(true); buf.setOffset(0); buf.setLength(0); ended = true; return; } String imageFile = (String) images.elementAt(nextImage); nextImage++; System.err.println(" - reading image file: " + imageFile); // read the PNG image BufferedImage image = ImageIO.read( new File(imageFile) ); Dimension size = format.getSize(); // convert 32-bit RGBA to 24-bit RGB byte[] imageData = convertTo24Bit(image.getRaster().getPixels(0, 0, size.width, size.height, (int[]) null)); buf.setData(imageData); System.err.println(" read " + imageData.length + " bytes."); buf.setOffset(0); buf.setLength(imageData.length); buf.setFormat(format); buf.setFlags(buf.getFlags() | Buffer.FLAG_KEY_FRAME); } private void convertIntByteToByte(int[] src, int srcIndex, byte[] out, int outIndex) { // Note: the int[] returned by bufferedImage.getRaster().getPixels() is an int[] // where each int is the value for one color ie the first 4 ints contain the RGBA values for the first pixel int r = src[srcIndex]; int g = src[srcIndex+1]; int b = src[srcIndex+2]; out[outIndex] = (byte) (r & 0xFF); out[outIndex+1] = (byte) (g & 0xFF); out[outIndex+2] = (byte) (b & 0xFF); } private byte[] convertTo24Bit(int[] input) { int dataLength = input.length; byte[] convertedData = new byte[ dataLength * 3 / 4 ]; // for every 4 int values of the original array (RGBA) write 3 // bytes (RGB) to the output array for (int i = 0, j = 0; i < dataLength; i+=4, j+=3) { convertIntByteToByte(input, i, convertedData, j); } return convertedData; } } } 

I am making a video using the following call in my main method

 r.makeVideo("Video.mov"); 

And here is this method.

 public void makeVideo (String movFile) throws MalformedURLException { JpegImagesToMovie imageToMovie = new JpegImagesToMovie(); Vector<String> imgList = new Vector <String>(); File f = new File("C:\\Users\\user\\Desktop\\tmp\\"); File[] fileList = f.listFiles(); for (int i = 0; i < fileList.length; i++) { imgList.add(fileList[i].getAbsolutePath()); } MediaLocator ml; if ((ml = imageToMovie.createMediaLocator(movFile)) == null) { System.exit(0); } imageToMovie.doIt(width, height, (1000/125), imgList, ml); } 

Error at startup:

Usage: java JpegImagesToMovie [-w] [-h] [-f] [-o] -i Since no input (-i) forder is provided, this JAR is assumed to be inside the JPEG folder. Usage: java JpegImagesToMovie [-w] [-h] [-f] [-o] -i Usage: java JpegImagesToMovie [-w] [-h] [-f] [-o] -i Trying to guess the movie size from the first Image Exception in the "main" java.lang.NullPointerException thread on maple.JpegImagesToMovie.main (JpegImagesToMovie.javaI42)

line 342

 width = firstImageInFolder.getWidth(); 
+4
source share
4 answers

Although a bit late (since the question has already been answered), and because, coincidentally, I have been working with ImageIO over the past few days, I will simply add my answer here. Especially the second part of how to make JpegImagesToMovie to work with png files has not received an answer, and this may help someone else.

Double Compression Problem:. As correctly identified by others, you effectively compress a JPEG image twice using ImageIO.write() . ImageIO is a utility class based on the file type ("jpeg" in our case) selects the appropriate ImageWriter . Then it creates an ImageInputStream and passes it to the author. In the latter case, it calls its write() method. Pretty much what @meewoK does in its answer. However, each ImageWriter can accept an instance of ImageWriteParam that adjusts its details. ImageIO , apparently, cannot know what parameters each author can take and how it should be configured, so the default settings are used. If you look at the source, JPEGImageWriteParam has a default quality factor of 0.75 , so you effectively multiply this coefficient by any quality used for the source file. (If it was 0.75 again, your final image is pretty much 0.75 * 0.75 = 0.56 , meaning you lost half of the original). Bottom line: use ImageIO for quick reading or writing, but if you need more control, it is recommended that you use ImageWriter manually.

How to make JpegImagesToMovie work directly with PNG files:. If you look at the source of this class, all the work is done in its inner class ImageSourceStream . What he does is that he loads bytes from files (each file is a video frame) directly into the Processor class that creates the video. Conveniently, Processor knows how to handle the JPEG format directly (look at the stream constructor format = new VideoFormat(VideoFormat.JPEG, ...) ), so loading it with raw file data works as expected.

To make it work with the PNG format, simply replacing the file suffix according to your approach is not enough. You need to convert the PNG data to a format that Processor understands (that is, to decode it). Below you can find a quick and dirty way to do this. Disclaimer:. The approach below uses more memory, as it loads the image into memory and then processes it to convert it to byte[] . So the worst in performance and memory. However, if memory and speed are not a problem, this will allow you to work directly with PNG files.

Conversion Steps:

a) . When searching for an editor and replacing the string values "jpeg", "jpg" with "png" . The original author has these hardcoded values, and during your attempt you missed some entries.

b) In the ImageDataSource constructor ImageDataSource replace the first line with the second:

 streams[0] = new ImageSourceStream(width, height, frameRate, images); // delete this line streams[0] = new PngImageSourceStream(width, height, frameRate, images); // add this line 

c) At the end of the class, add the implementation of the new PngImageSourceStream below.

Now you should have a working copy of the original version, which can directly read PNG files and convert them to MOV video (Note. Not all players can play this new video because of the codec used. QuickTime and Media Player Classic worked fine for me)

Update 1: PngImageSourceStream source code assumes a 32-bit PNG file, a limitation that I forgot to mention. The next version is the second version, which supports 32-bit or 24-bit (that is, no alpha layer) images.

 class PngImageSourceStream extends ImageSourceStream { public PngImageSourceStream(int width, int height, int frameRate, Vector<String> images) { super(width, height, frameRate, images); // configure the new format as RGB format format = new RGBFormat(new Dimension(width, height), Format.NOT_SPECIFIED, Format.byteArray, frameRate, 24, // 24 bits per pixel 1, 2, 3); // red, green and blue masks when data are in the form of byte[] } public void read(Buffer buf) throws IOException { // Check if we've finished all the frames. if (nextImage >= images.size()) { // We are done. Set EndOfMedia. System.err.println("Done reading all images."); buf.setEOM(true); buf.setOffset(0); buf.setLength(0); ended = true; return; } String imageFile = (String) images.elementAt(nextImage); nextImage++; System.err.println(" - reading image file: " + imageFile); // read the PNG image BufferedImage image = ImageIO.read(new File(imageFile)); boolean hasAlpha = image.getColorModel().hasAlpha(); Dimension size = format.getSize(); // convert 32-bit RGBA to 24-bit RGB byte[] imageData = convertTo24Bit(hasAlpha, image.getRaster().getPixels(0, 0, size.width, size.height, (int[]) null)); buf.setData(imageData); System.err.println(" read " + imageData.length + " bytes."); buf.setOffset(0); buf.setLength(imageData.length); buf.setFormat(format); buf.setFlags(buf.getFlags() | Buffer.FLAG_KEY_FRAME); } private void convertIntByteToByte(int[] src, int srcIndex, byte[] out, int outIndex) { // Note: the int[] returned by bufferedImage.getRaster().getPixels() // is an int[] // where each int is the value for one color ie the first 4 ints // contain the RGBA values for the first pixel int r = src[srcIndex]; int g = src[srcIndex + 1]; int b = src[srcIndex + 2]; out[outIndex] = (byte) (r & 0xFF); out[outIndex + 1] = (byte) (g & 0xFF); out[outIndex + 2] = (byte) (b & 0xFF); } private byte[] convertTo24Bit(boolean hasAlpha, int[] input) { int dataLength = input.length; int newSize = (hasAlpha ? dataLength * 3 / 4 : dataLength); byte[] convertedData = new byte[newSize]; // for every 4 int values of the original array (RGBA) write 3 // bytes (RGB) to the output array // if there is no alpha (ie RGB image) then just convert int to byte for (int i = 0, j = 0; i < dataLength; i += 3, j += 3) { convertIntByteToByte(input, i, convertedData, j); if (hasAlpha) { i++; // skip an extra byte if the original image has an // extra int for transparency } } return convertedData; } } 
+6
source

Set JPEG 1 compression level to higher quality. This will result in a larger file size, but it should fix the quality problem.

Encode them only as:

 ImageIO.write(img, "jpg", new File("..." + ".jpg")); 

GyCaw.png

Compile and run the code in the linked answer, drag the Slider left up and down to see the result of this compression level in JPEG (lower / third image). The text area below will show the size in bytes at the "quality" level. The quality is the opposite of compression, and, as you can see from the images, the JPEG quality in% 80 is not only a little "muddy", but also much more in bytes than PNG.

Then look carefully at the code, especially:

 private Image getJpegCompressedImage(BufferedImage image) throws IOException { float qualityFloat = (float)quality.getValue()/100f; ByteArrayOutputStream outStream = new ByteArrayOutputStream(); ImageWriter imgWriter = ImageIO.getImageWritersByFormatName( "jpg" ).next(); ImageOutputStream ioStream = ImageIO.createImageOutputStream( outStream ); imgWriter.setOutput( ioStream ); JPEGImageWriteParam jpegParams = new JPEGImageWriteParam( Locale.getDefault() ); jpegParams.setCompressionMode( ImageWriteParam.MODE_EXPLICIT ); jpegParams.setCompressionQuality( qualityFloat ); // Set the compression level imgWriter.write( null, new IIOImage( image, null, null ), jpegParams ); ioStream.flush(); ioStream.close(); imgWriter.dispose(); jpgSize = outStream.toByteArray().length; BufferedImage compressedImage = ImageIO.read(new ByteArrayInputStream(outStream.toByteArray())); return compressedImage; } 
+5
source

I take a hit on this bypassing ImgIO.write() and directly writing bytes to a file using FileOutputStream ...

I overtook ImgIO.write due to the many mentions on the Internet that using this method you cannot control image quality.

Quote: http://www.universalwebservices.net/web-programming-resources/java/adjust-jpeg-image-compression-quality-when-saving-images-in-java

If you use the imageIO.write method to save JPEG images, you may notice that some images lose quality. This is because you cannot instruct the imagIO.write method to apply a certain compression quality to images.

My first hit:

 import java.awt.image.BufferedImage; public class Quality { public static void main (String[] args) { BufferedImage img; try { File f = new File("test" + ".jpg"); img = ImageIO.read(f); writeJpegCompressedImage(img,"NEW" + ".jpg" ); } catch (IOException e) { e.printStackTrace(); } } private static void writeJpegCompressedImage(BufferedImage image, String outFile) throws IOException { float qualityFloat = 1f; ByteArrayOutputStream outStream = new ByteArrayOutputStream(); ImageWriter imgWriter = ImageIO.getImageWritersByFormatName( "jpg" ).next(); ImageOutputStream ioStream = ImageIO.createImageOutputStream( outStream ); imgWriter.setOutput( ioStream ); JPEGImageWriteParam jpegParams = new JPEGImageWriteParam( Locale.getDefault() ); jpegParams.setCompressionMode( ImageWriteParam.MODE_EXPLICIT ); jpegParams.setCompressionQuality( qualityFloat ); imgWriter.write( null, new IIOImage( image, null, null ), jpegParams ); ioStream.flush(); ioStream.close(); imgWriter.dispose(); OutputStream outputStream = new FileOutputStream ( outFile); outStream.writeTo(outputStream); } } 
+3
source

you can find the answer by looking at this link http://javahive.in/png-to-jpg-converter/

0
source

Source: https://habr.com/ru/post/1494093/


All Articles