Как повысить производительность метода g.drawImage() для изменения размеров изображений
У меня есть приложение, в котором пользователи могут загружать фотографии в альбомы, но, естественно, загружаемые изображения необходимо изменить, чтобы также были доступны большие пальцы, а показанные изображения также попали на страницу (например, 800x600).
Способ изменения размера выглядит следующим образом:
Image scaledImage = img.getScaledInstance((int)width, (int)height, Image.SCALE_SMOOTH);
BufferedImage imageBuff = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_RGB);
Graphics g = imageBuff.createGraphics();
g.drawImage(scaledImage, 0, 0, new Color(0,0,0), null);
g.dispose();
И он работает нормально. Моя единственная проблема заключается в том, что метод g.drawImage()
кажется ужасно медленным, и я просто не могу представить, чтобы пользователь был достаточно терпелив, чтобы дождаться загрузки 20 изображений 20 * 10 секунд ~ 3 минуты. Фактически, на моем компьютере требуется около 40 секунд для создания трех разных размеров для одной картинки.
Это не очень хорошо, и я ищу более быстрое решение. Мне интересно, может ли кто-нибудь рассказать мне о лучшем в Java OR, вызвав команду script, команду, какой бы хак вы не знали, это должно быть быстрее, все остальное не имеет значения на этот раз.
Ответы
Ответ 1
Вы можете использовать ImageMagick для создавать эскизы,
convert -define jpeg:size=500x180 hatching_orig.jpg -auto-orient \
-thumbnail 250x90 -unsharp 0x.5 thumbnail.gif
Чтобы использовать его с Java, вы можете попробовать JMagick, который предоставляет интерфейс Java (JNI) для ImageMagick. Или вы можете просто вызвать команды ImageMagick напрямую, используя Runtime.exec
или ProcessBuilder
.
Ответ 2
Я использую код, похожий на следующий, для масштабирования изображений, я удалил часть, которая имеет дело с сохранением соотношения сторон. Производительность была определенно лучше, чем 10 с на изображение, но я не помню никаких точных цифр. Чтобы архивировать лучшее качество при масштабировании, вы должны масштабироваться в несколько этапов, если исходное изображение более чем в два раза превышает размер требуемого эскиза, каждый шаг должен масштабировать предыдущее изображение примерно до половины его размера.
public static BufferedImage getScaledImage(BufferedImage image, int width, int height) throws IOException {
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
double scaleX = (double)width/imageWidth;
double scaleY = (double)height/imageHeight;
AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY);
AffineTransformOp bilinearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR);
return bilinearScaleOp.filter(
image,
new BufferedImage(width, height, image.getType()));
}
Ответ 3
Вам действительно нужно качество, предоставляемое с помощью Image.SCALE_SMOOTH? Если вы этого не сделаете, вы можете попробовать Image.SCALE_FAST. Вы можете найти эту статью полезной, если хотите придерживаться чего-то, предоставляемого Java.
Ответ 4
Ну, мы с Джейкобом хотели изменить размер изображения, а не BufferedImage. Итак, мы закончили с этим кодом:
/**
* we want the x and o to be resized when the JFrame is resized
*
* @param originalImage an x or an o. Use cross or oh fields.
*
* @param biggerWidth
* @param biggerHeight
*/
private Image resizeToBig(Image originalImage, int biggerWidth, int biggerHeight) {
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage resizedImage = new BufferedImage(biggerWidth, biggerHeight, type);
Graphics2D g = resizedImage.createGraphics();
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(originalImage, 0, 0, biggerWidth, biggerHeight, this);
g.dispose();
return resizedImage;
}
Ответ 5
Основная проблема заключалась в производительности масштабирования изображений в Java. Другие ответы показали разные подходы, не оценивая их дальше. Мне также было интересно об этом, поэтому я попытался написать небольшой тест производительности. Тем не менее, тестирование масштабирования изображения надежно, разумно и объективно сложно. Слишком много факторов, которые необходимо учитывать:
- Размер входного изображения
- Размер выходного изображения
- Интерполяция (т.е. "качество": ближайший сосед, билинейный, бикубический)
-
BufferedImage.TYPE_*
входного изображения
-
BufferedImage.TYPE_*
выходного изображения
- Версия JVM и операционная система
- Наконец: метод, который фактически используется для выполнения операции.
Я попытался охватить те, которые я считал самыми важными. Настройка была:
-
Вход представляет собой простую "среднюю" фотографию (в частности, это "изображение дня" из Википедии, с размером 2560x1706 пикселей)
-
Проверяются основные типы интерполяции, а именно, используя RenderingHints
, где для ключа INTERPOLATION
были установлены значения NEAREST_NEIGHBOR
, BILINEAR
и BICUBIC
-
Входное изображение было преобразовано в разные типы:
-
BufferedImage.TYPE_INT_RGB
: тип, который обычно используется, поскольку он "обычно" показывает лучшие характеристики производительности
-
BufferedImage.TYPE_3BTE_BGR
: Это тип, который он читает по умолчанию, когда просто читал его с помощью ImageIO
-
Размер целевого изображения варьировался между шириной 10000 (таким образом, масштабированием изображения вверх) и 100 (таким образом, масштабируя изображение до размера миниатюр)
Тесты выполнялись на Win64/AMD K10 с 3.7 ГГц и JDK 1.8u31, с -Xmx4000m -server
.
Проверенные методы:
- Использование
AffineTransformOp
, как в ответе Йорна Хорстмана
- Используя
Graphics
, как в ответе от johnstosh
- Использование
Image#getScaledInstance
Код тестов показан здесь:
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.Supplier;
import javax.imageio.ImageIO;
import javax.swing.JLabel;
public class ImageScalingPerformance
{
private static int blackHole = 0;
public static void main(String[] args) throws IOException
{
// Image with size 2560 x 1706, from https://upload.wikimedia.org/
// wikipedia/commons/4/41/Pitta_moluccensis_-_Kaeng_Krachan.jpg
BufferedImage image = ImageIO.read(
new File("Pitta_moluccensis_-_Kaeng_Krachan.jpg"));
int types[] =
{
BufferedImage.TYPE_3BYTE_BGR,
BufferedImage.TYPE_INT_RGB,
};
Object interpolationValues[] =
{
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR,
RenderingHints.VALUE_INTERPOLATION_BILINEAR,
RenderingHints.VALUE_INTERPOLATION_BICUBIC,
};
int widths[] =
{
10000, 5000, 2500, 1000, 500, 100
};
System.out.printf("%10s%22s%6s%18s%10s\n",
"Image type", "Interpolation", "Size", "Method", "Duration (ms)");
for (int type : types)
{
BufferedImage currentImage = convert(image, type);
for (Object interpolationValue : interpolationValues)
{
for (int width : widths)
{
List<Supplier<Image>> tests =
createTests(currentImage, interpolationValue, width);
for (Supplier<Image> test : tests)
{
double durationMs = computeMs(test);
System.out.printf("%10s%22s%6s%18s%10s\n",
stringForBufferedImageType(type),
stringForInterpolationValue(interpolationValue),
String.valueOf(width),
String.valueOf(test),
String.format(Locale.ENGLISH, "%6.3f", durationMs));
}
}
}
}
System.out.println(blackHole);
}
private static List<Supplier<Image>> createTests(
BufferedImage image, Object interpolationValue, int width)
{
RenderingHints renderingHints = new RenderingHints(null);
renderingHints.put(
RenderingHints.KEY_INTERPOLATION,
interpolationValue);
double scale = (double) width / image.getWidth();
int height = (int)(scale * image.getHeight());
Supplier<Image> s0 = new Supplier<Image>()
{
@Override
public BufferedImage get()
{
return scaleWithAffineTransformOp(
image, width, height, renderingHints);
}
@Override
public String toString()
{
return "AffineTransformOp";
}
};
Supplier<Image> s1 = new Supplier<Image>()
{
@Override
public Image get()
{
return scaleWithGraphics(
image, width, height, renderingHints);
}
@Override
public String toString()
{
return "Graphics";
}
};
Supplier<Image> s2 = new Supplier<Image>()
{
@Override
public Image get()
{
return scaleWithGetScaledInstance(
image, width, height, renderingHints);
}
@Override
public String toString()
{
return "GetScaledInstance";
}
};
List<Supplier<Image>> tests = new ArrayList<Supplier<Image>>();
tests.add(s0);
tests.add(s1);
tests.add(s2);
return tests;
}
private static double computeMs(Supplier<Image> supplier)
{
int runs = 5;
long before = System.nanoTime();
for (int i=0; i<runs; i++)
{
Image image0 = supplier.get();
blackHole += image0.hashCode();
}
long after = System.nanoTime();
double durationMs = (after-before) / 1e6 / runs;
return durationMs;
}
private static BufferedImage convert(BufferedImage image, int type)
{
BufferedImage newImage = new BufferedImage(
image.getWidth(), image.getHeight(), type);
Graphics2D g = newImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}
private static BufferedImage scaleWithAffineTransformOp(
BufferedImage image, int w, int h,
RenderingHints renderingHints)
{
BufferedImage scaledImage = new BufferedImage(w, h, image.getType());
double scaleX = (double) w / image.getWidth();
double scaleY = (double) h / image.getHeight();
AffineTransform affineTransform =
AffineTransform.getScaleInstance(scaleX, scaleY);
AffineTransformOp affineTransformOp = new AffineTransformOp(
affineTransform, renderingHints);
return affineTransformOp.filter(
image, scaledImage);
}
private static BufferedImage scaleWithGraphics(
BufferedImage image, int w, int h,
RenderingHints renderingHints)
{
BufferedImage scaledImage = new BufferedImage(w, h, image.getType());
Graphics2D g = scaledImage.createGraphics();
g.setRenderingHints(renderingHints);
g.drawImage(image, 0, 0, w, h, null);
g.dispose();
return scaledImage;
}
private static Image scaleWithGetScaledInstance(
BufferedImage image, int w, int h,
RenderingHints renderingHints)
{
int hint = Image.SCALE_REPLICATE;
if (renderingHints.get(RenderingHints.KEY_ALPHA_INTERPOLATION) !=
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)
{
hint = Image.SCALE_AREA_AVERAGING;
}
Image scaledImage = image.getScaledInstance(w, h, hint);
MediaTracker mediaTracker = new MediaTracker(new JLabel());
mediaTracker.addImage(scaledImage, 0);
try
{
mediaTracker.waitForAll();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
return scaledImage;
}
private static String stringForBufferedImageType(int type)
{
switch (type)
{
case BufferedImage.TYPE_INT_RGB : return "INT_RGB";
case BufferedImage.TYPE_INT_ARGB : return "INT_ARGB";
case BufferedImage.TYPE_INT_ARGB_PRE : return "INT_ARGB_PRE";
case BufferedImage.TYPE_INT_BGR : return "INT_BGR";
case BufferedImage.TYPE_3BYTE_BGR : return "3BYTE_BGR";
case BufferedImage.TYPE_4BYTE_ABGR : return "4BYTE_ABGR";
case BufferedImage.TYPE_4BYTE_ABGR_PRE : return "4BYTE_ABGR_PRE";
case BufferedImage.TYPE_USHORT_565_RGB : return "USHORT_565_RGB";
case BufferedImage.TYPE_USHORT_555_RGB : return "USHORT_555_RGB";
case BufferedImage.TYPE_BYTE_GRAY : return "BYTE_GRAY";
case BufferedImage.TYPE_USHORT_GRAY : return "USHORT_GRAY";
case BufferedImage.TYPE_BYTE_BINARY : return "BYTE_BINARY";
case BufferedImage.TYPE_BYTE_INDEXED : return "BYTE_INDEXED";
}
return "CUSTOM";
}
private static String stringForInterpolationValue(Object value)
{
if (value == RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)
{
return "NEAREST/REPLICATE";
}
if (value == RenderingHints.VALUE_INTERPOLATION_BILINEAR)
{
return "BILINEAR/AREA_AVG";
}
if (value == RenderingHints.VALUE_INTERPOLATION_BICUBIC)
{
return "BICUBIC/AREA_AVG";
}
return "(unknown)";
}
}
Во-первых, относительно getScaledInstance
: Как сказал Крис Кэмпбелл в своей (известной) статье о The Perils of Image.getScaledInstance() (что уже было связано с другими ответами), метод Image#getScaledInstance
несколько разбит и имеет большую неприятную производительность для большинства конфигураций. Кроме того, он имеет недостаток в том, что он не имеет такого мелкозернистого контроля над типом интерполяции. Это следует учитывать при следующем сравнении производительности. Качество полученных изображений может отличаться, что не рассматривается здесь. Например, метод "усреднения области" getScaledInstance
не дает хорошего качества изображения при увеличении размера изображения.
(Самый серьезный недостаток Image#getScaledInstance
- это ИМХО, что он поставляет только Image
, а не a BufferedImage
, но если изображение должно быть расписано только в Graphics
, это может быть неважно)
Я просто выгружу вывод программы здесь для справки, некоторые подробности будут приведены ниже:
Image type Interpolation Size MethodDuration (ms)
3BYTE_BGR NEAREST/REPLICATE 10000 AffineTransformOp 197.287
3BYTE_BGR NEAREST/REPLICATE 10000 Graphics 184.427
3BYTE_BGR NEAREST/REPLICATE 10000 GetScaledInstance 1869.759
3BYTE_BGR NEAREST/REPLICATE 5000 AffineTransformOp 38.354
3BYTE_BGR NEAREST/REPLICATE 5000 Graphics 40.220
3BYTE_BGR NEAREST/REPLICATE 5000 GetScaledInstance 1088.448
3BYTE_BGR NEAREST/REPLICATE 2500 AffineTransformOp 10.153
3BYTE_BGR NEAREST/REPLICATE 2500 Graphics 9.461
3BYTE_BGR NEAREST/REPLICATE 2500 GetScaledInstance 613.030
3BYTE_BGR NEAREST/REPLICATE 1000 AffineTransformOp 2.137
3BYTE_BGR NEAREST/REPLICATE 1000 Graphics 1.956
3BYTE_BGR NEAREST/REPLICATE 1000 GetScaledInstance 464.989
3BYTE_BGR NEAREST/REPLICATE 500 AffineTransformOp 0.861
3BYTE_BGR NEAREST/REPLICATE 500 Graphics 0.750
3BYTE_BGR NEAREST/REPLICATE 500 GetScaledInstance 407.751
3BYTE_BGR NEAREST/REPLICATE 100 AffineTransformOp 0.206
3BYTE_BGR NEAREST/REPLICATE 100 Graphics 0.153
3BYTE_BGR NEAREST/REPLICATE 100 GetScaledInstance 385.863
3BYTE_BGR BILINEAR/AREA_AVG 10000 AffineTransformOp 830.097
3BYTE_BGR BILINEAR/AREA_AVG 10000 Graphics 1501.290
3BYTE_BGR BILINEAR/AREA_AVG 10000 GetScaledInstance 1627.934
3BYTE_BGR BILINEAR/AREA_AVG 5000 AffineTransformOp 207.816
3BYTE_BGR BILINEAR/AREA_AVG 5000 Graphics 376.789
3BYTE_BGR BILINEAR/AREA_AVG 5000 GetScaledInstance 1063.942
3BYTE_BGR BILINEAR/AREA_AVG 2500 AffineTransformOp 52.362
3BYTE_BGR BILINEAR/AREA_AVG 2500 Graphics 95.041
3BYTE_BGR BILINEAR/AREA_AVG 2500 GetScaledInstance 612.660
3BYTE_BGR BILINEAR/AREA_AVG 1000 AffineTransformOp 9.121
3BYTE_BGR BILINEAR/AREA_AVG 1000 Graphics 15.749
3BYTE_BGR BILINEAR/AREA_AVG 1000 GetScaledInstance 452.578
3BYTE_BGR BILINEAR/AREA_AVG 500 AffineTransformOp 2.593
3BYTE_BGR BILINEAR/AREA_AVG 500 Graphics 4.237
3BYTE_BGR BILINEAR/AREA_AVG 500 GetScaledInstance 407.661
3BYTE_BGR BILINEAR/AREA_AVG 100 AffineTransformOp 0.275
3BYTE_BGR BILINEAR/AREA_AVG 100 Graphics 0.297
3BYTE_BGR BILINEAR/AREA_AVG 100 GetScaledInstance 381.835
3BYTE_BGR BICUBIC/AREA_AVG 10000 AffineTransformOp 3015.943
3BYTE_BGR BICUBIC/AREA_AVG 10000 Graphics 5431.703
3BYTE_BGR BICUBIC/AREA_AVG 10000 GetScaledInstance 1654.424
3BYTE_BGR BICUBIC/AREA_AVG 5000 AffineTransformOp 756.136
3BYTE_BGR BICUBIC/AREA_AVG 5000 Graphics 1359.288
3BYTE_BGR BICUBIC/AREA_AVG 5000 GetScaledInstance 1063.467
3BYTE_BGR BICUBIC/AREA_AVG 2500 AffineTransformOp 189.953
3BYTE_BGR BICUBIC/AREA_AVG 2500 Graphics 341.039
3BYTE_BGR BICUBIC/AREA_AVG 2500 GetScaledInstance 615.807
3BYTE_BGR BICUBIC/AREA_AVG 1000 AffineTransformOp 31.351
3BYTE_BGR BICUBIC/AREA_AVG 1000 Graphics 55.914
3BYTE_BGR BICUBIC/AREA_AVG 1000 GetScaledInstance 451.808
3BYTE_BGR BICUBIC/AREA_AVG 500 AffineTransformOp 8.422
3BYTE_BGR BICUBIC/AREA_AVG 500 Graphics 15.028
3BYTE_BGR BICUBIC/AREA_AVG 500 GetScaledInstance 408.626
3BYTE_BGR BICUBIC/AREA_AVG 100 AffineTransformOp 0.703
3BYTE_BGR BICUBIC/AREA_AVG 100 Graphics 0.825
3BYTE_BGR BICUBIC/AREA_AVG 100 GetScaledInstance 382.610
INT_RGB NEAREST/REPLICATE 10000 AffineTransformOp 330.445
INT_RGB NEAREST/REPLICATE 10000 Graphics 114.656
INT_RGB NEAREST/REPLICATE 10000 GetScaledInstance 2784.542
INT_RGB NEAREST/REPLICATE 5000 AffineTransformOp 83.081
INT_RGB NEAREST/REPLICATE 5000 Graphics 29.148
INT_RGB NEAREST/REPLICATE 5000 GetScaledInstance 1117.136
INT_RGB NEAREST/REPLICATE 2500 AffineTransformOp 22.296
INT_RGB NEAREST/REPLICATE 2500 Graphics 7.735
INT_RGB NEAREST/REPLICATE 2500 GetScaledInstance 436.779
INT_RGB NEAREST/REPLICATE 1000 AffineTransformOp 3.859
INT_RGB NEAREST/REPLICATE 1000 Graphics 2.542
INT_RGB NEAREST/REPLICATE 1000 GetScaledInstance 205.863
INT_RGB NEAREST/REPLICATE 500 AffineTransformOp 1.413
INT_RGB NEAREST/REPLICATE 500 Graphics 0.963
INT_RGB NEAREST/REPLICATE 500 GetScaledInstance 156.537
INT_RGB NEAREST/REPLICATE 100 AffineTransformOp 0.160
INT_RGB NEAREST/REPLICATE 100 Graphics 0.074
INT_RGB NEAREST/REPLICATE 100 GetScaledInstance 126.159
INT_RGB BILINEAR/AREA_AVG 10000 AffineTransformOp 1019.438
INT_RGB BILINEAR/AREA_AVG 10000 Graphics 1230.621
INT_RGB BILINEAR/AREA_AVG 10000 GetScaledInstance 2721.918
INT_RGB BILINEAR/AREA_AVG 5000 AffineTransformOp 254.616
INT_RGB BILINEAR/AREA_AVG 5000 Graphics 308.374
INT_RGB BILINEAR/AREA_AVG 5000 GetScaledInstance 1269.898
INT_RGB BILINEAR/AREA_AVG 2500 AffineTransformOp 68.137
INT_RGB BILINEAR/AREA_AVG 2500 Graphics 80.163
INT_RGB BILINEAR/AREA_AVG 2500 GetScaledInstance 444.968
INT_RGB BILINEAR/AREA_AVG 1000 AffineTransformOp 13.093
INT_RGB BILINEAR/AREA_AVG 1000 Graphics 15.396
INT_RGB BILINEAR/AREA_AVG 1000 GetScaledInstance 211.929
INT_RGB BILINEAR/AREA_AVG 500 AffineTransformOp 3.238
INT_RGB BILINEAR/AREA_AVG 500 Graphics 3.689
INT_RGB BILINEAR/AREA_AVG 500 GetScaledInstance 159.688
INT_RGB BILINEAR/AREA_AVG 100 AffineTransformOp 0.329
INT_RGB BILINEAR/AREA_AVG 100 Graphics 0.277
INT_RGB BILINEAR/AREA_AVG 100 GetScaledInstance 127.905
INT_RGB BICUBIC/AREA_AVG 10000 AffineTransformOp 4211.287
INT_RGB BICUBIC/AREA_AVG 10000 Graphics 4712.587
INT_RGB BICUBIC/AREA_AVG 10000 GetScaledInstance 2830.749
INT_RGB BICUBIC/AREA_AVG 5000 AffineTransformOp 1069.088
INT_RGB BICUBIC/AREA_AVG 5000 Graphics 1182.285
INT_RGB BICUBIC/AREA_AVG 5000 GetScaledInstance 1155.663
INT_RGB BICUBIC/AREA_AVG 2500 AffineTransformOp 263.003
INT_RGB BICUBIC/AREA_AVG 2500 Graphics 297.663
INT_RGB BICUBIC/AREA_AVG 2500 GetScaledInstance 444.497
INT_RGB BICUBIC/AREA_AVG 1000 AffineTransformOp 42.841
INT_RGB BICUBIC/AREA_AVG 1000 Graphics 48.605
INT_RGB BICUBIC/AREA_AVG 1000 GetScaledInstance 209.261
INT_RGB BICUBIC/AREA_AVG 500 AffineTransformOp 11.004
INT_RGB BICUBIC/AREA_AVG 500 Graphics 12.407
INT_RGB BICUBIC/AREA_AVG 500 GetScaledInstance 156.794
INT_RGB BICUBIC/AREA_AVG 100 AffineTransformOp 0.817
INT_RGB BICUBIC/AREA_AVG 100 Graphics 0.790
INT_RGB BICUBIC/AREA_AVG 100 GetScaledInstance 128.700
Можно видеть, что почти во всех случаях getScaledInstance
работает плохо по сравнению с другими подходами (и несколько случаев, когда он кажется лучше, можно объяснить более низким качеством при масштабировании).
Подход, основанный на AffineTransformOp
, как представляется, лучше всего работает в среднем, причем единственным заметным исключением является масштабирование NEAREST_NEIGHBOR
изображений TYPE_INT_RGB
, где подход на основе Graphics
, как представляется, последовательно быстрее.
Суть в следующем: метод, использующий AffineTransformOp
, как в ответе Йорна Хорстмана, кажется, тот, который предлагает лучшую производительность для большинства приложений случаев.
Ответ 6
Самый быстрый способ масштабирования изображения в java без потери качества изображения - использовать Bilinear scaling. Bilinear только хорош, если вы масштабируете изображение на 50% за раз из-за того, как он работает. Следующий код от "Filthy rich clients" от Чет Хаазе. Он объясняет несколько методов в книге, но у этого есть наивысшая эффективность для качественного компромисса.
Он поддерживает все типы BufferedImages, поэтому не беспокойтесь о совместимости. Он также позволяет аппарату java2D ускорить ваш образ, потому что вычисления выполняются с помощью Java2D. Не беспокойтесь, если вы не понимаете эту последнюю часть. Самое главное, что это самый быстрый способ сделать это.
public static BufferedImage getFasterScaledInstance(BufferedImage img, int targetWidth, int targetHeight, boolean progressiveBilinear)
{
int type = (img.getTransparency() == Transparency.OPAQUE) ?
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
BufferedImage scratchImage = null;
Graphics2D g2 = null;
int w, h;
int prevW = ret.getWidth();
int prevH = ret.getHeight();
if(progressiveBilinear) {
w = img.getWidth();
h = img.getHeight();
}else{
w = targetWidth;
h = targetHeight;
}
do {
if (progressiveBilinear && w > targetWidth) {
w /= 2;
if(w < targetWidth) {
w = targetWidth;
}
}
if (progressiveBilinear && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
if(scratchImage == null) {
scratchImage = new BufferedImage(w, h, type);
g2 = scratchImage.createGraphics();
}
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(ret, 0, 0, w, h, 0, 0, prevW, prevH, null);
prevW = w;
prevH = h;
ret = scratchImage;
} while (w != targetWidth || h != targetHeight);
if (g2 != null) {
g2.dispose();
}
if (targetWidth != ret.getWidth() || targetHeight != ret.getHeight()) {
scratchImage = new BufferedImage(targetWidth, targetHeight, type);
g2 = scratchImage.createGraphics();
g2.drawImage(ret, 0, 0, null);
g2.dispose();
ret = scratchImage;
}
System.out.println("ret is "+ret);
return ret;
}
Ответ 7
это работает для меня:
private BufferedImage getScaledImage(BufferedImage src, int w, int h){
int original_width = src.getWidth();
int original_height = src.getHeight();
int bound_width = w;
int bound_height = h;
int new_width = original_width;
int new_height = original_height;
// first check if we need to scale width
if (original_width > bound_width) {
//scale width to fit
new_width = bound_width;
//scale height to maintain aspect ratio
new_height = (new_width * original_height) / original_width;
}
// then check if we need to scale even with the new height
if (new_height > bound_height) {
//scale height to fit instead
new_height = bound_height;
//scale width to maintain aspect ratio
new_width = (new_height * original_width) / original_height;
}
BufferedImage resizedImg = new BufferedImage(new_width, new_height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = resizedImg.createGraphics();
g2.setBackground(Color.WHITE);
g2.clearRect(0,0,new_width, new_height);
g2.drawImage(src, 0, 0, new_width, new_height, null);
g2.dispose();
return resizedImg;
}
Также я добавил белый фон для png
Ответ 8
У вас всегда будет компромисс между скоростью изменения размера и качеством результирующего изображения.
Вы можете попробовать другой алгоритм масштабирования JDK.
Лучший и самый гибкий инструмент для редактирования изображений AFAIK ImageMagick.
Для языка Java существуют два интерфейса:
- JMagick - это интерфейс JNI для ImageMagick. Для получения дополнительной информации см. Проекты Wiki.
- im4java - это интерфейс командной строки для ImageMagick. Это не так, как JMagick, основанный на JNI.
Вы должны предпочесть im4java, прежде чем использовать командную строку для вызова ImageMagick.
Ответ 9
Некоторое улучшение производительности (возможно, небольшое, возможно, незначительное, возможно, за счет качества) может быть достигнуто путем настройки подсказок рендеринга. Например.
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
Ответ 10
Старый вопрос, но в случае, если кто-то еще сталкивается с этой проблемой: я профилировал ваш код, а ваше самое узкое место - вызов:
Image.getScaledInstance()
Этот вызов, как известно, ужасно медленный. Пожалуйста, убедитесь, прочитав этот документ:
Опасности Image.getScaledInstance()
Простейшим/лучшим решением для резкого повышения производительности будет замена этого вызова. Вы можете использовать метод из ответа dpineda (см. Его ответ/код выше):
private BufferedImage getScaledImage(BufferedImage src, int w, int h){
Я протестировал его метод, и он работает очень хорошо. В моем тестировании его реализация (которая позволяет избежать медленного Image.getScaledInstance()), побрит 80% времени обработки!
Ответ 11
Если вы хотите что-то быстро, вы, вероятно, лучше с каким-то собственным кодом, если можете отказаться от переносимости.
Но если вы хотите чистое решение Java, вы можете попробовать и другие решения, например Graphics2D.scale и Image.getScaledInstance.
Я использовал их в прошлом, но не могу вспомнить, у кого была лучшая производительность или лучшие результаты, извините.
Попробуйте их и посмотрите, какой из них лучше всего соответствует вашим потребностям.
Ответ 12
Я использовал im4java
с GraphicsMagick для того, чтобы иметь действительно более быстрые результаты (быстрее, чем ImageIO).
Используется этот код:
public static void createFilePreview(final File originalFile, final String originalFileMimeType, final File destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException {
runThumbnail(new ConvertCmd(), originalFile.getAbsolutePath(), originalFileMimeType, destinationPreviewFile.getAbsolutePath(), maxWidth, maxHeight);
}
public static void createFilePreview(final InputStream originalFileInputStream, final String originalFileMimeType, final File destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException {
final ConvertCmd cmd = new ConvertCmd();
cmd.setInputProvider(new Pipe(originalFileInputStream, null));
runThumbnail(cmd, "-", originalFileMimeType, destinationPreviewFile.getAbsolutePath(), maxWidth, maxHeight);
}
private static void runThumbnail(final ConvertCmd cmd, final String originalFile, final String originalFileMimeType, final String destinationPreviewFile, final Integer maxWidth, final Integer maxHeight) throws IOException, InterruptedException, IM4JavaException {
final IMOperation operation = new IMOperation();
// if it is a PDF, will add some optional parameters to get nicer results
if (originalFileMimeType.startsWith("application/pdf")) {
operation.define("pdf:use-trimbox=true"); // as it is said here http://www.prepressure.com/pdf/basics/page_boxes "The imposition programs and workflows that I know all use the TrimBox as the basis for positioning pages on a press sheet."
operation.density(300, 300); // augment the rendering from 75 (screen size) to 300 dpi in order to create big preview with good quality
}
operation.addImage("[0]"); // if it is a PDF or other multiple image source, will extract the first page / image, else it is ignored
operation.autoOrient(); // Auto-orient the image if it contains some orientation information (typically JPEG with EXIF header)
operation.thumbnail(maxWidth, maxHeight);
operation.addImage();
cmd.run(operation, originalFile, destinationPreviewFile);
}