2011-02-11 10 views
5

Aquí está la imagen original: http://rank.my/public/images/uploaded/orig-4193395691714613396.png¿Por qué esta imagen se ve tan mal después de ser reducida en Java?

Y aquí es reducido a 300x225:

http://rank.my/public/images/uploaded/norm-4193395691714613396.png

Y aquí es reducido a 150x112:

http://rank.my/public/images/uploaded/small-4193395691714613396.png

Como se puede ver, 300x225 se ve bastante mal, y 150x112 se ve horrible. Aquí está el código que estoy usando para reducir la imagen:

private static BufferedImage createResizedCopy(final BufferedImage source, final int destWidth, 
     final int destHeight) { 
    final BufferedImage resized = new BufferedImage(destWidth, destHeight, source.getType()); 
    final Graphics2D bg = resized.createGraphics(); 
    bg.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
    bg.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 
    bg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
    final float sx = (float) destWidth/source.getWidth(); 
    final float sy = (float) destHeight/source.getHeight(); 
    bg.scale(sx, sy); 
    bg.drawImage(source, 0, 0, null); 
    bg.dispose(); 
    return resized; 
} 

¿Qué estoy haciendo mal aquí? La escala de la imagen no tiene que ser especialmente rápida, la calidad es definitivamente una prioridad sobre la velocidad. ¿Estoy usando la técnica incorrecta?

+1

@MusiGenesis: Creo que su respuesta eliminado es excelente, y me gustaría que deshacer el borrado . –

+0

Los enlaces están muertos, host con imgur o similar :) – alex

+0

Los enlaces están de vuelta ahora - lo siento – sanity

Respuesta

5

Hay tres maneras de solucionar el problema de la reducción de escala. Lo primero es hacerlo en varios pasos, con no más de un 75% de reducción en cada paso. El segundo es difuminar la imagen antes de cambiar el tamaño; cuanto más se encoja, más tendrás que difuminar. La tercera forma es usar un método que filtre usando más de los bloques de 2x2 a 4x4 píxeles utilizados por un método de interpolación bilineal o bicúbico ingenuo. A medida que el factor de contracción crece, también lo hará el bloque de píxeles utilizado por el filtro, de lo contrario obtendrá artefactos de aliasing como lo ha visto aquí.

3

Nunca he usado estas bibliotecas, por lo que no estoy seguro de responder, pero no puedo evitar pasar estos enlaces que creo que pueden ser útiles. Espero que esto ayude.

http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html

http://www.componenthouse.com/article-20

+0

El enlace today.java.net se ve bastante bien para mí, especialmente la sección titulada "Creación de instancias escaladas" y la implementación 'getScaledInstance (...)' de muestra. – msandiford

2

JAI es bastante frustrante. Todavía me pregunto por qué, sin importar la configuración que hagas, nunca coincide con la velocidad, calidad y simplicidad de ImageMagick. Prefiero usar ImageMagick siempre que puedo.

El siguiente código es el que me da el mejor resultado para escalar la imagen. Tenga en cuenta que he utilizado RenderingHints.VALUE_RENDER_QUALITY y SubsampleAverage y noRenderingHints.VALUE_INTERPOLATION_BICUBIC.

He completado el procesamiento de JAI en un bloque pequeño y siempre lo uso para reducir la escala. El mismo código no da buenos resultados cuando amplía-se aplica una configuración diferente para eso. No lo he probado con PNG, es con lo que trabajé JPEG.

Esperanza esto ayuda

//Set-up and load file 
PlanarImage image = JAI.create("fileload", absPath); 
RenderingHints quality = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 
Properties p = new Properties(System.getProperties()); 
p.put("com.sun.media.jai.disableMediaLib", "true"); 
System.setProperties(p); 

//Setup the processes 
ParameterBlock pb = new ParameterBlock() 
    .addSource(image) 
    .add(scaleX)  //scaleX = (double)1.0*finalX/origX 
    .add(scaleY); //scaleY = (double)1.0*finalY/origY 
RenderedOp tempProcessingFile = JAI.create("SubsampleAverage", pb, quality); 

//Save the file 
FileOutputStream fout = new FileOutputStream(file); 
JPEGEncodeParam encodeParam = new JPEGEncodeParam(); 
encodeParam.setQuality(0.92f); //My experience is anything below 0.92f gives bad result 
ImageEncoder encoder = ImageCodec.createImageEncoder("JPEG", fout, encodeParam); 
encoder.encode(tempProcessingFile.getAsBufferedImage()); 

Además, los artículos que me han ayudado son.

(Los enlaces de arriba son de mis favoritos, editarlos si encuentra que están muertos)

+0

Creo que SubsampleAverage sigue mi tercera sugerencia, utilizando un bloque de píxeles de entrada más grande para cada salida de píxeles. –

+0

que perdió el enlace a los foros de Sun, podría ser este: https://forums.oracle.com/forums/thread.jspa?threadID=1270119? Este hilo comienza con _ "Parece ser lo más fácil de hacer en JAI (o cualquier API de imagen para el caso) pero me da dolor de cabeza. La tarea es: reducir una imagen con la mejor calidad. Resultado: el rendimiento de JAI es deficiente. a aplicaciones estándar como GIMP ... "_ Luego continúa con' RenderingHints KEY_RENDERING, VALUE_RENDER_QUALITY' y con 'MemoryCacheSeekableStream' y termina con la sugerencia de usar' ImageReadParam setSourceRegion, setSourceSubsampling' – gnat

Cuestiones relacionadas