繁体   English   中英

处理海量大jpg

[英]Processing huge amount of large jpgs

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

现在,一切都差不多完成了——除了一件事。 有问题的图片是大jpg,每个大约7-8MB,大约有700张。 如果我尝试一次加载它们,自然地,将它们全部加载需要大量内存和时间。 那么,有没有好的解决办法呢? 我的两个想法如下。

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

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

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

在任何一种情况下, JTable的默认Boolean 渲染器/编辑器都是JCheckBox CheckOne就是一个例子。

您必须为所有图片创建缩略图,您可以将缩略图保存在内存中。 那可能需要很多时间。

那么你要么准备好解决问题。 否则缩略图将无法全部放入内存中。 如果是这种情况:您加载 30-40 个,并在滚动期间检测滚动方向,然后在单独的线程中加载下一组。

如果加载速度比用户滚动速度慢,那么您会为此类“尚未加载的图片”显示默认的占位符图像

您可以缩小所有图像以使用更少的 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);
    }
}

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM