[英]Improving performance when processing images in Java
我正在編寫一個程序,其中包括一個圖像文件夾(通常約2000個jpeg圖像)調整它們的大小,並將它們添加到圖像的時間軸。 結果如下:
這樣做很好,但是我這樣做的方式似乎非常低效。 處理這些圖像的代碼如下所示:
public void setTimeline(Vector<String> imagePaths){
int numberOfImages = imagePaths.size();
JLabel [] TotalImages = new JLabel[numberOfImages];
setGridPanel.setLayout(new GridLayout(1, numberOfImages, 10, 0));
Dimension image = new Dimension(96, 72);
if (imagePaths != null){
for(int i = 0; i <numberOfImages; i++){
TotalImages[i] = new JLabel("");
TotalImages[i].setPreferredSize(image);
ImageIcon tempicon = new ImageIcon(imagePaths.elementAt(i));
Image tempimage = tempicon.getImage();
Image newimg = tempimage.getScaledInstance(96, 72, java.awt.Image.SCALE_SMOOTH);
ImageIcon newIcon = new ImageIcon(newimg);
TotalImages[i].setIcon(newIcon);
setGridPanel.add(TotalImages[i]);
}
}
}
可以看出,此代碼循環遍歷每個圖像路徑,將其添加到標簽並將其添加到面板 - 完全按照正確的輸出執行。
但是,這樣做的時間很長。 對於2000張圖像通常大約需要5分鍾(取決於機器)。 我想知道是否有任何方法可以通過使用不同的技術來改善這種性能?
任何幫助是極大的贊賞。
保存縮放的實例並直接加載它們。 硬盤空間很便宜。 這不會影響生成拇指的初始成本,但任何后續出現都將是閃電般快速的。
拿一個圖像文件夾
使用tempimage.getScaledInstance(96, 72, java.awt.Image.SCALE_SMOOTH);
進程tempimage.getScaledInstance(96, 72, java.awt.Image.SCALE_SMOOTH);
JTable
,功能降低,你也可以使用JList
對於2000張圖像通常約為5分鍾
Image.getScaledInstance
是簡單的異步,確保快速和性能,然后你必須將圖像加載到Background任務
優勢第一部分圖像可立即使用
dis_advantage需要為用戶提供不正確的加載狀態,非常了解Swing
和Event Dispatch Thread
我建議看一下Runnable#Thread
,並輸出put到DefaultTableModel
,注意這個輸出必須包裝到invokeLater
另一個也是最復雜的方法是使用SwingWorker
,但也需要非常好的Java
和Swing
知識
要添加到mKorbel的優秀答案,我肯定會使用后台線程,例如SwingWorker。 這可能不會使程序更快,但它看起來會快得多,而且可以產生重大影響。 就像是:
// use List<String> not Vector<String> so you can use Vector now, or change your
// mind and use ArrayList later if desired
// pass dimensions and components in as parameters to be cleaner
public void setTimeLine2(List<String> imagePaths, Dimension imgSize,
JComponent imgDisplayer) {
if (imagePaths != null && imgSize != null && imgDisplayer != null) {
// are you sure you want to set the layout in here?
imgDisplayer.setLayout(new GridLayout(1, 0, 10, 0));
// create your SwingWorker, passing in parameters that it will need
ImageWorker imgWorker = new ImageWorker(imagePaths, imgSize,
imgDisplayer);
imgWorker.execute(); // then ask it to run doInBackground on a background thread
} else {
// TODO: throw exception
}
}
private class ImageWorker extends SwingWorker<Void, ImageIcon> {
private List<String> imagePaths;
private JComponent imgDisplayer;
private int imgWidth;
private int imgHeight;
public ImageWorker(List<String> imagePaths, Dimension imgSize,
JComponent imgDisplayer) {
this.imagePaths = imagePaths;
this.imgDisplayer = imgDisplayer;
imgWidth = imgSize.width;
imgHeight = imgSize.height;
}
// do image creation in a background thread so as not to lock the Swing event thread
@Override
protected Void doInBackground() throws Exception {
for (String imagePath : imagePaths) {
BufferedImage bImg = ImageIO.read(new File(imagePath));
Image scaledImg = bImg.getScaledInstance(imgWidth, imgHeight,
Image.SCALE_SMOOTH);
ImageIcon icon = new ImageIcon(scaledImg);
publish(icon);
}
return null;
}
// but do all Swing manipulation on the event thread
@Override
protected void process(List<ImageIcon> chunks) {
for (ImageIcon icon : chunks) {
JLabel label = new JLabel(icon);
imgDisplayer.add(label);
}
}
}
使用瓷磚。 這意味着,除了操作未在屏幕上顯示的圖像之外,您只能在必須在屏幕上顯示圖像時進行操作。
您需要保持時間軸的邏輯位置以及顯示的圖像。 當用戶將光標移動到先前隱藏的位置時,您可以計算下一個需要顯示的圖像。 如果圖像尚未處理,則處理它們。 這與Web瀏覽器用於性能的技術相同。
您可以做的第一件事是異步添加圖像,而不是嘗試一次添加所有圖像。 像你一樣循環遍歷它們,將它們添加到面板並每隔幾張圖像渲染一次,這樣用戶就不需要等待很長的初始化時間。
重用圖像對象。 我們會想到一個flyweight模式,並且可能會將屏幕重繪限制為僅在異步加載中添加新圖像的部分。
如果您將來可能會重新繪制相同的圖像(或重新加載相同的文件夾),您可能需要考慮緩存某些圖像對象,並可能將已調整大小的縮略圖保存到文件中(許多照片查看器會執行此操作,將隱藏文件或文件夾中的縮略圖版本和一些有用的元數據存儲起來,這樣他們就可以在下次更快地重新加載它們。
你可以做些什么來使它更快是通過制作4個線程,並讓他們同時計算圖像。 我不知道vm是否會將它們分散在多個cpu核心上。 需要考慮的因為這會增加多核PC上的perfotrmace
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.