我使用以下语句将图像像素保存在字节数组中:
bytePixels = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData();
现在我需要构建一个缩小版的图像,并使用此字节数组将其放入ImageIcon中。请注意,原始图像可能很大,因此构建一个像这样的新BufferedImage:
public Icon getImageIcon(int newWidth, int newHeight) throws IOException {
BufferedImage image = ImageIO.read(originalImageFile);
BufferedImage resizedImage = new BufferedImage(newWidth, newHeight, this.getType());
Graphics2D g = resizedImage.createGraphics();
g.drawImage(image, 0, 0, newWidth, newHeight, null);
g.dispose();
return new ImageIcon(resizedImage);
}
不是一个选项,因为如果原始图像很大,它很可能会导致OutOfMemoryError。
所以我的问题是:使用字节数组中的原始像素,获得更小尺寸的ImageIcon的最有效方法(以内存方式)是什么?
答案 0 :(得分:1)
OutOfMemory错误,这很奇怪。默认的JVM堆大小非常大。最有可能的是,它超过100 MB。一个缓冲的图像,100 MB的每像素4个字节将是:
100 000 000 = x * 4 bytes
x = 25 000 000 pixels
这意味着你正在使用近5000 * 5000像素的图像。尝试使用此命令行参数扩大最大堆大小:
java -XmX400M YourClass
另外,我不会使用字节数组存储像素。但只是作为BufferedImage,这样你就可以确定图像数据的内存不是两倍。
答案 1 :(得分:1)
使用Scaled Image
与意图缩小像素大小,然后在GUI中使用它们之前,您可以使用Image#getScaledInstance(int width, int height, int hints),查找您查找的实际代码Java Advanced Imaging (JAI)
获取OutOfMemoryError
没问题,您可以使用此代码测试JVM
和/或JProfiler
中的个人资料的效果
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import java.io.ByteArrayOutputStream;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
class ImageCacheTest {
private JLabel imageLabel;
private Dimension halfScreenSize;
private Random random;
private JProgressBar memory;
private Font bigFont = new Font("Arial", Font.BOLD, 30);
private int count = 0;
private int startMem = 0;
private int maxMem = 0;
private int peakMem = 0;
private int useMem = 0;
ImageCacheTest() {
startMem = ((int) Runtime.getRuntime().freeMemory());
maxMem = ((int) Runtime.getRuntime().freeMemory());
peakMem = ((int) Runtime.getRuntime().freeMemory());
JPanel p = new JPanel(new BorderLayout(4, 4));
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
halfScreenSize = new Dimension(d.width / 2, d.height / 2);
//halfScreenSize = new Dimension(d.width - 11, d.height - 101);
//halfScreenSize = new Dimension(4000, 3000);
random = new Random();
imageLabel = new JLabel(new ImageIcon(convertToFromBytes(getImage())));
memory = new JProgressBar(0, (int) Runtime.getRuntime().maxMemory());
memory.setPreferredSize(new Dimension(200, 30));
p.add(imageLabel, BorderLayout.CENTER);
p.add(memory, BorderLayout.SOUTH);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(p);
f.pack();
f.setVisible(true);
Runnable r = new Runnable() {
@Override
public void run() {
while (true) {
try {
imageLabel.setIcon(new ImageIcon(convertToFromBytes(getImage())));
memory.setValue((int) Runtime.getRuntime().freeMemory());
useMem = ((int) Runtime.getRuntime().freeMemory());
Thread.sleep(300);
} catch (InterruptedException ex) {
Logger.getLogger(ImageCacheTest.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
};
Thread t = new Thread(r);
t.start();
}
public BufferedImage getImage() {
GradientPaint gp = new GradientPaint(0f, 0f, new Color(127 + random.nextInt(128), 127 + random.nextInt(128), 127 + random.nextInt(128)),
(float) halfScreenSize.width, (float) halfScreenSize.width, new Color(random.nextInt(128), random.nextInt(128), random.nextInt(128)));
BufferedImage bi = new BufferedImage(halfScreenSize.width, halfScreenSize.height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bi.createGraphics();
g2d.setPaint(gp);
g2d.fillRect(0, 0, halfScreenSize.width, halfScreenSize.height);
g2d.setFont(bigFont);
g2d.setColor(Color.BLACK);
if (maxMem < ((int) Runtime.getRuntime().freeMemory())) {
maxMem = ((int) Runtime.getRuntime().freeMemory());
}
if (peakMem > ((int) Runtime.getRuntime().freeMemory())) {
peakMem = ((int) Runtime.getRuntime().freeMemory());
}
useMem = ((int) Runtime.getRuntime().freeMemory()) - useMem;
g2d.drawString("" + ++count, 20, 100);
g2d.drawString("JVM memory status ---> ", 20, 195);
g2d.drawString("tot. memory ---> " + ((int) Runtime.getRuntime().totalMemory()), 20, 240);
g2d.drawString("max. memory ---> " + ((int) Runtime.getRuntime().maxMemory()), 20, 270);
g2d.drawString("free on startUp ---> " + startMem, 20, 300);
g2d.drawString("max free memory ---> " + maxMem, 20, 350);
g2d.drawString("min free memory ---> " + peakMem, 20, 380);
g2d.drawString("act free memory ---> " + ((int) Runtime.getRuntime().freeMemory()), 20, 410);
g2d.drawString("usage of memory ---> " + useMem, 20, 450);
return bi;
}
/** Not entirely sure this method is necessary for indicating 'no cache',
but since the claim was specific to byte arrays, we'll do it. */
public Image convertToFromBytes(BufferedImage image) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos);
return Toolkit.getDefaultToolkit().createImage(baos.toByteArray());
} catch (Exception e) {
return null;
}
}
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
ImageCacheTest ict = new ImageCacheTest();
}
};
SwingUtilities.invokeLater(r);
}
}