J'ai une application où les utilisateurs peuvent télécharger des photos dans des albums, mais naturellement les images téléchargées doivent être redimensionnées, donc il y a aussi des pouces disponibles et les images montrées tiennent également dans la page (par exemple 800x600). La façon dont je fais le redimensionnement est comme ceci:
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();
Et cela fonctionne bien. Mon seul problème est que la méthode g.drawImage()
semble être extrêmement lente, et je ne peux pas imaginer que l'utilisateur soit patient assez pour attendre un téléchargement de 20 photos 20 * 10 secondes ~ 3 minutes. En fait, sur mon ordinateur, il faut presque 40 secondes pour faire les 3 redimensionnements différents pour une seule image.
Ce n'est pas suffisant et je recherche une solution plus rapide. Je me demande si quelqu'un pourrait m'en dire un meilleur en Java OR en appelant un script Shell, une commande, quel que soit le hack que vous connaissez, il doit soyez plus rapide, tout le reste n'a pas d'importance cette fois.
Vous pouvez utiliser ImageMagick à créer des vignettes .
convert -define jpeg:size=500x180 hatching_orig.jpg -auto-orient \
-thumbnail 250x90 -unsharp 0x.5 thumbnail.gif
Pour l'utiliser à partir de Java vous pouvez essayer JMagick qui fournit une interface Java (JNI) à ImageMagick. Ou vous pouvez simplement appeler les commandes ImageMagick directement en utilisant Runtime.exec
ou ProcessBuilder
.
J'utilise un code similaire au suivant pour mettre à l'échelle les images, j'ai supprimé la partie qui traite de la préservation du rapport d'aspect. Les performances étaient certainement meilleures que 10 secondes par image, mais je ne me souviens pas de chiffres exacts. Pour archiver une meilleure qualité lors de la réduction d'échelle, vous devez redimensionner en plusieurs étapes si l'image d'origine est plus de deux fois la taille de la vignette souhaitée, chaque étape doit redimensionner l'image précédente à environ la moitié de sa taille.
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()));
}
Avez-vous vraiment besoin de la qualité fournie par l'utilisation d'Image.SCALE_SMOOTH? Si vous ne le faites pas, vous pouvez essayer d'utiliser Image.SCALE_FAST . Vous pourriez trouver cela article utile si vous voulez vous en tenir à quelque chose fourni par Java.
Eh bien, Jacob et moi voulions redimensionner une image, pas une image tamponnée. Nous nous sommes donc retrouvés avec ce code:
/**
* 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;
}
Le point principal de la question concernait les performances de mise à l'échelle des images en Java . Les autres réponses ont montré des approches différentes, sans les évaluer davantage. J'étais également curieux à ce sujet, j'ai donc essayé d'écrire un petit test de performance. Cependant, tester les performances de mise à l'échelle de l'image de manière fiable, sensiblement et objectivement est difficile. Il y a bien trop de facteurs déterminants pour être pris en compte:
BufferedImage.TYPE_*
de l'image d'entréeBufferedImage.TYPE_*
de l'image de sortieJ'ai essayé de couvrir ceux que je considérais comme les plus importants. La configuration était:
L'entrée est une simple photo "moyenne" (en particulier, cette "Image du jour" de Wikipedia, avec une taille de 2560x1706 pixels)
Les principaux types d'interpolation sont testés, à savoir en utilisant RenderingHints
où la clé INTERPOLATION
a été définie sur les valeurs NEAREST_NEIGHBOR
, BILINEAR
et BICUBIC
L'image d'entrée a été convertie en différents types:
BufferedImage.TYPE_INT_RGB
: Type couramment utilisé, car il présente "généralement" les meilleures caractéristiques de performances
BufferedImage.TYPE_3BTE_BGR
: C'est le type avec lequel il est lu par défaut, quand il suffit de le lire avec ImageIO
La taille d'image cible variait entre une largeur de 10000 (donc, la mise à l'échelle de l'image vers le haut), et 100 (ainsi, la mise à l'échelle de l'image jusqu'à la taille de la miniature)
Les tests ont été exécutés sur un Win64/AMD K10 avec 3,7 GHz et JDK 1.8u31, avec -Xmx4000m -server
.
Les méthodes testées sont:
AffineTransformOp
, comme dans le réponse de Jörn HorstmannGraphics
, comme dans le réponse de johnstoshImage#getScaledInstance
Le code des tests est affiché ici:
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)";
}
}
Tout d'abord, concernant getScaledInstance
: Comme Chris Campbell l'a souligné dans son (célèbre) article sur The Perils of Image.getScaledInstance () (qui était déjà lié à d'autres réponses), le Image#getScaledInstance
La méthode est quelque peu cassée et ses performances sont terriblement mauvaises pour la plupart des configurations. De plus, il présente l'inconvénient de ne pas avoir un contrôle aussi fin sur le type d'interpolation. Ceci doit être pris en compte dans la comparaison de performances suivante : La qualité des images résultantes peut différer, ce qui est pas considéré ici. Par exemple, la méthode de "moyenne de zone" de getScaledInstance
ne donne pas une bonne qualité d'image lorsque la taille de l'image est augmentée.
(L'inconvénient le plus grave de Image#getScaledInstance
est à mon humble avis qu'il ne fournit qu'un Image
, et non un BufferedImage
, mais si l'image est uniquement censée être peinte dans un Graphics
, cela peut ne pas être important )
Je vais juste vider la sortie du programme ici pour référence, quelques détails suivront ci-dessous:
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
On peut voir que dans presque tous les cas, getScaledInstance
fonctionne mal par rapport aux autres approches (et les quelques cas où il semble mieux fonctionner peuvent être expliqués par la qualité inférieure lors de la mise à l'échelle).
L'approche basée sur AffineTransformOp
semble être la meilleure en moyenne, la seule exception notable étant une NEAREST_NEIGHBOR
mise à l'échelle de TYPE_INT_RGB
images, où l'approche basée sur Graphics
semble être toujours plus rapide.
La conclusion est la suivante: la méthode utilisant AffineTransformOp
, comme dans le réponse de Jörn Horstmann , semble être celle qui offre les meilleures performances pour la plupart = cas d'application.
cela fonctionne pour moi:
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;
}
j'ai aussi ajouté un fond blanc pour png
Le moyen le plus rapide de mettre une image à l'échelle en Java sans perdre la qualité de l'image est d'utiliser la mise à l'échelle bilinéaire. La bilinéaire n'est bonne que si vous mettez à l'échelle l'image de 50% à la fois en raison de la façon dont elle fonctionne. Le code suivant est tiré de 'Filthy rich clients' par Chet Haase. Il explique plusieurs techniques dans le livre, mais celle-ci a les performances les plus élevées pour un compromis de qualité.
Il prend en charge tous les types de BufferedImages, alors ne vous inquiétez pas de la compatibilité. Il permet également au matériel Java2D d'accélérer votre image car les calculs sont effectués par Java2D. Ne vous inquiétez pas si vous ne comprenez pas cette dernière partie. La chose la plus importante est que c'est le moyen le plus rapide de le faire.
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;
}
Vous aurez toujours un compromis entre la vitesse du redimensionnement et la qualité de l'image résultante. Vous pouvez essayer un autre algorithme de mise à l'échelle du JDK.
L'outil le meilleur et le plus flexible pour l'édition d'image AFAIK est ImageMagick .
Il existe deux interfaces pour la langue Java:
Vous devez préférer im4Java avant d'utiliser directement la ligne de commande pour appeler ImageMagick.
Vieille question mais au cas où quelqu'un d'autre rencontrerait ce problème: j'ai profilé votre code et votre plus gros goulot d'étranglement est l'appel à:
Image.getScaledInstance()
Cet appel est bien connu pour être horriblement lent. Soyez convaincu en lisant ce document:
Les dangers de Image.getScaledInstance ()
La solution la plus simple/la meilleure pour une amélioration spectaculaire des performances serait de remplacer cet appel. Vous pouvez utiliser la méthode de la réponse de dpineda (voir sa réponse/code ci-dessus):
private BufferedImage getScaledImage(BufferedImage src, int w, int h){
J'ai testé sa méthode et ça marche vraiment bien. Lors de mes tests, son implémentation (qui évite le lent Image.getScaledInstance ()) a réduit de 80% le temps de traitement!
Une amélioration des performances (peut-être petite, peut-être négligeable, peut-être au détriment de la qualité) peut être obtenue en modifiant les indices de rendu. Par exemple.
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
Si vous voulez quelque chose de rapide, vous êtes probablement mieux avec du code natif, si vous pouvez abandonner la portabilité.
Mais si vous voulez une pure Java, vous pouvez également essayer d'autres solutions, comme Graphics2D.scale et Image.getScaledInstance . Je les ai utilisés dans le passé, mais je ne me souviens pas qui avaient de meilleures performances ou de meilleurs résultats, désolé.
Essayez-les et voyez celui qui correspond le mieux à vos besoins.
J'ai utilisé im4Java
avec GraphicsMagick afin d'avoir des résultats vraiment plus rapides (plus rapides que ImageIO).
Utilisé ce genre de code:
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);
}