Quality of Image after resize very low -- Java - java

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.

Related

Fit image into JPanel

I'm trying scale an image so it will always fit my JPanel. Unfortunately using this method I don't always receive an Image I wanted to receive. Mostly it is zoomed and I would rather have the whole image but scaled.
Thats the class that creates the image. 600 is the PanelWidth and 400 is the PanelHeight.
Any ideas what goes wrong?
public class Image extends Component{
private BufferedImage img;
protected int width;
protected int height;
private String path;
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
double scale = getScale(600,400,img.getWidth(),img.getHeight());
double xPos = (600 - scale * img.getWidth())/2;
double yPos = (400 - scale *img.getHeight())/2;
AffineTransform at = AffineTransform.getTranslateInstance(xPos, yPos);
at.scale(scale, scale);
g2.drawRenderedImage(img, at);
System.out.println(scale);
}
public Image(String path){
try{
img = ImageIO.read(new File(path));
} catch (IOException e) { }
this.width=img.getWidth();
this.height=img.getHeight();
this.path = path;
}
public double getScale(int panelWidth, int panelHeight, int imageWidth, int imageHeight){
double scale = 1;
double xScale, yScale;
if(imageWidth > panelWidth || imageHeight > panelHeight){
xScale = (double)imageWidth/panelWidth;
yScale = (double)imageHeight/panelHeight;
scale = Math.max(xScale, yScale);
}else if(imageWidth < panelWidth && imageHeight < panelHeight){
xScale = (double)panelWidth/imageWidth;
yScale = (double)panelHeight/imageHeight;
scale = Math.max(xScale, yScale);
}else{
scale = 1;
}
return scale;
}
A JPanel is a Swing component which implies you are using Swing.
For custom painting you should extend JPanel or JComponent. Most people use JPanel because it will clear the background of the component for you.
Custom painting of a Swing component is done by overriding paintComponent(...)
so it will always fit my JPanel
Define "fit"?
Assuming you are trying to scale the image to retain its original proportions you could to something like:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
double imageWidth = image.getWidth(null);
double imageHeight = image.getHeight(null);
double factor = Math.min(getWidth() / imageWidth, getHeight() / imageHeight);
int width = (int)(image.getWidth(null) * factor);
int height = (int)(image.getHeight(null) * factor);
g.drawImage(image, 0, 0, width, height, this);
}
If you are just trying to fit the image on the panel then you do:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
You don't need to use float if you do your operations in the right order. Assuming imageWidth, imageHeight, panelWidth are all int:
// Calculate the width of the scaled image; if the image is wider than the
// panel, use the panel width, otherwise use the image width (i.e. don't upscale)
int scaledWidth = Math.min(imageWidth, panelWidth);
// Given the scaled width, calculate the scaled height
// Force it to be at least 1 pixel, since if you have an image that's wider than
// the panel and only 1 pixel tall, this will scale to zero height, which you
// don't want
int scaledHeight = Math.max(1, imageHeight * scaledWidth / imageWidth);
The above assumes you want to fit the width and will be providing a scrolling mechanism if the image height exceeds the panel height. If you want to fit height instead (and horizontal scroll for overflow) just make the necessary changes in variables.

printing bufferedimage to a printer

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

Bufferedimage resize

I am trying to resized a bufferedimage. I am able to store it and show up on a jframe no problems but I can't seem to resize it. Any tips on how I can change this to make it work and show the image as a 200*200 file would be great
private void profPic(){
String path = factory.getString("bottle");
BufferedImage img = ImageIO.read(new File(path));
}
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
int w = img.getWidth();
int h = img.getHeight();
BufferedImage dimg = new BufferedImage(newW, newH, img.getType());
Graphics2D g = dimg.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(img, 0, 0, newW, newH, 0, 0, w, h, null);
g.dispose();
return dimg;
}
Updated answer
I cannot recall why my original answer worked but having tested it in a separate environment, I agree, the original accepted answer doesn't work (why I said it did I cannot remember either). This, on the other hand, did work:
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
Image tmp = img.getScaledInstance(newW, newH, Image.SCALE_SMOOTH);
BufferedImage dimg = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = dimg.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
return dimg;
}
If all that is required is to resize a BufferedImage in the resize method, then the Thumbnailator library can do that fairly easily:
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
return Thumbnails.of(img).size(newW, newH).asBufferedImage();
}
The above code will resize the img to fit the dimensions of newW and newH while maintaining the aspect ratio of the original image.
If maintaining the aspect ratio is not required and resizing to exactly the given dimensions is required, then the forceSize method can be used in place of the size method:
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
return Thumbnails.of(img).forceSize(newW, newH).asBufferedImage();
}
Using the Image.getScaledInstance method will not guarantee that the aspect ratio of the original image will be maintained for the resized image, and furthermore, it is in general very slow.
Thumbnailator uses a technique to progressively resize the image which can be several times faster than Image.getScaledInstance while achieving an image quality which generally is comparable.
Disclaimer: I am the maintainer of this library.
Here's some code that I have used to resize bufferedimages, no frills, pretty quick:
public static BufferedImage scale(BufferedImage src, int w, int h)
{
BufferedImage img =
new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
int x, y;
int ww = src.getWidth();
int hh = src.getHeight();
int[] ys = new int[h];
for (y = 0; y < h; y++)
ys[y] = y * hh / h;
for (x = 0; x < w; x++) {
int newX = x * ww / w;
for (y = 0; y < h; y++) {
int col = src.getRGB(newX, ys[y]);
img.setRGB(x, y, col);
}
}
return img;
}
This class resize from a file and get the format name:
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream;
import org.apache.commons.io.IOUtils;
public class ImageResizer {
public static void main(String as[]) throws IOException{
File f = new File("C:/Users/samsungrob/Desktop/shuttle.jpg");
byte[] ba = resize(f, 600, 600);
IOUtils.write(ba, new FileOutputStream( new File("C:/Users/samsungrob/Desktop/shuttle_resized.jpg") ) );
}
public static byte[] resize(File file,
int maxWidth, int maxHeight) throws IOException{
int scaledWidth = 0, scaledHeight = 0;
BufferedImage img = ImageIO.read((ImageInputStream) file );
scaledWidth = maxWidth;
scaledHeight = (int) (img.getHeight() * ( (double) scaledWidth / img.getWidth() ));
if (scaledHeight> maxHeight) {
scaledHeight = maxHeight;
scaledWidth= (int) (img.getWidth() * ( (double) scaledHeight/ img.getHeight() ));
if (scaledWidth > maxWidth) {
scaledWidth = maxWidth;
scaledHeight = maxHeight;
}
}
Image resized = img.getScaledInstance( scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
BufferedImage buffered = new BufferedImage(scaledWidth, scaledHeight, Image.SCALE_REPLICATE);
buffered.getGraphics().drawImage(resized, 0, 0 , null);
String formatName = getFormatName( file ) ;
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(buffered,
formatName,
out);
return out.toByteArray();
}
private static String getFormatName(ImageInputStream iis) {
try {
// Find all image readers that recognize the image format
Iterator iter = ImageIO.getImageReaders(iis);
if (!iter.hasNext()) {
// No readers found
return null;
}
// Use the first reader
ImageReader reader = (ImageReader)iter.next();
// Close stream
iis.close();
// Return the format name
return reader.getFormatName();
} catch (IOException e) {
}
return null;
}
private static String getFormatName(File file) throws IOException {
return getFormatName( ImageIO.createImageInputStream(file) );
}
private static String getFormatName(InputStream is) throws IOException {
return getFormatName( ImageIO.createImageInputStream(is) );
}
}
This is a shortened version of what is actually happening in imgscalr, if you just want to use the "balanced" smoothing:
/**
* Takes a BufferedImage and resizes it according to the provided targetSize
*
* #param src the source BufferedImage
* #param targetSize maximum height (if portrait) or width (if landscape)
* #return a resized version of the provided BufferedImage
*/
private BufferedImage resize(BufferedImage src, int targetSize) {
if (targetSize <= 0) {
return src; //this can't be resized
}
int targetWidth = targetSize;
int targetHeight = targetSize;
float ratio = ((float) src.getHeight() / (float) src.getWidth());
if (ratio <= 1) { //square or landscape-oriented image
targetHeight = (int) Math.ceil((float) targetWidth * ratio);
} else { //portrait image
targetWidth = Math.round((float) targetHeight / ratio);
}
BufferedImage bi = new BufferedImage(targetWidth, targetHeight, src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); //produces a balanced resizing (fast and decent quality)
g2d.drawImage(src, 0, 0, targetWidth, targetHeight, null);
g2d.dispose();
return bi;
}
try the imgscalr library. Best lib i found- very fast, good quality and simple to use
BufferedImage thumbnail = Scalr.resize(image, 150);
deprecated link: http://www.thebuzzmedia.com/software/imgscalr-java-image-scaling-library/
Apache 2 License
Check this out, it helps:
BufferedImage bImage = ImageIO.read(new File(C:\image.jpg);
BufferedImage thumbnail = Scalr.resize(bImage, Scalr.Method.SPEED, Scalr.Mode.FIT_TO_WIDTH,
750, 150, Scalr.OP_ANTIALIAS);

High Quality thumbnail in java

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;

Rotating BufferedImage instances

I am having trouble getting a rotated BufferedImage to display. I think the rotation is working just fine, but I can't actually draw it to the screen. My code:
Class extends JPanel {
BufferedImage img;
int rotation = 0;
public void paintComponent(Graphics g) {
g.clearRect(0, 0, getWidth(), getHeight());
img2d = img.createGraphics();
img2d.rotate(Math.toRadians(rotation), img.getWidth() / 2, img.getHeight() / 2);
g.drawImage(img, imgx, imgy, null);
this.repaint();
}
}
This is not working for me. I could not find any way to draw the rotated img2d onto g.
EDIT: I have multiple objects that are being drawn onto g, so I can't rotate that. I need to be able to rotate things individually.
Maybe you should try using AffineTransform like this:
AffineTransform transform = new AffineTransform();
transform.rotate(radians, bufferedImage.getWidth() / 2, bufferedImage.getHeight() / 2);
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
bufferedImage = op.filter(bufferedImage, null);
Hope this helps.
I would use Graphics2D.drawImage(image, affinetranform, imageobserver).
The code example below rotates and translates an image to the center of the component. This is a screenshot of the result:
public static void main(String[] args) throws IOException {
JFrame frame = new JFrame("Test");
frame.add(new JComponent() {
BufferedImage image = ImageIO.read(
new URL("http://upload.wikimedia.org/wikipedia/en/2/24/Lenna.png"));
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// create the transform, note that the transformations happen
// in reversed order (so check them backwards)
AffineTransform at = new AffineTransform();
// 4. translate it to the center of the component
at.translate(getWidth() / 2, getHeight() / 2);
// 3. do the actual rotation
at.rotate(Math.PI / 4);
// 2. just a scale because this image is big
at.scale(0.5, 0.5);
// 1. translate the object so that you rotate it around the
// center (easier :))
at.translate(-image.getWidth() / 2, -image.getHeight() / 2);
// draw the image
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(image, at, null);
// continue drawing other stuff (non-transformed)
//...
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setVisible(true);
}
You are rotating the graphics for drawing into your image, not the image. Thats why you see no effect. Apply the rotation to the graphics you are painting on and it will draw the image rotated:
public void paintComponent(Graphics g) {
g.clearRect(0, 0, getWidth(), getHeight());
g.rotate(Math.toRadians(rotation), img.getWidth() / 2, img.getHeight() / 2);
g.drawImage(img, imgx, imgy, null);
this.repaint();
}
This will probably not draw entirely what you expect, the rotation will revolve around the coordinate origin. For the image to be rotate around its center you need to apply a coordinate translation before the rotation, for example:
g.translate(imgx >> 1, imgy >> 1);
The Graphics2D Tutorial has some more examples.
I know this question is old but I came up with a solution that has some advantages:
creates image of correct size.
correct offset.
does not unnecessarily rotate by 0° or 360°.
works for negative angles (e.g. -90°).
works when input is BufferedImage.TYPE_CUSTOM.
As it is, it is assumed that the angle is a multiple of 90°. The only improvement that one might need is to use an Enum for angle instead of just int.
Here's my code:
public static BufferedImage rotateBufferedImage(BufferedImage img, int angle) {
if (angle < 0) {
angle = 360 + (angle % 360);
}
angle %= 360;
if (angle == 0) {
return img;
}
final boolean r180 = angle == 180;
if (angle != 90 && !r180 && angle != 270)
throw new IllegalArgumentException("Invalid angle.");
final int w = r180 ? img.getWidth() : img.getHeight();
final int h = r180 ? img.getHeight() : img.getWidth();
final int type = img.getType() == BufferedImage.TYPE_CUSTOM ? BufferedImage.TYPE_INT_ARGB : img.getType();
final BufferedImage rotated = new BufferedImage(w, h, type);
final Graphics2D graphic = rotated.createGraphics();
graphic.rotate(Math.toRadians(angle), w / 2d, h / 2d);
final int offset = r180 ? 0 : (w - h) / 2;
graphic.drawImage(img, null, offset, -offset);
graphic.dispose();
return rotated;
}
public static BufferedImage rotateBufferedImage(String img, int angle) throws IOException {
return rotateBufferedImage(Paths.get(img), angle);
}
public static BufferedImage rotateBufferedImage(Path img, int angle) throws IOException {
return rotateBufferedImage(ImageIO.read(img.toFile()), angle);
}

Categories