I tried the code below to generate a thumbnail.
I am able to get the thumbnail but the quality is not there. Please can any one help me in this one to generate a high quality thumbnail? The original image is high quality.
BufferedImage thumbImage = new BufferedImage(thumbWidth, thumbHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = thumbImage.createGraphics();
graphics2D.setBackground(Color.WHITE);
graphics2D.setPaint(Color.WHITE);
graphics2D.fillRect(0, 0, thumbWidth, thumbHeight);
graphics2D.setComposite(AlphaComposite.Src);
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);
graphics2D.dispose();
File file = new File(thumbnailFile);
if (javax.imageio.ImageIO.write(thumbImage, "JPG", file))
return file;
You might want to take a look at this: http://download.oracle.com/javase/tutorial/uiswing/components/icon.html
http://download.oracle.com/javase/tutorial/uiswing/examples/components/IconDemoProject/src/components/IconDemoApp.java
I used that as a reference for doing something similar before.
I had the same problem and found this great article with sample code and sample images at the end:
http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
check this I found best jar file here
public static javaxt.io.Image resizeThumbnailImage(javaxt.io.Image image, int width, int height) {
Integer imgWidth = image.getWidth();
Integer imgHeight = image.getHeight();
Double imgRatio = (imgWidth.doubleValue() / imgHeight.doubleValue());
logger.info("\n======= imgRatio " + imgRatio);
if (imgRatio >= 2) {
image.setWidth(width - 1);
} else if (imgRatio < 1) {
image.setHeight(300);
} else {
Double expectedHeight = (imgRatio * (height / ProjectConstant.THUMBNAIL_IMG_ASPECT_RATIO));
image.setHeight(expectedHeight.intValue());
if (image.getWidth() > width) {
image.setWidth(width - 20);
}
}
logger.info("=======after Processing image Width " + image.getWidth()+" Hight "+image.getHeight());
return image;
}
my constant
public static final double THUMBNAIL_IMG_ASPECT_RATIO = 1.4;
Related
I am working a method to resize featured image and I have finished it, but after testing seems something isn't right cause the image is deformed and don't look nice at all, I am looking very carefully, but I don't get to see what's the problem, I would really appreciate if you have any idea what is wrong or a better way to solve this. Have a look please, I would really need some better advice and thanks in advance!!
private byte[] resizeFeatureImage(MultipartFile featureImage)
{
try
{
BufferedImage originalImage = ImageIO.read(featureImage.getInputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
double featImageWidth = originalImage.getWidth();
double featImageHeight = originalImage.getHeight();
if (featImageHeight > MAX_FEAT_IMAGE_HEIGHT || featImageWidth > MAX_FEAT_IMAGE_WIDTH)
{
// Sanity check on the input (division by zero, infinity):
if (featImageWidth <= 1 || featImageHeight <= 1)
{
throw new IllegalArgumentException("Cannot do image resize for " + featureImage);
}
// The scaling factors to reach to max on WIDTH and HEIGHT:
double xScale = MAX_FEAT_IMAGE_WIDTH / featImageWidth;
double yScale = MAX_FEAT_IMAGE_HEIGHT / featImageHeight;
// Proportional (scale WIDTH and HEIGHT by the same factor):
double scale = Math.min(xScale, yScale);
// (Possibly) Do not enlarge:
scale = Math.min(1.0, scale);
int finalWidth = Math.min((int) Math.round(scale * featImageWidth), MAX_FEAT_IMAGE_WIDTH);
int finalHeight = Math.min((int) Math.round(scale * featImageHeight), MAX_FEAT_IMAGE_HEIGHT);
double ratio = featImageWidth / featImageHeight;
// WIDTH is bigger then HEIGHT
if (ratio > 1)
{
finalWidth = MAX_FEAT_IMAGE_WIDTH;
finalHeight = (int) Math.round(MAX_FEAT_IMAGE_HEIGHT / ratio);
}
// HEIGHT is bigger then WIDTH
else if (ratio < 1)
{
finalWidth = (int) Math.round(MAX_FEAT_IMAGE_WIDTH / ratio);
finalHeight = MAX_FEAT_IMAGE_HEIGHT;
}
// WIDTH and HEIGHT are equal
else
{
finalHeight = MAX_FEAT_IMAGE_HEIGHT;
finalWidth = MAX_FEAT_IMAGE_WIDTH;
}
logger.info("[resizeFeatureImage] [FEATURE IMAGE RESIZE] Starting to resize feature Image");
Graphics2D g2d;
BufferedImage resizedImage;
if (featureImage.getContentType().contains("png"))
{
resizedImage = new BufferedImage(finalWidth, finalHeight, BufferedImage.TYPE_INT_ARGB);
}
else
{
resizedImage = new BufferedImage(finalWidth, finalHeight, BufferedImage.TYPE_3BYTE_BGR);
}
g2d = resizedImage.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(ImageIO.read(featureImage.getInputStream()), 0, 0, finalWidth, finalHeight,
null);
g2d.dispose();
ImageIO.write(resizedImage, featureImage.getContentType().split("/")[1], baos);
logger.info("[resizeFeatureImage] [FEATURE IMAGE RESIZE] Feature image resized!");
return baos.toByteArray();
}
else
{
ImageIO.write(originalImage, featureImage.getContentType().split("/")[1], baos);
return baos.toByteArray();
}
}
catch (Exception e)
{
logger.warn("[resizeFeatureImage] [STATUS] - ERROR ");
logger.warn("[resizeFeatureImage] [EXCEPTION] " + e.getMessage(), e);
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"The file you uploaded can be damaged or has incorrect encoding.");
}
}
When you scale, your image needs to retain the same ratio.
double featImageWidth = originalImage.getWidth();
double featImageHeight = originalImage.getHeight();
double ratio = featImageWidth/featImageHeight;
//this width meets your constraints
int finalWidth = MAX_FEAT_IMAGE_WIDTH;
//this final height is what the height would be to keep the same ratio.
int finalHeight = (int)(finalWidth/ratio);
if(finalHeight > MAX_FEAT_IMAGE_HEIGHT){
//the height constrains the image.
finalHeight = MAX_FEAT_IMAGE_HEIGHT;
finalWidth = (int)(finalHeight*ratio)
}
In the script it is going from around the 300x300 mark down to 60x60. Need to improve the overall image quality as it is coming out very poorly at the moment.
public static Boolean resizeImage(String sourceImg, String destImg, Integer Width, Integer Height, Integer whiteSpaceAmount)
{
BufferedImage origImage;
try
{
origImage = ImageIO.read(new File(sourceImg));
int type = origImage.getType() == 0? BufferedImage.TYPE_INT_ARGB : origImage.getType();
int fHeight = Height;
int fWidth = Width;
int whiteSpace = Height + whiteSpaceAmount; //Formatting all to squares so don't need two whiteSpace calcs..
double aspectRatio;
//Work out the resized dimensions
if (origImage.getHeight() > origImage.getWidth()) //If the pictures height is greater than the width then scale appropriately.
{
fHeight = Height; //Set the height to 60 as it is the biggest side.
aspectRatio = (double)origImage.getWidth() / (double)origImage.getHeight(); //Get the aspect ratio of the picture.
fWidth = (int)Math.round(Width * aspectRatio); //Sets the width as created via the aspect ratio.
}
else if (origImage.getHeight() < origImage.getWidth()) //If the pictures width is greater than the height scale appropriately.
{
fWidth = Width; //Set the height to 60 as it is the biggest side.
aspectRatio = (double)origImage.getHeight() / (double)origImage.getWidth(); //Get the aspect ratio of the picture.
fHeight = (int)Math.round(Height * aspectRatio); //Sets the height as created via the aspect ratio.
}
int extraHeight = whiteSpace - fHeight;
int extraWidth = whiteSpace - fWidth;
BufferedImage resizedImage = new BufferedImage(whiteSpace, whiteSpace, type);
Graphics2D g = resizedImage.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, whiteSpace, whiteSpace);
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(origImage, extraWidth/2, extraHeight/2, fWidth, fHeight, null);
g.dispose();
ImageIO.write(resizedImage, "jpg", new File(destImg));
}
catch (IOException ex)
{
return false;
}
return true;
}
Really just need to know if their is something I can plug in that will bump up the quality or if I need to look at something else entirely.
EDIT: Picture comparison.
Source, just picked a random washing machine from google.
http://www.essexappliances.co.uk/images/categories/washing-machine.jpg
The same picture converted in Photoshop to what I need it to be.
http://imgur.com/78B1p
What it looks like being converted like this.
http://imgur.com/8WlXD
Scaling an image down over a large range is inherently dangerous (from the point of view of quality), especially using a single step.
The recommended method is to use a divide and conquer method. Basically, you scale the image down in steps of 50% until you reach your desired size.
So, I took the original image of 650x748 and scaled it down to fit within a 60x60 region (52x60).
Divide and conquer compared to one step...
public class TestImageResize {
public static void main(String[] args) {
new TestImageResize();
}
public TestImageResize() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new ScalePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ScalePane extends JPanel {
private BufferedImage original;
private BufferedImage scaled;
public ScalePane() {
try {
original = ImageIO.read(new File("path/to/master.jpg"));
scaled = getScaledInstanceToFit(original, new Dimension(60, 60));
ImageIO.write(scaled, "jpg", new File("scaled.jpg"));
BufferedImage image = new BufferedImage(52, 60, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawImage(original, 0, 0, 52, 60, this);
g2d.dispose();
ImageIO.write(image, "jpg", new File("test.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if (original != null) {
if (scaled != null) {
size.width = original.getWidth() + scaled.getWidth();
size.height = original.getHeight();
} else {
size.width = original.getWidth();
size.height = original.getHeight();
}
}
return size;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (original != null) {
int x = 0;
int y = (getHeight() - original.getHeight()) / 2;;
if (scaled != null) {
x = (getWidth() - (original.getWidth() + scaled.getWidth())) / 2;
} else {
x = (getWidth() - original.getWidth()) / 2;
}
g2d.drawImage(original, x, y, this);
if (scaled != null) {
x += original.getWidth();
y = (getHeight() - scaled.getHeight()) / 2;
g2d.drawImage(scaled, x, y, this);
}
}
g2d.dispose();
}
public BufferedImage getScaledInstanceToFit(BufferedImage img, Dimension size) {
float scaleFactor = getScaleFactorToFit(img, size);
return getScaledInstance(img, scaleFactor);
}
public float getScaleFactorToFit(BufferedImage img, Dimension size) {
float scale = 1f;
if (img != null) {
int imageWidth = img.getWidth();
int imageHeight = img.getHeight();
scale = getScaleFactorToFit(new Dimension(imageWidth, imageHeight), size);
}
return scale;
}
public float getScaleFactorToFit(Dimension original, Dimension toFit) {
float scale = 1f;
if (original != null && toFit != null) {
float dScaleWidth = getScaleFactor(original.width, toFit.width);
float dScaleHeight = getScaleFactor(original.height, toFit.height);
scale = Math.min(dScaleHeight, dScaleWidth);
}
return scale;
}
public float getScaleFactor(int iMasterSize, int iTargetSize) {
float scale = 1;
if (iMasterSize > iTargetSize) {
scale = (float) iTargetSize / (float) iMasterSize;
} else {
scale = (float) iTargetSize / (float) iMasterSize;
}
return scale;
}
public BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {
BufferedImage imgBuffer = null;
imgBuffer = getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
return imgBuffer;
}
protected BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, Object hint, boolean higherQuality) {
int targetWidth = (int) Math.round(img.getWidth() * dScaleFactor);
int targetHeight = (int) Math.round(img.getHeight() * dScaleFactor);
int type = (img.getTransparency() == Transparency.OPAQUE)
? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
if (targetHeight > 0 || targetWidth > 0) {
int w, h;
if (higherQuality) {
w = img.getWidth();
h = img.getHeight();
} else {
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
} else {
ret = new BufferedImage(1, 1, type);
}
return ret;
}
}
}
You may, also, find The Perils of Image.getScaledInstance() of interest.
The issue you are seeing is actually related to the resampling filter used for downscaling. Obviously, the one used by your library is a bad one for the situation. Nearest neighbor, bilinear and bicubic are typical bad examples to be used when downscaling. I don't know the exact resampling filter Photoshop uses, but I used 3-lobed lanczos and got the following result:
So, to solve your problem, you need to use a smarter resampling filter.
dutchman, this is why I maintain the imgscalr library -- to make this kind of stuff painfully easy.
In your example, a single method call would do the trick, right after your first ImageIO.read line:
origImage = ImageIO.read(new File(sourceImg));
you can do the following to get what you want (javadoc for this method):
origImage = Scalr.resize(origImage, Method.ULTRA_QUALITY, 60);
and if that still looked a little jagged (because you are removing so much information from the image, you can add the following OP to the command to apply a light anti-aliasing filter to the image so it looks smoother):
origImage = Scalr.resize(origImage, Method.ULTRA_QUALITY, 60, Scalr.OP_ANTIALIAS);
That will replace all the remainder of the code logic you have. The only other thing I would recommend is saving out your really small samples as PNG's so there is no more compression/lossy conversion done on the image OR make sure you use little to none compression on the JPG if you really want it in JPG format. (Here is an article on how to do it; it utilizes the ImageWriteParam class)
imgscalr is licensed under an Apache 2 license and hosted on GitHub so you can do what you want with it; it also includes asynchronous scaling support if you are using the library in a server-side app and queuing up huge numbers of scaling operations and don't want to kill the server.
As already stated, Java's Graphics2D does not provide a very good algorithm for down-scaling. If you don't want to implement a sophisticated algorithm yourself you could try out the current open source libs specialized for this: Thumbnailator, imgscalr and a Java interface for ImageMagick.
While researching for a private project I tried them out (except ImageMagick) and here are the visual results with Photoshop as reference:
A. Thumbnailator 0.4.8 with default settings (no additional internal resizing)
B. imgscalr 4.2 with ULTRA_QUALTY setting
C. Photoshop CS5 bicubic filter (save for web)
D. Graphics2d with all HQ render hints
Here is the used code
Thumbnailator and PS create similar results, while imgscalr seems to be softer. It is subjective which one of the libs creates the preferable results. Another point to consider though is the performance. While Thumbnailator and Graphics2d have similar runtime, imgscalr is considerably slower (with ULTRA_QUALITY) in my benchmarks.
For more info, read this post providing more detail on this matter.
i have an application from which i want to print an image. The image is loaded as a BufferedImage object. The problem is, when i print the image (to the postscript or to the pdf file), the quality is really poor.
When i'm using some other tools (basically any picture viewer application which can print the image) the result is significantly better.
I know there can be some problems with the DPI vs resolution but i'm not exactly sure how to compute the correct values for printing.
I tried to google and tried some methods, but nothing seems to work as i expected.
Basicaly i just want to print an image (in resolution let's say 3000x2000) to a printer (with DPI for example 600x600).
This is how i create the print job:
PrintRequestAttributeSet printAttributes = new HashPrintRequestAttributeSet();
printAttributes.add(PrintQuality.HIGH);
printAttributes.add(new PrinterResolution(600, 600 PrinterResolution.DPI));
printAttributes.add(new Destination(URI.create("file:/tmp/test.ps")));
PageFormat pf = printerJob.defaultPage();
Paper paper = pf.getPaper();
double xMargin = 0.0;
double yMargin = 0.0;
paper.setImageableArea(xMargin, yMargin, paper.getWidth() - 2 * xMargin, paper.getHeight() - 2 * yMargin);
pf.setPaper(paper);
// create new Printable for the specified image
printerJob.setPrintable(PrintableImage.get(image), pf)
if (printerJob.printDialog(printAttributes)) {
printerJob.print(printAttributes);
}
Where image is BufferedImage and PrintableImage.get returns new instance which implements Printable
Then the actual print is doing this way (i let the commented code which i tried but didn't work for me)
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
if (image == null)
throw new PrinterException("no image specified to be printed");
// We have only one page, and 'page' is zero-based
if (pageIndex > 0) {
return NO_SUCH_PAGE;
}
// tranlate the coordinates (according to the orientations, margins, etc)
Graphics2D printerGraphics = (Graphics2D) graphics;
//g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
//g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
printerGraphics.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
// THIS IS A TEST - javax.printing api uses 72 DPI, but we have 600DPI set for the printer
//AffineTransform at = printerGraphics.getTransform();
//printerGraphics.scale((double)72 / (double)600, (double)72 / (double)600);
//printerGraphics.drawRenderedImage(image, null);
//printerGraphics.setTransform(at);
//if(printerGraphics != null)
//return PAGE_EXISTS;
double scale = 72.0 / 600.0;
Dimension pictureSize = new Dimension((int)Math.round(image.getWidth() / scale), (int) Math.round(image.getHeight() / scale));
// center the image horizontaly and verticaly on the page
int xMargin = (int) ((pageFormat.getImageableWidth() - image.getWidth()) / 2);
int yMargin = (int) ((pageFormat.getImageableHeight() - image.getHeight()) / 2);
xMargin = yMargin = 0;
System.out.println(String.format("page size [%.2f x %.2f], picture size [%.2f x %.2f], margins [%d x %d]", pageFormat.getImageableWidth(), pageFormat.getImageableHeight(), pictureSize.getWidth(), pictureSize.getHeight(), xMargin, yMargin));
printerGraphics.drawImage(image, xMargin, yMargin, (int)pageFormat.getWidth(), (int)pageFormat.getHeight(), null);
//printerGraphics.drawImage(image, 0, 0, null);
//printerGraphics.drawImage(image, xMargin, yMargin, pictureSize.width, pictureSize.height, null);
//printerGraphics.drawImage(image, xMargin, yMargin, (int) pageFormat.getImageableWidth(), (int) pageFormat.getImageableHeight(), 0, 0, pictureSize.width, pictureSize.height, null);
//printerGraphics.drawImage(image, 0, 0, (int) pageFormat.getWidth() - xMargin, (int) pageFormat.getHeight() - yMargin, 0, 0, pictureSize.width, pictureSize.height, null);
return PAGE_EXISTS;
}
Does anybody solves the same problem?
Any help would be appreciated.
Thanks
Matej
This is how I did it. It works smoothly. Note that this code snippet doesn't center the image.
public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
if (pageIndex > 0) {
return (NO_SUCH_PAGE);
} else {
double pageHeight = pageFormat.getImageableHeight(), pageWidth = pageFormat.getImageableWidth();
Graphics2D g2d = (Graphics2D) g;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
if (pageHeight < image.getHeight() || pageWidth < image.getWidth()) {
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(image, 0, 0, (int) pageWidth, (int) pageHeight - textSize, null);
} else {
g2d.drawImage(image, 0, 0, null);
}
g2d.dispose();
return (PAGE_EXISTS);
}
}
#Viktor Fonic
Thank you for this, I was searching a lot to do this!
You're solution works perfectly, but has a small error,
textSize was not declared, so:
int textSize = (int) (pageHeight - image.getHeight()*pageWidth/image.getWidth());
I am a newbie to graphics. I've been using this code to make thumbnails of image files. When i use small files(~100KB) like wall papers, it works fine but when i use an image file(a photo) of size ~5MB, it produces just a few bytes(~1-8KB) of file which shows up as black image. It does not matter what Width and Height i give it. What could be going wrong here? Is it a difference between image types or the camera that produces the images? I'm sure the problem images are from a different camera than the non problematic ones. I am giving quality param as 100 to not miss out any detail that way...
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
int dx = thumbWidth, dy = thumbHeight;
Image image = Toolkit.getDefaultToolkit().createImage(file);
MediaTracker mediaTracker = new MediaTracker(new Container());
mediaTracker.addImage(image, 0);
mediaTracker.waitForID(0);
double thumbRatio = (double)thumbWidth / (double)thumbHeight;
int imageWidth = image.getWidth(null);
int imageHeight = image.getHeight(null);
double imageRatio = (double)imageWidth / (double)imageHeight;
if (thumbRatio < imageRatio) {
thumbHeight = (int)(thumbWidth / imageRatio);
} else {
thumbWidth = (int)(thumbHeight * imageRatio);
}
if(thumbWidth > dx) {
thumbWidth = dx;
thumbHeight = (int)(thumbWidth / imageRatio);
}
if(thumbHeight > dy)
{
thumbHeight = dy;
thumbWidth = (int) (thumbHeight*imageRatio);
}
log.debug("X="+thumbWidth+" Y="+thumbHeight);
BufferedImage thumbImage = new BufferedImage(thumbWidth, thumbHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = thumbImage.createGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(thumbImage);
quality = Math.max(0, Math.min(quality, 100));
param.setQuality((float)quality / 100.0f, false);
encoder.setJPEGEncodeParam(param);
encoder.encode(thumbImage);
log.debug("ThumbLength"+out.toByteArray().length);
FileOutputStream fos = new FileOutputStream("/root/testx.jpg");
fos.write(out.toByteArray());
fos.close();
} catch(Exception e) { log.debug(e.getMessage());}
return out.toByteArray();
You might try BufferedImage.TYPE_INT_ARGB, as shown here.
Also, your MediaTracker is waiting on the same thread; ImageIO.read() might be simpler.
Addendum: Also consider AffineTransformOp, although the src and dst must be different.
im trying to resize bufferdImage in memory in java but to keep the aspect ratio of the image
im have something like this but this is not good
int w = picture.getWidth();
int h = picture.getWidth();
int neww=w;
int newh=h;
int wfactor = w;
int hfactor = h;
if(w > DEFULT_PICTURE_WIDTH || h > DEFULT_PICTURE_HIGHT)
{
while(neww > DEFULT_PICTURE_WIDTH)
{
neww = wfactor /2;
newh = hfactor /2;
wfactor = neww;
hfactor = newh;
}
}
picture = Utils.resizePicture(picture,neww,newh);
Adding to Erik's point about getScaledInstance, if you moved away from it to using the recommended scaling mechanisms in Java2D, you might have noticed that your images look noticeably worse.
The reason for that is when the Java2D discouraged use of getScaledInstance and AreaAveragingScaleFilter, they didn't replace it with anything as easy to use in the API, instead we were left to our own devices using Java2D APIs directly. Fortunately, Chris Campbell (from the J2D team) followed up with the recommendation of using an incremental scaling technique that gives similar looking results to AreaAveragingScaleFilter and runs faster; unfortunately the code is of a decent size and doesn't address your original question of honoring proportions.
About 6 months ago I saw all these questions on SO again and again about "scaling images in Java" and eventually collected all the advice, did all the digging and research I could, and compiled all of into a single "best practices" image scaling library.
The API is dead simple as it is only 1 class and a bunch of static methods. Basic use looks like this:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 320);
This is the simplest call where the library will make a best-guess at the quality, honor your image proportions, and fit the result within a 320x320 bounding box. NOTE, the bounding box is just the maximum W/H used, since your image proportions are honored, the resulting image would still honor that, say 320x200.
If you want to override the automatic mode and force it to give you the best-looking result and even apply a very mild anti-alias filter to the result so it looks even better (especially good for thumbnails), that call would look like:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY,
150, 100, Scalr.OP_ANTIALIAS);
These are all just examples, the API is broad and covers everything from super-simple use cases to very specialized. You can even pass in your own BufferedImageOps to be applied to the image (and the library automatically fixes the 6-year BufferedImageOp JDK bug for you!)
There is a lot more to scaling images in Java successfully that the library does for you, for example always keeping the image in one of the best supported RGB or ARGB image types while operating on it. Under the covers the Java2D image processing pipeline falls back to an inferior software pipeline if the image type used for any image operations is poorly supported.
If all that sounded like a lot of headache, it sort of is... that's why I wrote the library and open sourced it, so folks could just resize their images and move on with their lives without needing to worry about it.
If width, height of source and target are known, use following function to determine scale of the image.
private double determineImageScale(int sourceWidth, int sourceHeight, int targetWidth, int targetHeight) {
double scalex = (double) targetWidth / sourceWidth;
double scaley = (double) targetHeight / sourceHeight;
return Math.min(scalex, scaley);
}
Then use this scale to scale up/down the image using following code
Image scaledImage = sourceBufferedImage.getScaledInstance((int) (width * scale), (int) (height * scale), Image.SCALE_SMOOTH);
For starters - take a look at line 2. Shouldnt that be getHeight()?
You dont want a while loop for the resizing, you want to find out the resizing ratio, which is a simple bit of math.
(width / height) = (new_width / new_height)
If you know one of the 'new' sizes, the other can be found via multiplication
new_height * (width / height) = new_width
You can also use the lazy method provided by BufferedImage's superclass Image, getScaledInstance() - using -1 for either width or height will maintain aspect ratio
ex:
scaledPic = picture.getScaledInstance(new_width, -1, Image.SCALE_FAST);
You may have a look at perils-of-image-getscaledinstance.html that explains why getScaledInstance(), used in some of the answers, should be avoided.
The article also provides alternative code.
I use these two methods to scale images, where max is the bigger dimension of your destination image. For 100x100 image it will be 100, for 200x300 image it will be 300.
public static BufferedImage scale(InputStream is, int max) {
Image image = null;
try {
image = ImageIO.read(is);
} catch (IOException e) {
e.printStackTrace();
}
int width = image.getWidth(null);
int height = image.getHeight(null);
double dWidth = 0;
double dHeight = 0;
if (width == height) {
dWidth = max;
dHeight = max;
}
else if (width > height) {
dWidth = max;
dHeight = ((double) height / (double) width) * max;
}
else {
dHeight = max;
dWidth = ((double) width / (double) height) * max;
}
image = image.getScaledInstance((int) dWidth, (int) dHeight, Image.SCALE_SMOOTH);
BufferedImage bImage = toBufferedImage(image);
return bImage;
}
public static BufferedImage toBufferedImage(Image img)
{
if (img instanceof BufferedImage)
{
return (BufferedImage) img;
}
BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics2D bGr = bimage.createGraphics();
bGr.drawImage(img, 0, 0, null);
bGr.dispose();
return bimage;
}
If you want to resize a picture of w0 x h0 to w1 x h1 by keeping the aspect ratio, then calculate the vertical and horizontal scale and select the smaller one.
double scalex = 1;
double scaley = 1;
if (scalingMode == ScalingMode.WINDOW_SIZE) {
scalex = (double)getWidth() / frontbuffer.getWidth();
scaley = (double)getHeight() / frontbuffer.getHeight();
} else
if (scalingMode == ScalingMode.KEEP_ASPECT) {
double sx = (double)getWidth() / frontbuffer.getWidth();
double sy = (double)getHeight() / frontbuffer.getHeight();
scalex = Math.min(sx, sy);
scaley = scalex;
// center the image
g2.translate((getWidth() - (frontbuffer.getWidth() * scalex)) / 2,
(getHeight() - (frontbuffer.getHeight() * scaley)) / 2);
}
g2.scale(scalex, scaley);
if (interpolation != ImageInterpolation.NONE) {
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolation.hint);
}
g2.drawImage(frontbuffer, 0, 0, null);
private static BufferedImage resize(BufferedImage img, int width, int height) {
double scalex = (double) width / img.getWidth();
double scaley = (double) height / img.getHeight();
double scale = Math.min(scalex, scaley);
int w = (int) (img.getWidth() * scale);
int h = (int) (img.getHeight() * scale);
Image tmp = img.getScaledInstance(w, h, Image.SCALE_SMOOTH);
BufferedImage resized = new BufferedImage(w, h, img.getType());
Graphics2D g2d = resized.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
return resized;
}