更改.png以使用JpegImagesToMovie.java

我正在使用JpegImagesToMovie.java将图像转换为.mov文件。 我想知道是否有我可以编辑这个以使用.png文件,因为video的质量不是很好,改变它会改善它。

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")); 

所有这三个都会产生一个video(没有错误通过该程序),但video不会播放图像只是打开和完成。

我也尝试过编辑JpegImagesToMovie.java

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

但这没有用,我找不到其他任何地方可以编辑。 我怎样才能使用.png图像?

最近更新:

这是我更新的JpegImagesToMovies类

包枫;

 /* * @(#)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 getImageFilesPathsVector( String imagesFolderPath) { File imagesFolder = new File(imagesFolderPath); String[] imageFilesArray = imagesFolder.list(); Vector imageFilesPathsVector = new Vector(); 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 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's 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 inputFiles = new Vector(); String rootDir = null; String outputURL = null; while (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 ] [-h ] [-f ] [-o ] -i "); // 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 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's 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 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 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 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; } } } 

我在main方法中使用以下调用制作video

 r.makeVideo("Video.mov"); 

这是方法。

 public void makeVideo (String movFile) throws MalformedURLException { JpegImagesToMovie imageToMovie = new JpegImagesToMovie(); Vector imgList = new Vector (); 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); } 

运行时出错:

用法:java JpegImagesToMovie [-w] [-h] [-f] [-o] -i由于没有提供输入(-i)forder,假设这个JAR在JPEGs文件夹中。 用法:java JpegImagesToMovie [-w] [-h] [-f] [-o] -i用法:java JpegImagesToMovie [-w] [-h] [-f] [-o] -i试图猜测电影大小第一个图像在线程“main”中的exceptionjava.lang.NullPointerException在maple.JpegImagesToMovie.main(JpegImagesToMovie.java:342)

第342行

 width = firstImageInFolder.getWidth(); 

虽然有点晚了(因为问题已经回答了),而且巧合的是我在过去的几天里一直在使用ImageIO ,我只想在这里添加我的答案。 特别是如何使JpegImagesToMovie使用png文件的第二部分尚未得到解答,它可能会帮助其他人。

双压缩问题:正如其他人正确识别的那样,您通过使用ImageIO.write()有效地压缩JPEG图像两次。 ImageIO是一个基于文件类型的实用程序类(在我们的例子中是“jpeg”)选择一个合适的ImageWriter 。 然后它构造一个ImageInputStream并将它传递给writer。 最后它调用了write()方法。 几乎@meewoK在他的回答中做了什么。 但是,每个ImageWriter都可以使用ImageWriteParam实例来配置其详细信息。 ImageIO显然无法知道每个编写器可以接受的参数,也不知道应该如何配置它们,因此使用默认设置。 如果您查看源JPEGImageWriteParam的默认品质因数为0.75 ,那么您实际上将此因子乘以原始文件所用的质量。 (如果它再次是0.75,你的最终图像几乎有0.75 * 0.75 = 0.56质量,即你丢失了一半原版)。 底线:使用ImageIO进行快速读取或写入,但如果需要更多控制,建议的方法是手动配置和使用ImageWriter

如何使JpegImagesToMovie直接使用PNG文件:如果查看该类的源代码,所有工作都在其ImageSourceStream内部类中完成。 它的作用是,它将文件中的字节( 每个文件是一个video帧 )直接加载到创建video的Processor类。 方便地, Processor知道如何直接处理JPEG格式(查看流的构造函数format = new VideoFormat(VideoFormat.JPEG, ...) ),因此使用原始文件数据提供它可以按预期工作。

但要使其与PNG格式一起使用,只需根据您的方法替换文件后缀是不够的。 您需要将PNG数据转换为Processor理解的格式(即解码它)。 下面你可以找到一种快速而肮脏的方法。 免责声明:下面的方法使用更多的内存,因为它将图像加载到内存中,并进一步操作它将其转换为byte[] 。 因此,性能和内存方面最差。 但是,如果不考虑内存和速度,它将允许您直接使用PNG文件。

转换步骤:

a) 使用编辑器搜索并将字符串值"jpeg", "jpg"替换为"png" 。 原作者将这些值硬编码,在您尝试期间,您错过了一些条目。

b)ImageDataSource的构造函数中,用第二行替换第一行:

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

c)在类的末尾添加下面提供的新PngImageSourceStream的实现。

您现在应该拥有原始版本的工作副本,该副本能够直接读取PNG文件并将其转换为MOVvideo(注意:由于使用了编解码器,并非所有播放器都可以播放该新video.QuickTime和Media Player Classic工作正常为了我)

更新1: PngImageSourceStream的原始代码假设一个32位的PNG文件,我忘了提一个限制。 下面的版本是第二个版本,支持32位或24位(即没有alpha层)图像。

 class PngImageSourceStream extends ImageSourceStream { public PngImageSourceStream(int width, int height, int frameRate, Vector 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; } } 

将JPEG压缩级别1设置为更高的质量。 这将导致更大的文件大小,但应该解决质量问题。

但只能编码为:

 ImageIO.write(img, "jpg", new File("..." + ".jpg")); 
  1. 如此答案所示 – 下面的截图。

编译并运行链接答案中的代码,向上和向下拖动左侧的Slider以查看JPEG(底部/第三图像)中压缩级别的结果。 底部的文本区域将显示“质量”级别的字节大小。 质量与压缩相反,正如您可能从图像中看到的那样,%80质量的JPEG不仅有点’模糊’,而且已经明显比PNG大得多。

然后仔细看看代码,特别是:

 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; } 

我通过绕过ImgIO.write()并使用FileOutputStream直接将字节写入文件来对此进行ImgIO.write()

由于互联网上有很多提及说图像质量无法通过这种方法控制,我正在使用ImgIO.write

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

如果您一直使用imageIO.write方法来保存JPEG图像,您可能会注意到某些图像质量下降。 这是因为您无法指示imagIO.write方法将某种压缩质量应用于图像。

我的第一个刺:

 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); } } 

你可以通过查看这个链接找到你的答案http://javahive.in/png-to-jpg-converter/