¿Cómo puedo cambiar el tamaño de una imagen usando Java?

126

Necesito cambiar el tamaño de los archivos PNG, JPEG y GIF. ¿Cómo puedo hacer esto usando Java?

pbreault
fuente
14
Como solicitó una biblioteca, la respuesta real está aquí: stackoverflow.com/questions/244164/… . Es mucho más fácil de usar que el código en la respuesta aceptada;)
Artur Gajowy
No estoy de acuerdo con la parte de la biblioteca: Graphics2D es parte de la biblioteca awt y es de código abierto. Para la última parte (código abierto) no estoy 100% seguro, pero ¿quién miraría el código awt de todos modos?
Burkhard

Respuestas:

87

Después de cargar la imagen puedes probar:

BufferedImage createResizedCopy(Image originalImage, 
            int scaledWidth, int scaledHeight, 
            boolean preserveAlpha)
    {
        System.out.println("resizing...");
        int imageType = preserveAlpha ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
        BufferedImage scaledBI = new BufferedImage(scaledWidth, scaledHeight, imageType);
        Graphics2D g = scaledBI.createGraphics();
        if (preserveAlpha) {
            g.setComposite(AlphaComposite.Src);
        }
        g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null); 
        g.dispose();
        return scaledBI;
    }
Burkhard
fuente
9
Encontré que esta técnica crea una imagen que no tiene la calidad suficiente para mis necesidades.
morgancodes
1
¿Image.getScaledInstance (ancho, alto, sugerencias) también lo hará?
codeplay
1
¿Es el check de preserveAlpha al revés (para imageType)?
Thilo
Estoy de acuerdo con @morgancodes. La calidad de imagen es mucho peor que la que se obtiene, por ejemplo, con OS X Preview al cambiar el tamaño a las mismas dimensiones. Probaré algunas bibliotecas de código abierto para ver si les va mejor.
Thilo el
1
"Probaré algunas bibliotecas de código abierto para ver si les va mejor". +1 para imgscalr de la respuesta de @ RiyadKalla.
Thilo
182

FWIW Acabo de lanzar (Apache 2, alojado en GitHub) una biblioteca simple de escalado de imágenes para Java llamada imgscalr (disponible en Maven central ).

La biblioteca implementa algunos enfoques diferentes para el escalado de imágenes (incluido el enfoque incremental de Chris Campbell con algunas mejoras menores) y elegirá el enfoque más óptimo para usted si así lo solicita, o le dará el aspecto más rápido o mejor (si usted pide eso).

El uso es muy simple, solo un montón de métodos estáticos. El caso de uso más simple es:

BufferedImage scaledImage = Scalr.resize(myImage, 200);

Todas las operaciones mantienen las proporciones originales de la imagen, por lo que en este caso le está pidiendo a imgscalr que cambie el tamaño de su imagen dentro de los límites de 200 píxeles de ancho y 200 píxeles de alto y, de forma predeterminada, seleccionará automáticamente el enfoque más atractivo y más rápido para eso, ya que no era No especificado.

Al principio me doy cuenta de que esto parece autopromoción (lo es), pero pasé mi parte de tiempo buscando en Google exactamente el mismo tema y seguí sacando diferentes resultados / enfoques / pensamientos / sugerencias y decidí sentarme y escribir un Implementación simple que abordaría esos 80-85% de casos de uso en los que tiene una imagen y probablemente desee una miniatura para ella, ya sea lo más rápido posible o lo más atractivo posible (para aquellos que lo han intentado, notará haciendo una Graphics.drawImage incluso con interpolación BICUBIC a una imagen lo suficientemente pequeña, todavía parece basura).

Riyad Kalla
fuente
1
Riad, me gustó usar Scalr. Tengo curiosidad por saber, ¿cómo terminaste eligiendo una API con todos los métodos estáticos? Había escrito una API similar que estaba más cerca de un constructor. Al igual new ImageScaler(img).resizeTo(...).rotate(...).cropTo(...).toOutputBuffer(). También me gusta tu forma y creo que es más simple.
Amir Raminfar
44
Amir, el Builder funciona de maravilla para estos tipos de bibliotecas, así, me acaba de pasar para ir con el enfoque de métodos estáticos porque pensé que era un pelo más fácil de seguir para los nuevos usuarios y el uso de patrones que esperaba de imgscalr (originalmente solo métodos de cambio de tamaño individuales) no se beneficiaron de tener un estado de retención de instancia (la instancia del generador). Así que guardé en la instanciación de objetos y seguí con los métodos estáticos. He tomado una postura firme contra la creación de objetos dentro de imgscalr en todos los lugares donde puedo evitarlo. Sin embargo, ambos enfoques funcionan muy bien.
Riyad Kalla
55
¡+1 por hacer que esté disponible en el repositorio central de Maven!
Grilse
¿Qué es esta clase BufferedImage? ¿Por qué Android Studio no la encuentra? @Riyad Kalla
Milad
3
@ xe4me BufferedImage es una clase en J2SE JDK (parte del marco Java2D) utilizada para representar datos de píxeles sin comprimir sin procesar. No está disponible en Android, Android proporciona sus propias clases para trabajar con datos de imágenes.
Riyad Kalla
54

Thumbnailator es una biblioteca de redimensionamiento de imágenes de código abierto para Java con una interfaz fluida, distribuida bajo la licencia MIT.

Escribí esta biblioteca porque hacer miniaturas de alta calidad en Java puede ser sorprendentemente difícil, y el código resultante podría ser bastante complicado. Con Thumbnailator, es posible expresar tareas bastante complicadas utilizando una API fluida simple.

Un simple ejemplo

Para un ejemplo simple, tomar una imagen y cambiar su tamaño a 100 x 100 (preservando la relación de aspecto de la imagen original), y guardarla en un archivo se puede lograr en una sola declaración:

Thumbnails.of("path/to/image")
    .size(100, 100)
    .toFile("path/to/thumbnail");

Un ejemplo avanzado

La realización de tareas complejas de cambio de tamaño se simplifica con la interfaz fluida de Thumbnailator.

Supongamos que queremos hacer lo siguiente:

  1. tomar las imágenes en un directorio y
  2. redimensionarlos a 100 x 100, con la relación de aspecto de la imagen original,
  3. guárdelos todos en JPEG con configuraciones de calidad de 0.85,
  4. donde los nombres de archivo se toman del original con thumbnail.adjuntos al principio

Traducido al Thumbnailator, podríamos realizar lo anterior con lo siguiente:

Thumbnails.of(new File("path/to/directory").listFiles())
    .size(100, 100)
    .outputFormat("JPEG")
    .outputQuality(0.85)
    .toFiles(Rename.PREFIX_DOT_THUMBNAIL);

Una nota sobre la calidad y la velocidad de la imagen.

Esta biblioteca también utiliza el método de escala progresiva bilineal resaltado en Filthy Rich Clients por Chet Haase y Romain Guy para generar miniaturas de alta calidad y garantizar un rendimiento de tiempo de ejecución aceptable.

Coobird
fuente
1
Coobird, muy buen trabajo con la API. Muy sencillo y fácil de usar.
Riyad Kalla
@LordT: Me alegra que haya encontrado que Thumbnailator es fácil de usar :)
coobird
¿Cómo puedo crear una miniatura de 80x80 con una imagen original de 500x400? No vi ninguna opción para ello. ¡Gracias!
Terry
".outputFormat (" JPEG ")" no está escrito en ninguna parte de la documentación.
Vincent Cantin
@ Vincent La lista completa de métodos que se pueden usar se encuentra en la documentación de la API de Thumbnailator: thumbnailator.googlecode.com/hg/javadoc/net/coobird/…
coobird
12

No necesitas una biblioteca para hacer esto. Puedes hacerlo con el propio Java.

Chris Campbell tiene una excelente y detallada reseña sobre escalar imágenes: vea este artículo .

Chet Haase y Romain Guy también tienen una reseña detallada y muy informativa sobre el escalado de imágenes en su libro, Filthy Rich Clients .

David Koelle
fuente
77
El artículo de Chris es exactamente lo que me motivó a escribir imgscalr en primer lugar; y preguntas como esta (y respuestas como la tuya). Mucha gente pregunta una y otra vez cómo obtener una buena imagen en miniatura de una imagen. Hay varias formas de hacerlo, Chris no siempre es la mejor manera, depende de lo que intentes hacer y de cuán grande sea la reducción. imgscalr aborda todo eso y es 1 clase.
Riyad Kalla
2
+1, estoy de acuerdo, los clientes Filthy Rich son uno de los mejores libros de Java a la par con "Java efectivo", pero ImgScalr es lo que uso porque soy flojo.
Shawn Vader
9

Si se trata de imágenes grandes o quiere un resultado atractivo, no es una tarea trivial en Java. Simplemente hacerlo a través de una operación de reescalado a través de Graphics2D no creará una miniatura de alta calidad. Puede hacerlo usando JAI, pero requiere más trabajo del que imagina para obtener algo que se vea bien y JAI tiene la desagradable costumbre de volar nuestra JVM con errores de OutOfMemory.

Sugiero usar ImageMagick como un ejecutable externo si puede salirse con la suya. Es simple de usar y hace el trabajo correctamente para que no tenga que hacerlo.

Joel Carranza
fuente
4

Si tener una imagen instalada en su máquina es una opción, le recomiendo im4java . Es una capa de abstracción muy delgada sobre la interfaz de línea de comando, pero hace su trabajo muy bien.

naltatis
fuente
3

La API de Java no proporciona una función de escala estándar para imágenes y degrada la calidad de la imagen.

Debido a esto, traté de usar cvResize de JavaCV pero parece causar problemas.

Encontré una buena biblioteca para escalar imágenes: simplemente agregue la dependencia para "java-image-scaling" en su pom.xml.

<dependency>
    <groupId>com.mortennobel</groupId>
    <artifactId>java-image-scaling</artifactId>
    <version>0.8.6</version>
</dependency>

En el repositorio de Maven obtendrá la versión más reciente para esto.

Ex. En su programa java

ResampleOp resamOp = new ResampleOp(50, 40);
BufferedImage modifiedImage = resamOp.filter(originalBufferedImage, null);
Ruju
fuente
También está disponible en GitHub ( github.com/mortennobel/java-image-scaling.git ) y tiene la versión 8.7 por ahora. Necesitaba una pequeña solución en pom.xml, tú (cambiar la fuente y el destino a al menos 1.6 ya que Maven más nuevo ya no admite 1.5).
Cromax
1

Image Magick ha sido mencionado. Hay un proyecto front-end JNI llamado JMagick. No es un proyecto particularmente estable (y se sabe que Image Magick en sí misma cambia mucho e incluso rompe la compatibilidad). Dicho esto, hemos tenido una buena experiencia usando JMagick y una versión compatible de Image Magick en un entorno de producción para realizar escalado a un alto rendimiento y baja tasa de latencia. La velocidad fue sustancialmente mejor que con una biblioteca de gráficos de Java que probamos anteriormente.

http://www.jmagick.org/index.html

Jice
fuente
1

Simplemente use la respuesta de Burkhard pero agregue esta línea después de crear los gráficos:

    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

También puede establecer el valor en BICUBIC, producirá una imagen de mejor calidad pero es una operación más costosa. Hay otras sugerencias de representación que puede establecer, pero he descubierto que la interpolación produce el efecto más notable. Tenga en cuenta que si desea acercarse mucho, el código Java probablemente será muy lento. Encuentro que las imágenes más grandes comienzan a producir un retraso de alrededor del 300% de zoom, incluso con todas las sugerencias de representación configuradas para optimizar la velocidad sobre la calidad.

The One True Colter
fuente
1

Resulta que escribir un escalador de rendimiento no es trivial. Lo hice una vez para un proyecto de código abierto: ImageScaler .

En principio, 'java.awt.Image # getScaledInstance (int, int, int)' también haría el trabajo, pero hay un error desagradable con esto: consulte mi enlace para obtener más información.

aanno
fuente
0

He desarrollado una solución con las clases disponibles gratuitamente (AnimatedGifEncoder, GifDecoder y LWZEncoder) disponibles para manejar la animación GIF.
Puede descargar el jgifcode jar y ejecutar la clase GifImageUtil. Enlace: http://www.jgifcode.com

Rohit Tiwari
fuente
1
Mencione en la respuesta si está afiliado al producto que recomienda.
Hans Olsson
waw ... ¿eso es para cambiar el tamaño de la imagen animada GIF? y es gratis? Wwaww ... debería probarlo ahora ...
gumuruh
Sí, esto es desarrollado por mí. Aquí está el código fuente para él github.com/rt29/jgifcode : ahora está aquí. Ya no lo estoy apoyando.
Rohit Tiwari
0

Si no desea importar imgScalr como @Riyad Kalla, la respuesta anterior que probé también funciona bien, puede hacer esto tomado de Peter Walser, responda @Peter Walser en otro tema:

 /**
     * utility method to get an icon from the resources of this class
     * @param name the name of the icon
     * @return the icon, or null if the icon wasn't found.
     */
    public Icon getIcon(String name) {
        Icon icon = null;
        URL url = null;
        ImageIcon imgicon = null;
        BufferedImage scaledImage = null;
        try {
            url = getClass().getResource(name);

            icon = new ImageIcon(url);
            if (icon == null) {
                System.out.println("Couldn't find " + url);
            }

            BufferedImage bi = new BufferedImage(
                    icon.getIconWidth(),
                    icon.getIconHeight(),
                    BufferedImage.TYPE_INT_RGB);
            Graphics g = bi.createGraphics();
            // paint the Icon to the BufferedImage.
            icon.paintIcon(null, g, 0,0);
            g.dispose();

            bi = resizeImage(bi,30,30);
            scaledImage = bi;// or replace with this line Scalr.resize(bi, 30,30);
            imgicon = new ImageIcon(scaledImage);

        } catch (Exception e) {
            System.out.println("Couldn't find " + getClass().getName() + "/" + name);
            e.printStackTrace();
        }
        return imgicon;
    }

 public static BufferedImage resizeImage (BufferedImage image, int areaWidth, int areaHeight) {
        float scaleX = (float) areaWidth / image.getWidth();
        float scaleY = (float) areaHeight / image.getHeight();
        float scale = Math.min(scaleX, scaleY);
        int w = Math.round(image.getWidth() * scale);
        int h = Math.round(image.getHeight() * scale);

        int type = image.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;

        boolean scaleDown = scale < 1;

        if (scaleDown) {
            // multi-pass bilinear div 2
            int currentW = image.getWidth();
            int currentH = image.getHeight();
            BufferedImage resized = image;
            while (currentW > w || currentH > h) {
                currentW = Math.max(w, currentW / 2);
                currentH = Math.max(h, currentH / 2);

                BufferedImage temp = new BufferedImage(currentW, currentH, type);
                Graphics2D g2 = temp.createGraphics();
                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                g2.drawImage(resized, 0, 0, currentW, currentH, null);
                g2.dispose();
                resized = temp;
            }
            return resized;
        } else {
            Object hint = scale > 2 ? RenderingHints.VALUE_INTERPOLATION_BICUBIC : RenderingHints.VALUE_INTERPOLATION_BILINEAR;

            BufferedImage resized = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2 = resized.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
            g2.drawImage(image, 0, 0, w, h, null);
            g2.dispose();
            return resized;
        }
    }
shareef
fuente
0

Pruebe este siguiente método:

ImageIcon icon = new ImageIcon("image.png");
Image img = icon.getImage();
Image newImg = img.getScaledInstance(350, 350, java.evt.Image.SCALE_SMOOTH);
icon = new ImageIcon(img);
JOptionPane.showMessageDialog(null, "image on The frame", "Display Image", JOptionPane.INFORMATION_MESSAGE, icon);
Fridjato Parte Fridjat
fuente