2011-02-11 23 views
5

Ecco l'immagine originale: http://rank.my/public/images/uploaded/orig-4193395691714613396.pngPerché questa immagine appare così male dopo essere stata ridimensionata in Java?

E qui si è ridimensionato a 300x225:

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

E qui si è ridimensionato a 150x112:

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

Come puoi vedere, 300x225 sembra piuttosto male e 150x112 sembra orribile. Ecco il codice che sto usando per ridimensionare l'immagine:

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; 
} 

Cosa sto facendo di sbagliato qui? Il ridimensionamento delle immagini non deve essere particolarmente veloce, la qualità è sicuramente una priorità rispetto alla velocità. Sto usando la tecnica sbagliata?

+1

@MusiGenesis: Penso che la tua risposta eliminato è eccellente, e vorrei che ci si Undelete It . –

+0

I collegamenti sono morti, si prega di ospitare con imgur o simili :) – alex

+0

I collegamenti sono di nuovo disponibili - mi spiace – sanity

risposta

5

Ci sono tre modi per risolvere il problema downscaling. Il primo è farlo in più passaggi, con una riduzione non superiore al 75% in ogni passaggio. Il secondo è quello di sfocare l'immagine prima di ridimensionarla; più si restringe, più dovrai sfocare. Il terzo modo consiste nell'utilizzare un metodo che filtra usando più dei blocchi da 2x2 a 4x4 pixel utilizzati da un metodo di interpolazione bilineare o bicubico. Dato che il fattore di restringimento diventa più grande, così dovrebbe essere il blocco di pixel utilizzato dal filtro, altrimenti ottieni artefatti di aliasing come hai visto qui.

3

Non ho mai usato queste librerie quindi non mi sento in grado di rispondere, ma non posso fare a meno di passare lungo questi collegamenti che ho trovato che penso possano essere utili. Speriamo che questo possa aiutare.

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

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

+0

Il collegamento today.java.net mi sembra piuttosto buono, in particolare la sezione intitolata "Creazione di istanze in scala" e l'implementazione di esempio 'getScaledInstance (...)'. – msandiford

2

JAI è piuttosto frustrante. Mi chiedo ancora perché, a prescindere dalle impostazioni, non corrisponde mai alla velocità, alla qualità e alla semplicità di ImageMagick. Preferisco usare ImageMagick ovunque io sia.

Il codice seguente è quello che mi dà il miglior risultato per il ridimensionamento dell'immagine. Si prega di notare che ho usato RenderingHints.VALUE_RENDER_QUALITY e SubsampleAverage e nonRenderingHints.VALUE_INTERPOLATION_BICUBIC.

Ho racchiuso l'elaborazione JAI in un piccolo blocco e lo uso sempre per ridimensionarlo. Lo stesso codice non corrisponde a quando si esegue il ridimensionamento, in caso contrario viene applicata un'impostazione diversa. Non ho provato con PNG, JPEG è quello con cui ho lavorato.

Spero che questo aiuti

//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()); 

Inoltre, gli articoli che mi hanno aiutato sono.

(link qui sopra sono dal mio segnalibro, modificarli se trovate sono morti)

+0

Credo che SubsampleAverage segua il mio terzo suggerimento, utilizzando un blocco di pixel più grande di input per ogni output di pixel. –

+0

che ha perso il collegamento ai forum Sun, potrebbe essere questo: https://forums.oracle.com/forums/thread.jspa?threadID=1270119? Questo thread inizia con _ "Sembra essere la cosa più semplice da fare in JAI (o qualsiasi API di immagine per quella materia) ma mi dà mal di testa. Il compito è: ridimensionare un'immagine con la migliore qualità. Risultato: JAI ha scarse prestazioni rispetto a alle applicazioni standard come GIMP ... "_ Quindi prosegue con' RenderingHints KEY_RENDERING, VALUE_RENDER_QUALITY' e con 'MemoryCacheSeekableStream' e termina con il suggerimento di usare' ImageReadParam setSourceRegion, setSourceSubsampling' – gnat