5

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

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

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

我还尝试编辑 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<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'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<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'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<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 i.e. 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;
              }

            }
}

我在我的主要方法中使用以下调用制作视频

r.makeVideo("Video.mov");

这就是那个方法。

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

}

运行时出错:

用法: 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 试图从maple.JpegImagesToMovie.main(JpegImagesToMovie.java:342) 的线程“main”java.lang.NullPointerException 中的第一个图像异常

第 342 行

width = firstImageInFolder.getWidth();
4

3 回答 3

6

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

双重压缩问题:正如其他人正确识别的那样,您使用ImageIO.write(). 这ImageIO是一个实用程序类,它根据文件的类型(在我们的例子中为“jpeg”)选择适当的ImageWriter. 然后它构造一个ImageInputStream并将它传递给作者。最后它调用它的write()方法。@meewoK 在他的回答中几乎做了什么。但是,每个人都ImageWriter可以采用一个ImageWriteParam配置其详细信息的实例。ImageIO显然不知道每个 writer 可以接受哪些参数,也不知道应该如何配置,所以使用默认设置。如果您查看源JPEGImageWriteParam的默认品质因数为0.75,那么您实际上是在相乘这个因素与原始文件使用的任何质量有关。(如果它又是 0.75,那么您的最终图像几乎具有0.75 * 0.75 = 0.56质量,即您丢失了原始图像的一半)。底线:ImageIO用于快速读取或写入,但如果需要更多控制,推荐的方法是ImageWriter手动配置和使用。

如何JpegImagesToMovie直接使用 PNG 文件:如果您查看该类的源代码,所有工作都在其ImageSourceStream内部类中完成。它的作用是将文件中的字节(每个文件都是一个视频帧)直接加载到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 文件并将其转换为 MOV 视频的原始版本的工作副本(注意:由于使用了编解码器,并非所有播放器都可以播放该新视频。QuickTime 和 Media Player Classic 工作正常为了我)

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

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 i.e. 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 (i.e. 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;
  }

}
于 2013-08-07T08:16:17.000 回答
5

将 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;
}
于 2013-07-29T16:23:06.013 回答
4

我正在尝试通过绕过并使用...ImgIO.write()直接将字节写入文件来解决这个问题。FileOutputStream

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

    }
}
于 2013-08-05T22:34:58.260 回答