2009-10-26 237 views
14

Tengo una aplicación de código abierto que carga fotos en Facebook. Para ahorrar ancho de banda, las fotos se redimensionan automáticamente antes de cargarlas (Facebook impone un límite de tamaño máximo). Algunas personas se han quejado de la calidad de la foto, y de hecho se puede ver la diferencia (ver this issue para algunas imágenes de demostración).Calidad de cambio de tamaño de la imagen (Java)

Así que mi pregunta es, ¿cuál es la "mejor" forma de reducir las imágenes (es decir, fotos) en Java sin perder calidad, o al menos, con pérdida de calidad mínima/artefactos?

Puede ver el código actual que tengo here (cambie el tamaño del código a través de this page).

Respuesta

12

Lo he intentado todo, incluidos los trucos here, y todo lo que puedo decir es que es mejor usar ImageMagick con cualquier interfaz, las bibliotecas de imágenes de Javas no son muy rápidas en lo que se refiere a esto. Debe admitir tantos formatos y algoritmos para hacerlo bien.

+0

Esperaba evitar una llamada externa, pero creo que puede estar en lo cierto. ¡Java no parece tener el procesamiento de imágenes completo todavía! –

+1

¡Esta es mi conclusión también! ¡Las apis de la imagen de Java simplemente apestan! Puedo evitar la lentitud, ¡pero la mala calidad de imagen no es aceptable! Espero que los apis relevantes obtengan un tcc muy necesario junto con JavaFX 2.0. – Kimble

+1

Es obvio que no has encontrado [java-image-scaling] (http://code.google.com/p/java-image-scaling/). –

2

¿Qué sugerencia de representación está utilizando? Por lo general, el remuestreo bicúbico será el mejor. En las fotos a las que estás enlazando, son muy irregulares, lo que me hace pensar que estás usando el vecino más cercano como tu pista.

En la clase PictureScaler que enlaza, en el método paintComponent, utiliza seis medios diferentes para cambiar el tamaño de la imagen. ¿Has probado los seis para ver cuál da el mejor resultado?

+0

Me gustaría añadir que, en el código, parece que el interlocutor tiene un modo de "alta calidad" que implica cambiar el tamaño de la imagen en varias pasadas. Esto parece una mala idea por la misma razón por la que no querría comprimir y descomprimir repetidamente una muestra de sonido; la calidad de la imagen en realidad será peor y no mejor. – jprete

+0

Hola, en realidad no he probado los seis, lo que probablemente debería hacer. Aunque estoy usando un remuestreo bicúbico que, como dices, "debería" ser el mejor. Tampoco estoy usando el modo de "alta calidad", ya que no funciona en esa situación, la API de imágenes de Java parece ser propensa a bloqueos. Probablemente algo está mal con mi código, no estoy seguro de qué. –

0

Después de algunos experimentos frustrantes encontré el siguiente resize evaluation, y empleé el enfoque de múltiples pasadas en mi proyecto.

para hacer eso he copiado el método getScaledInstance() en mi clase de generador de imágenes en miniatura, cambiado de imagen Acercamiento leer utilizar ImageIO (que se devuelve un BufferedImage) y ahora soy muy feliz !

He comparado el resultado con un cambio de tamaño realizado en Photoshop CS3 y el resultado es muy similar.

16

Phil, no sé cuál es la solución que finalmente fue con, pero las imágenes de escala en Java puede verse bastante bien si:

  • tipos Evitar BufferedImage que no están bien soportados por el JDK.
  • Uso escalado
  • Palo incrementales a Bicúbico incrementales cuando se utiliza la escala

que he hecho una parte justa de las pruebas de dichos métodos y la escala gradual a lo largo de con pegarse a los tipos de imagen soportados así es la clave - - Veo que Alexander mencionó que todavía no tuvo buena suerte con eso, lo cual es un fastidio.

Lancé el imgscalr library (Apache 2) hace alrededor de 6 meses para abordar el problema de "Quiero copias a escala bonita de esta imagen, ¡HÁGALO AHORA MISMO!" después de leer algo así como 10 preguntas como esta en SO.

uso estándar se parece a:

BufferedImage img = ImageIO.read(...); // load image 
BufferedImage scaledImg = Scalr.resize(img, 640); 

El segundo argumento es la anchura y la altura de delimitación imgscalr va a utilizar para escalar la imagen - manteniendo sus proporciones correctas, incluso si usted pasó en las dimensiones no válidas - hay muchos more detailed methods, pero ese es el uso más simple.

El caso de uso que usted quiere, por ejemplo, si Facebook imágenes limitadas a 800x600 píxeles, se vería así:

BufferedImage img = ImageIO.read(...); // load image 
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY, 800, 600); 

que asegurará la imagen permanece en el tipo de imagen mejor apoyado y escaló con el método de la más alta calidad que Java puede reunir.

En mi propia prueba de alta resolución no he notado ninguna discrepancia con las imágenes escaladas utilizando esta biblioteca/estos métodos, EXCEPTO cuando el cargador ImageIO pone su imagen en un tipo de imagen poco soportada; por ejemplo, esto sucede mucho con GIFs Si los dejas así y no los sacas de esos tipos poco respaldados, terminan pareciendo muy vacilantes y terribles.

La razón de esto es que el equipo Java2D en realidad tiene diferentes tuberías aceleradas por hardware para todos los tipos de imágenes Buffered que el JDK puede procesar: un subconjunto de esos tipos de imágenes menos comunes recurre al mismo software la realización de tuberías debajo de las cubiertas en Java2D, lo que resulta en imágenes pobres y en ocasiones totalmente incorrectas. Este fue un PIA para explicar y tratar de descubrir que acabo de escribir esa lógica directamente en la biblioteca.

Los dos tipos más compatibles son BufferedImage.TYPE_INT_RGB y _ARGB si tiene curiosidad.

+0

Hola Riyad, Gracias por esforzarte en construir esto. Es genial y recién comencé a usarlo. Tengo un problema de calidad cuando trato de CAMBIAR TAMAÑO de una imagen a un ancho y alto específicos. Tengo una imagen que 240X320 y yo tenemos que cambiar su tamaño a 50X75 y 120X180. Intenté ambos por separado usando el siguiente código (estoy usando imgscalr 4.2). – WowBow

+1

image = resize (imagen, Method.ULTRA_QUALITY, 50,75, OP_ANTIALIAS, OP_BRIGHTER); saveImage (imagen, ImageFormatTypes.JPEG, DESTINATION + RESIZED_IMAGE + "." + ImageFormatTypes.JPEG); Tengo dos problemas. # 1 es de muy baja calidad en comparación con otras herramientas internas que utilicé para cambiar el tamaño de la imagen manualmente. # 2. El ancho aumenta de 50 a 56 y de 120 a 135, lo cual es extraño. ¿Tiene alguna idea de por qué están sucediendo? Aprecio tu ayuda. – WowBow

+2

@WowBow ¡Ah! Sí, creo que sé lo que está sucediendo ... Odio que imgscalr no lo abstraiga del desarrollador aún, pero opera en valores de píxeles en bruto: cuando se vuelve a escribir la imagen con ImageIO, se usa el valor predeterminado. Los ajustes del codificador JPG, que están configurados en un 75% de calidad, creo. Debes buscar en google "java ImageIO JPEG quality", por ejemplo, código sobre cómo configurarlo para que diga 95%. La forma "barata y fácil" de probar si este es el caso, escriba la imagen como PNG (formato de losssles); si se ve "mucho mejor", eso es todo. –

5

Para cambiar el tamaño de la imagen con calidad personalizada, use thumbnailator.jar.

Ejemplo Código http://code.google.com/p/thumbnailator/wiki/Examples

+1

Probado tanto org.imgscalr.Scalr como com.mortennobel.imagescaling en combinación con el cambio del nivel de compresión (http://blog.carsoncheng.ca/2011/02/how-to-change-jpeg-compression-in-java.html) y ninguno funcionó. thumbnailator fue increíble, al menos en cuanto a calidad. ¡Gracias! – daker

0

quería cambiar el tamaño de la más alta calidad con relación de aspecto conservado. Intenté algunas cosas y leí varias entradas. Recuperar dos días y al final me dieron el mejor resultado con el método de Java normal (intentado también ImageMagick y bibliotecas java-imagen-escala):

public static boolean resizeUsingJavaAlgo(String source, File dest, int width, int height) throws IOException { 
    BufferedImage sourceImage = ImageIO.read(new FileInputStream(source)); 
    double ratio = (double) sourceImage.getWidth()/sourceImage.getHeight(); 
    if (width < 1) { 
    width = (int) (height * ratio + 0.4); 
    } else if (height < 1) { 
    height = (int) (width /ratio + 0.4); 
    } 

    Image scaled = sourceImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING); 
    BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB); 
    Graphics2D g2d = bufferedScaled.createGraphics(); 
    g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); 
    g2d.drawImage(scaled, 0, 0, width, height, null); 
    dest.createNewFile(); 
    writeJpeg(bufferedScaled, dest.getCanonicalPath(), 1.0f); 
    return true; 
} 


/** 
* Write a JPEG file setting the compression quality. 
* 
* @param image a BufferedImage to be saved 
* @param destFile destination file (absolute or relative path) 
* @param quality a float between 0 and 1, where 1 means uncompressed. 
* @throws IOException in case of problems writing the file 
*/ 
private static void writeJpeg(BufferedImage image, String destFile, float quality) 
     throws IOException { 
    ImageWriter writer = null; 
    FileImageOutputStream output = null; 
    try { 
    writer = ImageIO.getImageWritersByFormatName("jpeg").next(); 
    ImageWriteParam param = writer.getDefaultWriteParam(); 
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 
    param.setCompressionQuality(quality); 
    output = new FileImageOutputStream(new File(destFile)); 
    writer.setOutput(output); 
    IIOImage iioImage = new IIOImage(image, null, null); 
    writer.write(null, iioImage, param); 
    } catch (IOException ex) { 
    throw ex; 
    } finally { 
    if (writer != null) { 
     writer.dispose(); 
    } 
    if (output != null) { 
     output.close(); 
    } 
    } 
} 
5

Las dos bibliotecas de código abierto más populares que se especializan en la imagen de cambio de tamaño en Java actualmente son:

Additonal no es la forma de JDK con Java de Graphics2D (see this question on how to do it) que es notorio para crear bad results especially with downscaling. También hay un Java interface to ImageMagick que se omitirá aquí porque requiere una herramienta externa.

Calidad visual

Aquí es una comparación de los resultados de cambio de tamaño/reducción de escala de un png 580x852-145x213. Como referencia, se usa el ajuste de tamaño de "guardar para web" de Photoshop CS5. Nota: los resultados son 1: 1 lo que las librerías creadas simplemente copiaron juntas. El zoom no usa ningún filtro, solo un algoritmo de vecino más simple.Here you can find the original image.

comparison

  1. Thumbnailator 0.4.8 con la configuración predeterminada, no hay ajustes dimensión
  2. Photoshop CS5 con el algoritmo bicúbica
  3. imgscalr 4.2 con ajuste ULTRA_QUALITY, no hay ajustes dimensión
  4. Graphics2D (Java 8) con render consejos VALUE_INTERPOLATION_BICUBIC, VALUE_RENDER_QUALITY, VALUE_ANTIALIAS_ON

Dejo que el lector seleccione el mejor resultado, ya que es subjetivo. En general, todos tienen un buen rendimiento, excepto Graphics2D. Thumbnailator genera imágenes más nítidas muy similares a la salida de Photoshop, mientras que la salida de imgscalr es considerablemente más suave. Para iconos/texto, etc., desea una salida más nítida, para las imágenes puede que desee una salida más suave.

tiempo computacional

aquí es punto de referencia no científico el uso de este tool y 114 imágenes con una dimensión de aproximadamente 96x96 hasta 2560x1440 tratándolo como 425% imágenes creando: 100%, 150%, 200%, 300% y Versiones escaladas al 400% (por lo tanto, 114 * 5 operaciones de escalado). Todas las bibliotecas usan la misma configuración que en la comparación de calidad (por lo que la mejor calidad posible). Los tiempos solo están escalando, no todo el proceso. Hecho en un i5-2520M con 8GB de RAM y 5 carreras.

  • Thumbnailator: 7003.0ms | 6581.3ms | 6019.1ms | 6375.3ms | 8700.3ms
  • imgscalr: 25218.5ms | 25786.6ms | 25095.7ms | 25790.4ms | 29296.3ms
  • Graphics2D: 7387.6ms | 7177.0ms | 7048.2ms | 7132.3ms | 7510.3ms

Here is the code used in this benchmark.

Curiosamente Thumbnailator es también el más rápido con un tiempo medio de 6,9 ​​seg seguido por Java2D con 7,2 seg dejando imgscalr atrás con un pobre 26,2 seg. Probablemente esto no sea justo ya que imgscalr está configurado en ULTRA_QUALITY, que parece ser extremadamente caro; con el ajuste QUALITY promedia a un 11.1 segundos más competitivo.

+0

Las mediciones de rendimiento son difíciles (escribí algunas palabras sobre esto en [esta respuesta] (http://stackoverflow.com/a/32278737/3182664)). Sin embargo, 1 para esta y la otra respuesta – Marco13

+0

@ Marco13 cierto, pero creo que tuvo un tamaño de muestra grande y suficiente para captar al menos las tendencias. – for3st

+0

Completé las pruebas yo mismo y observé patrones similares a los tuyos (Thumbnailator proporcionó el rendimiento más rápido). Le recomiendo que escriba la línea "imgscalr" dos veces (una para ULTRA_QUALITY y otra para QUALITY), para que los puntos de referencia sean más fáciles de comparar en un vistazo rápido. –

Cuestiones relacionadas