处理大量的大型jpgs

时间:2012-11-24 23:49:37

标签: java image swing

我正在编写一个程序,部分只是为了好玩,部分是为了帮助我处理一堆数字图片,我想分成打印类别。主要思想是它应该在一列中显示图片,并在每张图片旁边有一组带有类别名称的复选框。我检查所需的复选框,按“去!”按钮,图片将被复制到子文件夹中,具体取决于所选的复选框。

现在,一切都差不多完了 - 除了一件事。有问题的图片是大型的jpgs,每个大约7-8MB,其中大约有700个。如果我尝试一次加载它们,自然地,它需要大量的内存和时间才能加载它们。那么,这个问题有一个很好的解决方案吗?我的两个想法如下。

1)一次加载图片10并在某处有下一个/上一个按钮。我不喜欢这个想法,因为它添加了不需要的元素。 2)使应用程序在滚动到它们时加载新图片并卸载滚动过去的图片。我真的很喜欢这个主意。

有人能指出我正确的方向,关于如何实施后一个想法?我在Google上只发现了一个相关的link,但我不能说它对我有所帮助,我对代码的某些部分感到有些困惑。

4 个答案:

答案 0 :(得分:2)

如果缩略图足够,这个answer包含一种简单的重新采样方法,并引用了一些权衡。如果没有,这个answer概述了显示和缓存最近图像的一般方法。

在任何一种情况下,Boolean的默认JTable renderer/editor都是JCheckBoxCheckOne就是一个例子。

答案 1 :(得分:1)

你必须为所有图片创建thumnails,你可以将thumnails保留在内存中。 这可能需要很多时间。

然后你要么准备好了这个问题。或缩略图不适合所有内存。 如果是这种情况:你加载了30-40个,在滚动过程中你将滚动方向分开,并在另一个线程中加载下一个。

如果加载速度低于用户滚动的速度,则显示默认的占位符图像以显示“尚未加载的图片”

答案 2 :(得分:0)

您可以缩小所有图像以减少使用RAM。例如此代码将所有图像缩小为200x200。这样你可以在100MB内容纳1000张图像。

import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
import java.io.File;

public class Scroll extends JPanel {
    public static void main(String[] args) throws Exception {
        JFrame frame = new JFrame();
        JPanel panel = new Scroll();

        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        for(int i = 0; i < 10; i++) {
            JPanel buttonPanel = new JPanel();
            JRadioButton b1 = new JRadioButton("button 1");
            JRadioButton b2 = new JRadioButton("button 2");
            JRadioButton b3 = new JRadioButton("button 3");
            ButtonGroup group = new ButtonGroup();
            group.add(b1);
            group.add(b2);
            group.add(b3);
            buttonPanel.add(b1);
            buttonPanel.add(b2);
            buttonPanel.add(b3);

            BufferedImage buffer = new BufferedImage(200,200,BufferedImage.TYPE_INT_RGB);
            Graphics2D g = buffer.createGraphics();

            BufferedImage image = ImageIO.read(new File("image.jpg"));
            g.scale(buffer.getWidth()*1.0/image.getWidth(),
                    buffer.getHeight()*1.0/image.getHeight());
            g.drawImage(image, 0, 0, null);
            g.dispose();
            JLabel imageLabel = new JLabel(new ImageIcon(buffer));
            JSplitPane splitPane = new JSplitPane();
            splitPane.add(imageLabel, JSplitPane.LEFT);
            splitPane.add(buttonPanel, JSplitPane.RIGHT);
            panel.add(splitPane);
        }
        JScrollPane spane = new JScrollPane(panel);
        frame.add(spane);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500,600);
        frame.setVisible(true);
    }
}

如果要在图像变得可见时动态加载图像,则必须为每个图像使用空的JPanel而不是ImageIcons,然后覆盖这些JPanel的paint方法。只有在JPanel可见时才会调用paint方法。因此,最简单的解决方案是始终在paint方法中从磁盘加载图像,然后将其绘制到屏幕上。

import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
import java.io.File;

public class Scroll extends JPanel {
    public static void main(String[] args) throws Exception {
        JFrame frame = new JFrame();
        JPanel panel = new Scroll();

        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        for(int i = 0; i < 10; i++) {
            JPanel buttonPanel = new JPanel();
            JRadioButton b1 = new JRadioButton("button 1");
            JRadioButton b2 = new JRadioButton("button 2");
            JRadioButton b3 = new JRadioButton("button 3");
            ButtonGroup group = new ButtonGroup();
            group.add(b1);
            group.add(b2);
            group.add(b3);
            buttonPanel.add(b1);
            buttonPanel.add(b2);
            buttonPanel.add(b3);

            JPanel imagePanel = new JPanel() {
                {
                    BufferedImage image = ImageIO.read(new File("image.jpg"));
                    setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
                    image.flush();
                }
                @Override
                public void paint(Graphics g) {
                    try {
                        BufferedImage image = ImageIO.read(new File("image.jpg"));
                        g.drawImage(image, 0, 0, null);
                        image.flush();
                    } catch(Exception e) {
                    }
                }
            };

            JSplitPane splitPane = new JSplitPane();
            splitPane.add(imagePanel, JSplitPane.LEFT);
            splitPane.add(buttonPanel, JSplitPane.RIGHT);
            panel.add(splitPane);
        }
        JScrollPane spane = new JScrollPane(panel);
        frame.add(spane);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500,600);
        frame.setVisible(true);
    }
}

答案 3 :(得分:0)

如果这是一个“使用一次又扔掉”的程序,我不会费心去解决这个问题。我将使用命令行为每个图片生成缩略图,并在我的程序中使用这些生成的缩略图。将700个缩略图保留在内存中应该是可行的。

如果这不是一个选项,我会以JTable开头。您可以创建TableModel,而无需将所有图像加载到内存中。你只需要知道图像数量。 JTable只想渲染当时可见的图像。警告可能是在渲染器询问它们时加载图像可能为时已晚。我可以想象,当您必须加载大小为几MB的图像时,JTable将无法顺利运行。但这可以通过使用填充工作线程的缓存来解决。因此,如果渲染器请求第5张图像,您还将5张先前图像和5张下一张图像加载到缓存中。

另请注意,如果您正在移动图片iso复制它们,这可能会影响您的TableModel,因为目录中的图像数量会发生变化。一定要考虑到这一点!