简体   繁体   English

处理海量大jpg

[英]Processing huge amount of large jpgs

I am writing a program partly just for fun, partly to help me deal with a heap of digital pictures that I want to divide into categories for printing.我正在编写一个程序,部分只是为了好玩,部分是为了帮助我处理一堆我想分成几类进行打印的数字图片。 The main idea is that it should display the pictures in a single column and have a set of checkboxes, with category names, next to each picture.主要思想是它应该在单个列中显示图片,并在每张图片旁边有一组带有类别名称的复选框。 I check the needed checkboxes, press the "go!"我选中所需的复选框,按“开始!” button, and the pictures get copied into subfolders, depending on the selected checkboxes.按钮,图片将被复制到子文件夹中,具体取决于所选的复选框。

Now, everything is almost finished - except for one thing.现在,一切都差不多完成了——除了一件事。 The pictures in question are large jpgs, each about 7-8MB, and there's approximately 700 of them.有问题的图片是大jpg,每个大约7-8MB,大约有700张。 If I try to load them all at once, naturally, it takes a huge amount of memory and time to load them all.如果我尝试一次加载它们,自然地,将它们全部加载需要大量内存和时间。 So, is there a good solution to the problem?那么,有没有好的解决办法呢? My two thoughts were as follows.我的两个想法如下。

1) To load the pics 10 at a time and have next/previous buttons somewhere. 1) 一次加载 10 张图片并在某处设置下一个/上一个按钮。 I don't like this idea, as it adds unneeded elements.我不喜欢这个想法,因为它添加了不需要的元素。 2) To make the application load new pictures as you scroll to them and unload those you scrolled past. 2) 使应用程序在滚动到新图片时加载新图片并卸载滚动过去的图片。 I really like this idea.我真的喜欢这个主意。

Can someone point me in the right direction, as to how I can implement the latter idea?有人可以指出我正确的方向,至于我如何实施后一个想法吗? I have only found one relevant link on Google, but I cannot say that it helped me, i got a bit confused by some parts of the code.我只在 Google 上找到了一个相关链接,但我不能说它对我有帮助,我对代码的某些部分感到有些困惑。

If thumbnails are sufficient, this answer includes a simple approach to resampling and cites some trade-offs.如果缩略图就足够了,这个答案包括一种简单的重新采样方法并引用了一些权衡。 If not, this answer outlines a general approach to displaying and caching recent images.如果没有,此答案概述了显示和缓存最近图像的一般方法。

In either case, the default Boolean renderer/editor of JTable is a JCheckBox .在任何一种情况下, JTable的默认Boolean 渲染器/编辑器都是JCheckBox CheckOne is an example. CheckOne就是一个例子。

You have to create thumnails for all pictures, you may keep the thumnails in memory.您必须为所有图片创建缩略图,您可以将缩略图保存在内存中。 That may need a lot of time.那可能需要很多时间。

Then you are either ready with the pronblem.那么你要么准备好解决问题。 or the thumbnail will not fit all in memory.否则缩略图将无法全部放入内存中。 If that is the case: you load 30-40 of them, and during scrolling you detct the scroll direction, and load the next bunch in a separate thread.如果是这种情况:您加载 30-40 个,并在滚动期间检测滚动方向,然后在单独的线程中加载下一组。

If the loading is slower than the user scrolls, then you dispaly a default place holder image for such a "not yet loaded pic"如果加载速度比用户滚动速度慢,那么您会为此类“尚未加载的图片”显示默认的占位符图像

You can shrink down all your images to use less RAM.您可以缩小所有图像以使用更少的 RAM。 eg this code reduces all images to 200x200.例如,此代码将所有图像缩小到 200x200。 That way you can fit 1000 images in 100MB.这样你就可以在 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);
    }
}

If you want to dynamically load images as they become visible you have to use empty JPanels for each image instead of ImageIcons and then you override the paint method of those JPanels.如果您想在图像变得可见时动态加载它们,您必须为每个图像使用空的 JPanel 而不是 ImageIcons,然后覆盖这些 JPanel 的paint方法。 The paint method will only be called if the JPanel is visible.只有在 JPanel 可见时才会调用paint方法。 So the simplest solution would be to always load the image from disk in the paint method and then draw it to the screen.因此,最简单的解决方案是始终在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);
    }
}

If this is a "use once and throw away" kind-of program I wouldn't bother trying to solve this problem.如果这是一个“使用一次就扔掉”的程序,我不会费心去解决这个问题。 I would use the command line to generate thumbnails for each of the pictures, and use those generated thumbnails in my program.我会使用命令行为每张图片生成缩略图,并在我的程序中使用这些生成的缩略图。 Keeping 700 thumbnails in memory should be doable.在内存中保留 700 个缩略图应该是可行的。

If that is not an option, I would just start with a JTable .如果这不是一个选项,我会从JTable开始。 You can create a TableModel without having to load all the images into memory.您可以创建一个TableModel而不必将所有图像加载到内存中。 You just need to know the image count.您只需要知道图像计数。 The JTable will only want to render the images which are visible at that moment. JTable只希望渲染当时可见的图像。 Caveat might be that loading the images at the moment the renderer asks for them might be too late.警告可能是在渲染器要求它们的那一刻加载图像可能为时已晚。 I can imagine that the JTable will not run smoothly when you have to load images with a size of a few MB's.我可以想象,当您必须加载几 MB 大小的图像时, JTable将无法顺利运行。 But this can probably be solved by using a cache which gets populated with a worker thread.但这可能可以通过使用填充有工作线程的缓存来解决。 So if the renderer request the 5th image, you also load the 5 previous images and the 5 next images into the cache.因此,如果渲染器请求第 5 个图像,您还将前 5 个图像和 5 个下一个图像加载到缓存中。

Also note that if you are moving the pictures iso copying them, this might affect your TableModel as the number of images in your directory will change.另请注意,如果您正在移动图片 iso 复制它们,这可能会影响您的TableModel因为您目录中的图像数量将发生变化。 Make sure to consider that !一定要考虑到这一点!

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

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