Ridimensionamento immagini utilizzando JAI e Java2D

Sto preparando una nuova versione del codice del mio photoblog www.enricod.it, e una delle principali migliorie è l'introduzione della libreria JAI, in particolare per il ridimensionamento delle immagini.
La documentazione in rete non è ricchissima, quindi ho pensato di scrivere 2 note per ricapitolare quello che ho fatto.

Ho per ora fatto le prove utilizzando la versione "solo java", quindi semplicemente aggiungendo al classpath le librerie:
  • jai_codec.jar
  • jai_core.jar
ottendo comunque delle buone prestazioni, almeno per le mie esigenze attuali. Prestazioni migliori dovrebbere essere possibili utilizzando le versioni con codice nativo, almeno secondo la documentazione ufficiale. Non ho ancora fatto alcuna prova al momento.

Nel caso uin cui vogliare ridimensionare un'immagine jpeg salvata in un file, e salvare il risultato ancora in un file, è sufficiente fare:

public void resize(File source, File dest, int maxDim) {
InputStream is;
try {
is = new FileInputStream( source );
SeekableStream s = SeekableStream
.wrapInputStream(is, true);
RenderedOp objImage = JAI.create("stream", s);
((OpImage) objImage.getRendering()).setTileCache(null);

Dimension newDim = // calculate new image dimensions
float xScale = ((float) newDim.width)
/ objImage.getWidth();
float yScale = ((float) newDim.height)
/ objImage.getHeight();

RenderedOp out1 = JAI.create("scale"
, getParameterBlock(
objImage
, xScale
, yScale)
, null);
ImageIO.write(out1, "jpg", dest);

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

protected ParameterBlock getParameterBlock(
RenderedOp objImage
, float xScale, float yScale) {
ParameterBlock pb = new ParameterBlock();
pb.addSource(objImage); // The source image
pb.add(xScale); // The xScale
pb.add(yScale); // The yScale
pb.add(0.0F); // The x translation
pb.add(0.0F); // The y translation
pb.add( getInterpolation() ); // The interpolation
return pb;
}

Ho ottenuto risultati molto variabili, sia per quanto riguarda qualità che velocità, utilizzando i diversi algoritmi di interpolazione a disposizione.
Io ho provato con i seguenti algoritmi di interpolazione:

public Interpolation getInterpolation() {
return new javax.media.jai.InterpolationBilinear();
}

public Interpolation getInterpolation() {
return new InterpolationNearest() ;
}

public Interpolation getInterpolation() {
return new InterpolationBicubic(1);
}

public Interpolation getInterpolation() {
return new InterpolationBicubic2( 0 );
}

trovando un buon compromesso con l'interpolazione Bilineare.

Nel caso in cui l'immagine sia in forma binaria (per esempio perchè salvata in un database), ho risolto con il seguente metodo:

public byte[] resize(final byte[] source, int maxDim) {
InputStream is;
byte[] dest = null;
try {
is = new ByteArraySeekableStream( source );
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageOutputStream outStream =
ImageIO.createImageOutputStream(bos);

SeekableStream s =
SeekableStream.wrapInputStream(is, true);
RenderedOp objImage = JAI.create("stream", s);
((OpImage) objImage.getRendering()).setTileCache(null);

Dimension newDim = ImageUtils
.getNewDimension(
new Dimension(
objImage.getWidth()
, objImage.getHeight())
, maxDim );
float xScale = ((float) newDim.width)
/ objImage.getWidth();
float yScale = ((float) newDim.height)
/ objImage.getHeight();
RenderedOp out1
= JAI.create("scale"
, getParameterBlock(objImage, xScale, yScale), null);
ImageIO.write(out1, "jpg", outStream);
dest = bos.toByteArray();

} catch (IOException e) {
e.printStackTrace();
}
return dest;
}


Un metodo alternativo, più lento ma con una buona qualità, prevede l'utilizzo di Java2D anzichè di JAI. Riporto anche in questo caso il metodo:


public void resize(File source, File dest, int maxDim) {
String THUMBEXT = "jpg";
Image i;
try {
i = ImageIO.read(source);

Image resizedImage = null;
int iWidth = i.getWidth(null);
int iHeight = i.getHeight(null);
if (iWidth > iHeight)
resizedImage = i.getScaledInstance(maxDim
, (maxDim * iHeight) / iWidth
, Image.SCALE_SMOOTH);
else
resizedImage = i.getScaledInstance(
(maxDim * iWidth) / iHeight
, maxDim
, Image.SCALE_SMOOTH);

// This code ensures that all the
// pixels in the image are loaded.
Image temp = new ImageIcon(resizedImage).getImage();

// Create the buffered image.
BufferedImage bufferedImage = new BufferedImage(
temp.getWidth(null)
, temp.getHeight(null)
, BufferedImage.TYPE_INT_RGB);
// Copy image to buffered image.
Graphics g = bufferedImage.createGraphics();

// Clear background and paint the image.
g.setColor(Color.white);
g.fillRect(0, 0
, temp.getWidth(null), temp.getHeight(null));
g.drawImage(temp, 0, 0, null);
g.dispose();

// soften
float softenFactor = 0.01f;
float[] softenArray = { 0, softenFactor, 0, softenFactor
, 1 - (softenFactor * 4),
softenFactor, 0, softenFactor, 0 };
Kernel kernel = new Kernel(3, 3, softenArray);
ConvolveOp cOp = new ConvolveOp(kernel
, ConvolveOp.EDGE_NO_OP, null);
bufferedImage = cOp.filter(bufferedImage, null);

// suffix of resized should always be jpg
String resizedpath = dest.getPath();
if (!resizedpath.endsWith("." + THUMBEXT)) {
resizedpath += "." + THUMBEXT;
dest = new File(resizedpath);
}

ImageIO.write(bufferedImage, THUMBEXT, dest);
} catch (IOException e1) {
e1.printStackTrace();
}
}

Comments

Anonymous said…
Grazie, mi hai salvatooooooooooooooooooooooooooooooo.... tvb :)

Quando avrò problemi, contatterò subito il tuo sito!!
Anonymous said…
Grazie mille :)

Popular Posts