Ridimensionamento immagine Java, mantenere le proporzioni

Ho un’immagine che ridimensiono:

if((width != null) || (height != null)) { try{ // scale image on disk BufferedImage originalImage = ImageIO.read(file); int type = originalImage.getType() == 0? BufferedImage.TYPE_INT_ARGB : originalImage.getType(); BufferedImage resizeImageJpg = resizeImage(originalImage, type, 200, 200); ImageIO.write(resizeImageJpg, "jpg", file); } catch(IOException e) { System.out.println(e.getMessage()); } } 

Ecco come ridimensiono l’immagine:

 private static BufferedImage resizeImage(BufferedImage originalImage, int type, Integer img_width, Integer img_height) { BufferedImage resizedImage = new BufferedImage(img_width, img_height, type); Graphics2D g = resizedImage.createGraphics(); g.drawImage(originalImage, 0, 0, img_width, img_height, null); g.dispose(); return resizedImage; } 

Ora il problema è che devo anche mantenere le proporzioni. Cioè, ho bisogno della nuova immagine 200/200 per contenere la nuova immagine ridimensionata. Qualcosa come questo: inserisci la descrizione dell'immagine qui

Ho provato alcune cose ma non hanno funzionato come previsto. Qualsiasi aiuto è apprezzato. Molte grazie.

Eccoci qui:

 Dimension imgSize = new Dimension(500, 100); Dimension boundary = new Dimension(200, 200); 

Funzione per restituire la nuova dimensione a seconda del confine

 public static Dimension getScaledDimension(Dimension imgSize, Dimension boundary) { int original_width = imgSize.width; int original_height = imgSize.height; int bound_width = boundary.width; int bound_height = boundary.height; int new_width = original_width; int new_height = original_height; // first check if we need to scale width if (original_width > bound_width) { //scale width to fit new_width = bound_width; //scale height to maintain aspect ratio new_height = (new_width * original_height) / original_width; } // then check if we need to scale even with the new height if (new_height > bound_height) { //scale height to fit instead new_height = bound_height; //scale width to maintain aspect ratio new_width = (new_height * original_width) / original_height; } return new Dimension(new_width, new_height); } 

Nel caso qualcuno abbia anche bisogno del codice di ridimensionamento dell’immagine, ecco una soluzione decente .

Se non sei sicuro della soluzione di cui sopra, ci sono diversi modi per ottenere lo stesso risultato.

Dovrai controllare Image.getScaledInstance () e altro in questa risposta: Come migliorare le prestazioni del metodo g.drawImage () per ridimensionare le immagini

Tradotto da qui :

 Dimension getScaledDimension(Dimension imageSize, Dimension boundary) { double widthRatio = boundary.getWidth() / imageSize.getWidth(); double heightRatio = boundary.getHeight() / imageSize.getHeight(); double ratio = Math.min(widthRatio, heightRatio); return new Dimension((int) (imageSize.width * ratio), (int) (imageSize.height * ratio)); } 

Puoi anche usare imgscalr per ridimensionare le immagini mantenendo le proporzioni:

 BufferedImage resizeMe = ImageIO.read(new File("orig.jpg")); Dimension newMaxSize = new Dimension(255, 255); BufferedImage resizedImg = Scalr.resize(resizeMe, Method.QUALITY, newMaxSize.width, newMaxSize.height); 

prova questo

 float rateX = (float)jpDisplayImagen.getWidth()/(float)img.getWidth(); float rateY = (float)jpDisplayImagen.getHeight()/(float)img.getHeight(); if (rateX>rateY){ int W=(int)(img.getWidth()*rateY); int H=(int)(img.getHeight()*rateY); jpDisplayImagen.getGraphics().drawImage(img, 0, 0,W,H, null); } else{ int W=(int)(img.getWidth()*rateX); int H=(int)(img.getHeight()*rateX); jpDisplayImagen.getGraphics().drawImage(img, 0, 0,W,H, null); } 
 public class ImageTransformation { public static final String PNG = "png"; public static byte[] resize(FileItem fileItem, int width, int height) { try { ResampleOp resampleOp = new ResampleOp(width, height); BufferedImage scaledImage = resampleOp.filter(ImageIO.read(fileItem.getInputStream()), null); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(scaledImage, PNG, baos); return baos.toByteArray(); } catch (Exception ex) { throw new MapsException("An error occured during image resizing.", ex); } } public static byte[] resizeAdjustMax(FileItem fileItem, int maxWidth, int maxHeight) { try { BufferedInputStream bis = new BufferedInputStream(fileItem.getInputStream()); BufferedImage bufimg = ImageIO.read(bis); //check size of image int img_width = bufimg.getWidth(); int img_height = bufimg.getHeight(); if(img_width > maxWidth || img_height > maxHeight) { float factx = (float) img_width / maxWidth; float facty = (float) img_height / maxHeight; float fact = (factx>facty) ? factx : facty; img_width = (int) ((int) img_width / fact); img_height = (int) ((int) img_height / fact); } return resize(fileItem,img_width, img_height); } catch (Exception ex) { throw new MapsException("An error occured during image resizing.", ex); } } 

}

Questa è la mia soluzione:

 /* Change dimension of Image */ public static Image resizeImage(Image image, int scaledWidth, int scaledHeight, boolean preserveRatio) { if (preserveRatio) { double imageHeight = image.getHeight(); double imageWidth = image.getWidth(); if (imageHeight/scaledHeight > imageWidth/scaledWidth) { scaledWidth = (int) (scaledHeight * imageWidth / imageHeight); } else { scaledHeight = (int) (scaledWidth * imageHeight / imageWidth); } } BufferedImage inputBufImage = SwingFXUtils.fromFXImage(image, null); // creates output image BufferedImage outputBufImage = new BufferedImage(scaledWidth, scaledHeight, inputBufImage.getType()); // scales the input image to the output image Graphics2D g2d = outputBufImage.createGraphics(); g2d.drawImage(inputBufImage, 0, 0, scaledWidth, scaledHeight, null); g2d.dispose(); return SwingFXUtils.toFXImage(outputBufImage, null); } 

Tutte le altre risposte mostrano come calcolare la nuova altezza dell’immagine in funzione della nuova larghezza dell’immagine o viceversa e come ridimensionare l’immagine utilizzando Java Image API. Per coloro che sono alla ricerca di una soluzione semplice, consiglio qualsiasi framework di elaborazione delle immagini Java che possa fare ciò in una singola riga.

L’esempio seguente utilizza Marvin Framework :

 // 300 is the new width. The height is calculated to maintain aspect. scale(image.clone(), image, 300); 

Importazione necessaria:

 import static marvin.MarvinPluginCollection.* 

Carica immagine:

 BufferedImage bufferedImage = ImageIO.read(file); 

Ridimensiona:

 private BufferedImage resizeAndCrop(BufferedImage bufferedImage, Integer width, Integer height) { Mode mode = (double) width / (double) height >= (double) bufferedImage.getWidth() / (double) bufferedImage.getHeight() ? Scalr.Mode.FIT_TO_WIDTH : Scalr.Mode.FIT_TO_HEIGHT; bufferedImage = Scalr.resize(bufferedImage, Scalr.Method.ULTRA_QUALITY, mode, width, height); int x = 0; int y = 0; if (mode == Scalr.Mode.FIT_TO_WIDTH) { y = (bufferedImage.getHeight() - height) / 2; } else if (mode == Scalr.Mode.FIT_TO_HEIGHT) { x = (bufferedImage.getWidth() - width) / 2; } bufferedImage = Scalr.crop(bufferedImage, x, y, width, height); return bufferedImage; } 

Usando la libreria Scalr:

  org.imgscalr imgscalr-lib 4.2  

Ho trovato la risposta selezionata per avere problemi con l’upscaling e così ho creato (ancora) un’altra versione (che ho testato):

 public static Point scaleFit(Point src, Point bounds) { int newWidth = src.x; int newHeight = src.y; double boundsAspectRatio = bounds.y / (double) bounds.x; double srcAspectRatio = src.y / (double) src.x; // first check if we need to scale width if (boundsAspectRatio < srcAspectRatio) { // scale width to fit newWidth = bounds.x; //scale height to maintain aspect ratio newHeight = (newWidth * src.y) / src.x; } else { //scale height to fit instead newHeight = bounds.y; //scale width to maintain aspect ratio newWidth = (newHeight * src.x) / src.y; } return new Point(newWidth, newHeight); } 

Scritto in terminologia Android 🙂

come per i test:

 @Test public void scaleFit() throws Exception { final Point displaySize = new Point(1080, 1920); assertEquals(displaySize, Util.scaleFit(displaySize, displaySize)); assertEquals(displaySize, Util.scaleFit(new Point(displaySize.x / 2, displaySize.y / 2), displaySize)); assertEquals(displaySize, Util.scaleFit(new Point(displaySize.x * 2, displaySize.y * 2), displaySize)); assertEquals(new Point(displaySize.x, displaySize.y * 2), Util.scaleFit(new Point(displaySize.x / 2, displaySize.y), displaySize)); assertEquals(new Point(displaySize.x * 2, displaySize.y), Util.scaleFit(new Point(displaySize.x, displaySize.y / 2), displaySize)); assertEquals(new Point(displaySize.x, displaySize.y * 3 / 2), Util.scaleFit(new Point(displaySize.x / 3, displaySize.y / 2), displaySize)); } 

Basta aggiungere un altro blocco al codice di Ozzy in modo che la cosa assomigli a questo:

 public static Dimension getScaledDimension(Dimension imgSize,Dimension boundary) { int original_width = imgSize.width; int original_height = imgSize.height; int bound_width = boundary.width; int bound_height = boundary.height; int new_width = original_width; int new_height = original_height; // first check if we need to scale width if (original_width > bound_width) { //scale width to fit new_width = bound_width; //scale height to maintain aspect ratio new_height = (new_width * original_height) / original_width; } // then check if we need to scale even with the new height if (new_height > bound_height) { //scale height to fit instead new_height = bound_height; //scale width to maintain aspect ratio new_width = (new_height * original_width) / original_height; } // upscale if original is smaller if (original_width < bound_width) { //scale width to fit new_width = bound_width; //scale height to maintain aspect ratio new_height = (new_width * original_height) / original_width; } return new Dimension(new_width, new_height); }