Creating thumbnail views of images using Java - java

I have an application where i need to select a folder containing pictures and need to display a thumbnail view of those images using Java . I dont have any idea as to how to represent images in a thumbnail format .
Any resources like code examples , theory or links would be really helpful.
Thank You

I have this code that I use in one of my projects. I found on the net a while ago (not sure where but if anyone recognises it please let me know so I can reference it):
private static byte[] createThumbnail(byte[] bytes)
{
try
{
double scale;
int sizeDifference, originalImageLargestDim;
Image inImage = ImageIO.read(new ByteArrayInputStream(bytes));
//find biggest dimension
if(inImage.getWidth(null) > inImage.getHeight(null))
{
scale = (double)LARGEST_DIMENSION/(double)inImage.getWidth(null);
sizeDifference = inImage.getWidth(null) - LARGEST_DIMENSION;
originalImageLargestDim = inImage.getWidth(null);
}
else
{
scale = (double)LARGEST_DIMENSION/(double)inImage.getHeight(null);
sizeDifference = inImage.getHeight(null) - LARGEST_DIMENSION;
originalImageLargestDim = inImage.getHeight(null);
}
//create an image buffer to draw to
BufferedImage outImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); //arbitrary init so code compiles
Graphics2D g2d;
AffineTransform tx;
if(scale < 1.0d) //only scale if desired size is smaller than original
{
int numSteps = sizeDifference / 100;
int stepSize = sizeDifference / numSteps;
int stepWeight = stepSize/2;
int heavierStepSize = stepSize + stepWeight;
int lighterStepSize = stepSize - stepWeight;
int currentStepSize, centerStep;
double scaledW = inImage.getWidth(null);
double scaledH = inImage.getHeight(null);
if(numSteps % 2 == 1) //if there's an odd number of steps
centerStep = (int)Math.ceil((double)numSteps / 2d); //find the center step
else
centerStep = -1; //set it to -1 so it's ignored later
Integer intermediateSize = originalImageLargestDim, previousIntermediateSize = originalImageLargestDim;
for(Integer i=0; i<numSteps; i++)
{
if(i+1 != centerStep) //if this isn't the center step
{
if(i == numSteps-1) //if this is the last step
{
//fix the stepsize to account for decimal place errors previously
currentStepSize = previousIntermediateSize - LARGEST_DIMENSION;
}
else
{
if(numSteps - i > numSteps/2) //if we're in the first half of the reductions
currentStepSize = heavierStepSize;
else
currentStepSize = lighterStepSize;
}
}
else //center step, use natural step size
{
currentStepSize = stepSize;
}
intermediateSize = previousIntermediateSize - currentStepSize;
scale = (double)intermediateSize/(double)previousIntermediateSize;
scaledW = (int)scaledW*scale;
scaledH = (int)scaledH*scale;
outImage = new BufferedImage((int)scaledW, (int)scaledH, BufferedImage.TYPE_INT_RGB);
g2d = outImage.createGraphics();
g2d.setBackground(Color.WHITE);
g2d.clearRect(0, 0, outImage.getWidth(), outImage.getHeight());
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
tx = new AffineTransform();
tx.scale(scale, scale);
g2d.drawImage(inImage, tx, null);
g2d.dispose();
inImage = new ImageIcon(outImage).getImage();
previousIntermediateSize = intermediateSize;
}
}
else
{
//just copy the original
outImage = new BufferedImage(inImage.getWidth(null), inImage.getHeight(null), BufferedImage.TYPE_INT_RGB);
g2d = outImage.createGraphics();
g2d.setBackground(Color.WHITE);
g2d.clearRect(0, 0, outImage.getWidth(), outImage.getHeight());
tx = new AffineTransform();
tx.setToIdentity(); //use identity matrix so image is copied exactly
g2d.drawImage(inImage, tx, null);
g2d.dispose();
}
//JPEG-encode the image and write to file.
ByteArrayOutputStream os = new ByteArrayOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os);
encoder.encode(outImage);
return os.toByteArray();
}
catch(Exception e)
{
throw new RuntimeException(e);
}
}

The following code scales the entire image into an area. You can copy-paste the code and run it to see what it does.
The interesting call is g2d.drawImage(img, 0, 0, thumb.getWidth() - 1, thumb.getHeight() - 1, 0, 0, img.getWidth() - 1, img.getHeight() - 1, null); which copies the image into the thumbnail, scaling it to fit.
If you want different scalings to preserve the aspect ratio you could decide to use scale() on the g2d, or select a different source coordinates.
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class ThumbnailFactory {
public ThumbnailFactory() {
}
public void run(String folder) {
File dir = new File(folder);
for (File file : dir.listFiles()) {
createThumbnail(file);
}
}
private void createThumbnail(File file) {
try {
// BufferedImage is the best (Toolkit images are less flexible)
BufferedImage img = ImageIO.read(file);
BufferedImage thumb = createEmptyThumbnail();
// BufferedImage has a Graphics2D
Graphics2D g2d = (Graphics2D) thumb.getGraphics();
g2d.drawImage(img, 0, 0,
thumb.getWidth() - 1,
thumb.getHeight() - 1,
0, 0,
img.getWidth() - 1,
img.getHeight() - 1,
null);
g2d.dispose();
ImageIO.write(thumb, "PNG", createOutputFile(file));
} catch (Exception e) {
e.printStackTrace();
}
}
private File createOutputFile(File inputFile) {
// You'll want something better than this...
return new File(inputFile.getAbsolutePath()
+ ".thumb.png");
}
private BufferedImage createEmptyThumbnail() {
return new BufferedImage(100, 200,
BufferedImage.TYPE_INT_RGB);
}
public static void main(String[] args) {
ThumbnailFactory fac = new ThumbnailFactory();
fac.run("c:\\images");
}
}

Related

A program that cuts GIFs into frames and converts them into pixels is using too much memory

I wrote a code which splits a gif-image into frames.
Then, they are scaled-down into smaller ones, the resolution is 128 * 128 pixels. Then they combine a full frame with a specific resolution.
The getFrames (String path) method gets the GIF file along the path from the jar and creates image frames.
The getResizedimages (…) method cuts the frame into smaller 128 by 128 images.
The getPixels (Bufferedlmage image) method gets an array of pixels from an image.
The problem is that during the execution of this code (via the test method), a very very large amount of memory is used even at the stage of execution of the getFrames (…) method or the getFramesRaw (…) method.
import com.sun.imageio.plugins.gif.GIFImageReader;
import com.sun.imageio.plugins.gif.GIFImageReaderSpi;
import org.apache.commons.io.FileUtils;
import org.bukkit.map.MapPalette;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class Gif {
static void test(String path, int height, int width) {
List<BufferedImage> images = getFrames(path);
for (int x = 0; x < width; x ++) for (int y = 0; y < height; y ++)
getResizedFrames(images, width, height, x, y).forEach(image -> getPixels(image));
}
#SuppressWarnings("deprecation")
public static byte[] getPixels(BufferedImage image) {
int pixelCount = image.getWidth() * image.getHeight();
int[] pixels = new int[pixelCount];
image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
byte[] colors = new byte[pixelCount];
for (int i = 0; i < pixelCount; i++)
colors[i] = MapPalette.matchColor(new Color(pixels[i], true));
return colors;
}
public static BufferedImage getScaledImage(BufferedImage image) {
BufferedImage newImg = new BufferedImage(128, 128, 3);
Graphics2D g2 = newImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(image, 0, 0, 128, 128, null);
g2.dispose();
return newImg;
}
public static BufferedImage cropImage(BufferedImage completeImg, int width, int height, int xCoord, int yCoord) {
int coordWidth = completeImg.getWidth() / width;
int coordHeight = completeImg.getHeight() / height;
BufferedImage croppedImg = new BufferedImage(completeImg.getWidth(), completeImg.getHeight(), 1);
Graphics2D g2 = (Graphics2D)croppedImg.getGraphics();
g2.drawImage(completeImg, 0, 0, completeImg.getWidth(), completeImg.getHeight(), null);
g2.dispose();
BufferedImage image = croppedImg.getSubimage(coordWidth * xCoord, coordHeight * yCoord, coordWidth, coordHeight);
return getScaledImage(image);
}
public static java.util.List<BufferedImage> getFramesRaw(String path) {
java.util.List<BufferedImage> frames = new ArrayList<>();
try {
File file = File.createTempFile("data", ".gif");
InputStream stream = Main.class.getResourceAsStream(path);
FileUtils.copyInputStreamToFile(stream, file);
stream.close();
ImageInputStream inputStream = ImageIO.createImageInputStream(file);
ImageReader ir = new GIFImageReader(new GIFImageReaderSpi());
ir.setInput(inputStream);
int number = ir.getNumImages(true);
for (int i = 0; i < number; i++) frames.add(ir.read(i));
inputStream.close();
System.gc();
} catch (Exception e) {
e.printStackTrace();
}
return frames;
}
public static java.util.List<BufferedImage> getFrames(String path) {
java.util.List<BufferedImage> copies = new ArrayList<>();
java.util.List<BufferedImage> frames = getFramesRaw(path);
copies.add(frames.remove(0));
int width = copies.get(0).getWidth(), height = copies.get(0).getHeight();
for (BufferedImage frame : frames) {
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = img.getGraphics();
g.drawImage(copies.get(copies.size()-1),0,0,null);
g.drawImage(frame,0,0,null);
copies.add(img);
}
return copies;
}
public static java.util.List<BufferedImage> getResizedFrames(java.util.List<BufferedImage> frames, int width, int height, int x, int y) {
List<BufferedImage> copies = new ArrayList<>();
for (BufferedImage image : frames)
copies.add(cropImage(image, width, height, width - 1 - x, height - 1 - y));
return copies;
}
}
I need your help.
The reason your program uses a lot of memory, is because your code operates at all the frames at each step (in many small loops). All the decoded image data for all the frames will be held in memory at the same time, resulting in unnecessary memory usage. While this may feel logical, it is for sure not efficient.
Instead, create one loop that does all the needed operations for a single frame, before moving on to the next.
Something like this:
public static void test(String path) throws IOException {
List<BufferedImage> thumbnails = new ArrayList<>();
readFrames(path, image -> thumbnails.add(getScaledImage(image)));
}
private static void readFrames(String path, Consumer<BufferedImage> forEachFrame) throws IOException {
try (ImageInputStream inputStream = ImageIO.createImageInputStream(GifSplitter.class.getResourceAsStream(path))) {
Iterator<ImageReader> readers = ImageIO.getImageReaders(inputStream); // ImageIO detects format, no need to hardcode GIF plugin
if (!readers.hasNext()) {
return;
}
ImageReader ir = readers.next();
ir.setInput(inputStream);
// For GIF format (which does not contain frame count in header),
// it's more efficient to just read each frame until IOOBE occurs,
// instead of invoking getNumImages(true)
int i = 0;
while (i >= 0) {
try {
BufferedImage image = ir.read(i++);
// crop, scale, etc, for a SINGLE image
forEachFrame.accept(image);
}
catch (IndexOutOfBoundsException endOfGif) {
// No more frames
break;
}
}
ir.dispose();
}
}

Antialiasing on JButton Icon [duplicate]

I made this code to resize images with two factors. It works, but the quality of image is very bad after it is resized! Can you help me?
This is the code
public class ImageTest {
private static final int factor1 = 3;
private static final int factor2 = 4;
public static void main(String [] args){
JFileChooser cs = new JFileChooser();
cs.setFileSelectionMode(cs.DIRECTORIES_ONLY);
int i = cs.showOpenDialog(null);
if(i==cs.APPROVE_OPTION){
File f = cs.getSelectedFile();
File[] ff = f.listFiles();
for(int j=0;j<ff.length;j++){
String end = ff[j].getName().substring(ff[j].getName().indexOf(".")+1);
System.out.println(end);
try{
BufferedImage originalImage = ImageIO.read(ff[j]);
int type = originalImage.getType() == 0? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
BufferedImage resizeImageJpg = resizeImageWithHint(originalImage, type);
ImageIO.write(resizeImageJpg, end, new File("pr/"+ff[j].getName()));
}catch(IOException e){
e.printStackTrace();
}
}
}
}
private static BufferedImage resizeImageWithHint(BufferedImage originalImage, int type){
int IMG_WIDTH = (originalImage.getWidth()*factor1)/factor2;
int IMG_HEIGHT = (originalImage.getHeight()*factor1)/factor2;
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();
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);
return resizedImage;
}
}
I saw on web that resizeImageWithHint is done within the scope so as not to lose quality.. but it does! why? can you help me with this?
The best article I have ever read on this topic is The Perils of Image.getScaledInstance() (web archive).
In short: You need to use several resizing steps in order to get a good image. Helper method from the article:
public BufferedImage getScaledInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality)
{
int type = (img.getTransparency() == Transparency.OPAQUE) ?
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage)img;
int w, h;
if (higherQuality) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
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(w, h, 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);
return ret;
}
The following code produced me highest quality resize with aspect ratio preserved.
Tried few things and read several entries presented here in other answers. Lost two days and in the end I got the best result with plain Java method (tried also ImageMagick and java-image-scaling libraries):
public static boolean resizeUsingJavaAlgo(String source, File dest, int width, int height) throws IOException {
BufferedImage sourceImage = ImageIO.read(new FileInputStream(source));
double ratio = (double) sourceImage.getWidth()/sourceImage.getHeight();
if (width < 1) {
width = (int) (height * ratio + 0.4);
} else if (height < 1) {
height = (int) (width /ratio + 0.4);
}
Image scaled = sourceImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedScaled.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.drawImage(scaled, 0, 0, width, height, null);
dest.createNewFile();
writeJpeg(bufferedScaled, dest.getCanonicalPath(), 1.0f);
return true;
}
/**
* Write a JPEG file setting the compression quality.
*
* #param image a BufferedImage to be saved
* #param destFile destination file (absolute or relative path)
* #param quality a float between 0 and 1, where 1 means uncompressed.
* #throws IOException in case of problems writing the file
*/
private static void writeJpeg(BufferedImage image, String destFile, float quality)
throws IOException {
ImageWriter writer = null;
FileImageOutputStream output = null;
try {
writer = ImageIO.getImageWritersByFormatName("jpeg").next();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);
output = new FileImageOutputStream(new File(destFile));
writer.setOutput(output);
IIOImage iioImage = new IIOImage(image, null, null);
writer.write(null, iioImage, param);
} catch (IOException ex) {
throw ex;
} finally {
if (writer != null) {
writer.dispose();
}
if (output != null) {
output.close();
}
}
}
Know question is old... I've tried different solutions surfing then web, I got the best result using getScaledInstance(), supplying Image.SCALE_SMOOTH as argument. In fact the resulting image quality was really better.
My code below:
final int THUMB_SIDE = 140;
try {
BufferedImage masterImage = ImageIO.read(startingImage);
BufferedImage thumbImage = new BufferedImage(THUMB_SIDE, THUMB_SIDE, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = thumbImage.createGraphics();
g2d.drawImage(masterImage.getScaledInstance(THUMB_SIDE, THUMB_SIDE, Image.SCALE_SMOOTH), 0, 0, THUMB_SIDE, THUMB_SIDE, null);
g2d.dispose();
String thumb_path = path.substring(0, path.indexOf(".png")) + "_thumb.png";
ImageIO.write(thumbImage, "png", new File(thumb_path));
} catch (IOException e) {
e.printStackTrace();
}
If your image source is a png then use like this:
Image imgSmall = imgBig.getScaledInstance(
targetWidth, targetHeight, Image.SCALE_SMOOTH);
If you want to resize jpeg or gif without loose too much quality, I made a library in 2010 for this: beautylib on github that uses internally this other library: java-image-scaling. You can see directly the source code to find something useful: https://github.com/felipelalli/beautylib/blob/master/src/br/eti/fml/beautylib/ResizeImage.java
None of the answers will help you to get real quality you desire. Include thumbailator.jar in your project (download it form here):
https://code.google.com/p/thumbnailator/
https://code.google.com/p/thumbnailator/wiki/Downloads?tm=2
Then upload the image first (as file, without Thumbnailator - it's use is to create thumbs, but you can create large images with it of course), and resize it to every dimensions you want (with Thumbnailator 800x600 for example). Quality will be very good. I was searching for this long time, this .jar helped me to achieve what i want.
Yes, I had the same problems and solved them, please read my question (answer is embedded in the question). I tried imgscalr and java-image-scaling libraries and found the second much better quality. Get close to the monitor to appreciate the difference between the thumbnail examples.
Despite my initial thoughts, resizing an image seems a very complicate thing, you don't want to do it yourself. For example I tell java-image-scaling to use ResampleFilters.getLanczos3Filter() to have better result.
It also addresses how to save a JPG with a quality higher than the standard 75, which produces a bad result especially for a thumbnail.
I also wrote a small class, called MyImage to help with common tasks, such as reading an image from a byte array, from a file, scaling by specifying only width or only height, scaling by specifying a bounding box, scaling by specifying width and height and adding a white band to make the image not distorted and writing to JPG file.
Answer: Remove the hints VALUE_INTERPOLATION_BILINEAR, and VALUE_RENDERING_QUALITY.
Example:
public static BufferedImage resizeImage(BufferedImage image, int width, int height) {
// Temporary image
BufferedImage tmp = image;
// Result image
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Graphics object
Graphics2D graphics = (Graphics2D)result.createGraphics();
// Add rendering hints
graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
graphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
// Draw tmp
graphics.drawImage(tmp, 0, 0, width, height, null);
// Dispose of graphics object
graphics.dispose();
// Return image result
return result;
}
Note: For some reason, the hints VALUE_INTERPOLATION_BILINEAR and VALUE_RENDERING_QUALITY blur the image being resized.
All the methods posted does'nt work for me i have to reduce QrCode size, but with above methods the quality is poor and scanner doesn't work , if i take original picture and resize it in paint the scanner is working.
The do while loop used in The Perils of Image.getScaledInstance() will run into an infinite loop, given those values,
w = 606; h = 505, targetWidth = 677, targetHeight = 505
Here is a simplied testing code, you can try it.
public class LoopTest {
public static void main(String[] args) {
new LoopTest(true, 606, 505, 677, 505);
}
public LoopTest(boolean higherQuality, int w, int h, int targetWidth, int targetHeight) {
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
} while (w != targetWidth || h != targetHeight); // TODO Auto-generated constructor stub
}
}
A quick work around: define an index for loop count. If the index is >=10, break out of loop.

Is there the better way to resize the Buffered image without losing of quality? [duplicate]

I made this code to resize images with two factors. It works, but the quality of image is very bad after it is resized! Can you help me?
This is the code
public class ImageTest {
private static final int factor1 = 3;
private static final int factor2 = 4;
public static void main(String [] args){
JFileChooser cs = new JFileChooser();
cs.setFileSelectionMode(cs.DIRECTORIES_ONLY);
int i = cs.showOpenDialog(null);
if(i==cs.APPROVE_OPTION){
File f = cs.getSelectedFile();
File[] ff = f.listFiles();
for(int j=0;j<ff.length;j++){
String end = ff[j].getName().substring(ff[j].getName().indexOf(".")+1);
System.out.println(end);
try{
BufferedImage originalImage = ImageIO.read(ff[j]);
int type = originalImage.getType() == 0? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
BufferedImage resizeImageJpg = resizeImageWithHint(originalImage, type);
ImageIO.write(resizeImageJpg, end, new File("pr/"+ff[j].getName()));
}catch(IOException e){
e.printStackTrace();
}
}
}
}
private static BufferedImage resizeImageWithHint(BufferedImage originalImage, int type){
int IMG_WIDTH = (originalImage.getWidth()*factor1)/factor2;
int IMG_HEIGHT = (originalImage.getHeight()*factor1)/factor2;
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();
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);
return resizedImage;
}
}
I saw on web that resizeImageWithHint is done within the scope so as not to lose quality.. but it does! why? can you help me with this?
The best article I have ever read on this topic is The Perils of Image.getScaledInstance() (web archive).
In short: You need to use several resizing steps in order to get a good image. Helper method from the article:
public BufferedImage getScaledInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality)
{
int type = (img.getTransparency() == Transparency.OPAQUE) ?
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage)img;
int w, h;
if (higherQuality) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
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(w, h, 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);
return ret;
}
The following code produced me highest quality resize with aspect ratio preserved.
Tried few things and read several entries presented here in other answers. Lost two days and in the end I got the best result with plain Java method (tried also ImageMagick and java-image-scaling libraries):
public static boolean resizeUsingJavaAlgo(String source, File dest, int width, int height) throws IOException {
BufferedImage sourceImage = ImageIO.read(new FileInputStream(source));
double ratio = (double) sourceImage.getWidth()/sourceImage.getHeight();
if (width < 1) {
width = (int) (height * ratio + 0.4);
} else if (height < 1) {
height = (int) (width /ratio + 0.4);
}
Image scaled = sourceImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedScaled.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.drawImage(scaled, 0, 0, width, height, null);
dest.createNewFile();
writeJpeg(bufferedScaled, dest.getCanonicalPath(), 1.0f);
return true;
}
/**
* Write a JPEG file setting the compression quality.
*
* #param image a BufferedImage to be saved
* #param destFile destination file (absolute or relative path)
* #param quality a float between 0 and 1, where 1 means uncompressed.
* #throws IOException in case of problems writing the file
*/
private static void writeJpeg(BufferedImage image, String destFile, float quality)
throws IOException {
ImageWriter writer = null;
FileImageOutputStream output = null;
try {
writer = ImageIO.getImageWritersByFormatName("jpeg").next();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);
output = new FileImageOutputStream(new File(destFile));
writer.setOutput(output);
IIOImage iioImage = new IIOImage(image, null, null);
writer.write(null, iioImage, param);
} catch (IOException ex) {
throw ex;
} finally {
if (writer != null) {
writer.dispose();
}
if (output != null) {
output.close();
}
}
}
Know question is old... I've tried different solutions surfing then web, I got the best result using getScaledInstance(), supplying Image.SCALE_SMOOTH as argument. In fact the resulting image quality was really better.
My code below:
final int THUMB_SIDE = 140;
try {
BufferedImage masterImage = ImageIO.read(startingImage);
BufferedImage thumbImage = new BufferedImage(THUMB_SIDE, THUMB_SIDE, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = thumbImage.createGraphics();
g2d.drawImage(masterImage.getScaledInstance(THUMB_SIDE, THUMB_SIDE, Image.SCALE_SMOOTH), 0, 0, THUMB_SIDE, THUMB_SIDE, null);
g2d.dispose();
String thumb_path = path.substring(0, path.indexOf(".png")) + "_thumb.png";
ImageIO.write(thumbImage, "png", new File(thumb_path));
} catch (IOException e) {
e.printStackTrace();
}
If your image source is a png then use like this:
Image imgSmall = imgBig.getScaledInstance(
targetWidth, targetHeight, Image.SCALE_SMOOTH);
If you want to resize jpeg or gif without loose too much quality, I made a library in 2010 for this: beautylib on github that uses internally this other library: java-image-scaling. You can see directly the source code to find something useful: https://github.com/felipelalli/beautylib/blob/master/src/br/eti/fml/beautylib/ResizeImage.java
None of the answers will help you to get real quality you desire. Include thumbailator.jar in your project (download it form here):
https://code.google.com/p/thumbnailator/
https://code.google.com/p/thumbnailator/wiki/Downloads?tm=2
Then upload the image first (as file, without Thumbnailator - it's use is to create thumbs, but you can create large images with it of course), and resize it to every dimensions you want (with Thumbnailator 800x600 for example). Quality will be very good. I was searching for this long time, this .jar helped me to achieve what i want.
Yes, I had the same problems and solved them, please read my question (answer is embedded in the question). I tried imgscalr and java-image-scaling libraries and found the second much better quality. Get close to the monitor to appreciate the difference between the thumbnail examples.
Despite my initial thoughts, resizing an image seems a very complicate thing, you don't want to do it yourself. For example I tell java-image-scaling to use ResampleFilters.getLanczos3Filter() to have better result.
It also addresses how to save a JPG with a quality higher than the standard 75, which produces a bad result especially for a thumbnail.
I also wrote a small class, called MyImage to help with common tasks, such as reading an image from a byte array, from a file, scaling by specifying only width or only height, scaling by specifying a bounding box, scaling by specifying width and height and adding a white band to make the image not distorted and writing to JPG file.
Answer: Remove the hints VALUE_INTERPOLATION_BILINEAR, and VALUE_RENDERING_QUALITY.
Example:
public static BufferedImage resizeImage(BufferedImage image, int width, int height) {
// Temporary image
BufferedImage tmp = image;
// Result image
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Graphics object
Graphics2D graphics = (Graphics2D)result.createGraphics();
// Add rendering hints
graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
graphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
// Draw tmp
graphics.drawImage(tmp, 0, 0, width, height, null);
// Dispose of graphics object
graphics.dispose();
// Return image result
return result;
}
Note: For some reason, the hints VALUE_INTERPOLATION_BILINEAR and VALUE_RENDERING_QUALITY blur the image being resized.
All the methods posted does'nt work for me i have to reduce QrCode size, but with above methods the quality is poor and scanner doesn't work , if i take original picture and resize it in paint the scanner is working.
The do while loop used in The Perils of Image.getScaledInstance() will run into an infinite loop, given those values,
w = 606; h = 505, targetWidth = 677, targetHeight = 505
Here is a simplied testing code, you can try it.
public class LoopTest {
public static void main(String[] args) {
new LoopTest(true, 606, 505, 677, 505);
}
public LoopTest(boolean higherQuality, int w, int h, int targetWidth, int targetHeight) {
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
} while (w != targetWidth || h != targetHeight); // TODO Auto-generated constructor stub
}
}
A quick work around: define an index for loop count. If the index is >=10, break out of loop.

Java rotating an ImageBuffer fails

I am trying to rotate an instance of a BufferImage named pic when I try this it resizes and skews and crops the image, any advice to get it to work properly
public void rotate(double rads){
AffineTransform tx = new AffineTransform();
tx.rotate(rads,pic.getWidth()/2,pic.getHeight()/2);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
pic = op.filter(pic, null);
}
When I have it rotate 90˚ it works fine so I'm wondering if the problem is that it is the shape of the image?
For use with AffineTransform, you can square an image using something like this:
private BufferedImage getImage(String name) {
BufferedImage image;
try {
image = ImageIO.read(new File(name));
} catch (IOException ioe) {
return errorImage;
}
int w = image.getWidth();
int h = image.getHeight();
int max = Math.max(w, h);
max = (int) Math.sqrt(2 * max * max);
BufferedImage square = new BufferedImage(
max, max, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = square.createGraphics();
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawImage(image, (max - w) / 2, (max - h) / 2, null);
g2d.dispose();
return square;
}

Re-sizing an image without losing quality

I made this code to resize images with two factors. It works, but the quality of image is very bad after it is resized! Can you help me?
This is the code
public class ImageTest {
private static final int factor1 = 3;
private static final int factor2 = 4;
public static void main(String [] args){
JFileChooser cs = new JFileChooser();
cs.setFileSelectionMode(cs.DIRECTORIES_ONLY);
int i = cs.showOpenDialog(null);
if(i==cs.APPROVE_OPTION){
File f = cs.getSelectedFile();
File[] ff = f.listFiles();
for(int j=0;j<ff.length;j++){
String end = ff[j].getName().substring(ff[j].getName().indexOf(".")+1);
System.out.println(end);
try{
BufferedImage originalImage = ImageIO.read(ff[j]);
int type = originalImage.getType() == 0? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
BufferedImage resizeImageJpg = resizeImageWithHint(originalImage, type);
ImageIO.write(resizeImageJpg, end, new File("pr/"+ff[j].getName()));
}catch(IOException e){
e.printStackTrace();
}
}
}
}
private static BufferedImage resizeImageWithHint(BufferedImage originalImage, int type){
int IMG_WIDTH = (originalImage.getWidth()*factor1)/factor2;
int IMG_HEIGHT = (originalImage.getHeight()*factor1)/factor2;
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();
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);
return resizedImage;
}
}
I saw on web that resizeImageWithHint is done within the scope so as not to lose quality.. but it does! why? can you help me with this?
The best article I have ever read on this topic is The Perils of Image.getScaledInstance() (web archive).
In short: You need to use several resizing steps in order to get a good image. Helper method from the article:
public BufferedImage getScaledInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality)
{
int type = (img.getTransparency() == Transparency.OPAQUE) ?
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage)img;
int w, h;
if (higherQuality) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
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(w, h, 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);
return ret;
}
The following code produced me highest quality resize with aspect ratio preserved.
Tried few things and read several entries presented here in other answers. Lost two days and in the end I got the best result with plain Java method (tried also ImageMagick and java-image-scaling libraries):
public static boolean resizeUsingJavaAlgo(String source, File dest, int width, int height) throws IOException {
BufferedImage sourceImage = ImageIO.read(new FileInputStream(source));
double ratio = (double) sourceImage.getWidth()/sourceImage.getHeight();
if (width < 1) {
width = (int) (height * ratio + 0.4);
} else if (height < 1) {
height = (int) (width /ratio + 0.4);
}
Image scaled = sourceImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedScaled.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.drawImage(scaled, 0, 0, width, height, null);
dest.createNewFile();
writeJpeg(bufferedScaled, dest.getCanonicalPath(), 1.0f);
return true;
}
/**
* Write a JPEG file setting the compression quality.
*
* #param image a BufferedImage to be saved
* #param destFile destination file (absolute or relative path)
* #param quality a float between 0 and 1, where 1 means uncompressed.
* #throws IOException in case of problems writing the file
*/
private static void writeJpeg(BufferedImage image, String destFile, float quality)
throws IOException {
ImageWriter writer = null;
FileImageOutputStream output = null;
try {
writer = ImageIO.getImageWritersByFormatName("jpeg").next();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);
output = new FileImageOutputStream(new File(destFile));
writer.setOutput(output);
IIOImage iioImage = new IIOImage(image, null, null);
writer.write(null, iioImage, param);
} catch (IOException ex) {
throw ex;
} finally {
if (writer != null) {
writer.dispose();
}
if (output != null) {
output.close();
}
}
}
Know question is old... I've tried different solutions surfing then web, I got the best result using getScaledInstance(), supplying Image.SCALE_SMOOTH as argument. In fact the resulting image quality was really better.
My code below:
final int THUMB_SIDE = 140;
try {
BufferedImage masterImage = ImageIO.read(startingImage);
BufferedImage thumbImage = new BufferedImage(THUMB_SIDE, THUMB_SIDE, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = thumbImage.createGraphics();
g2d.drawImage(masterImage.getScaledInstance(THUMB_SIDE, THUMB_SIDE, Image.SCALE_SMOOTH), 0, 0, THUMB_SIDE, THUMB_SIDE, null);
g2d.dispose();
String thumb_path = path.substring(0, path.indexOf(".png")) + "_thumb.png";
ImageIO.write(thumbImage, "png", new File(thumb_path));
} catch (IOException e) {
e.printStackTrace();
}
If your image source is a png then use like this:
Image imgSmall = imgBig.getScaledInstance(
targetWidth, targetHeight, Image.SCALE_SMOOTH);
If you want to resize jpeg or gif without loose too much quality, I made a library in 2010 for this: beautylib on github that uses internally this other library: java-image-scaling. You can see directly the source code to find something useful: https://github.com/felipelalli/beautylib/blob/master/src/br/eti/fml/beautylib/ResizeImage.java
None of the answers will help you to get real quality you desire. Include thumbailator.jar in your project (download it form here):
https://code.google.com/p/thumbnailator/
https://code.google.com/p/thumbnailator/wiki/Downloads?tm=2
Then upload the image first (as file, without Thumbnailator - it's use is to create thumbs, but you can create large images with it of course), and resize it to every dimensions you want (with Thumbnailator 800x600 for example). Quality will be very good. I was searching for this long time, this .jar helped me to achieve what i want.
Yes, I had the same problems and solved them, please read my question (answer is embedded in the question). I tried imgscalr and java-image-scaling libraries and found the second much better quality. Get close to the monitor to appreciate the difference between the thumbnail examples.
Despite my initial thoughts, resizing an image seems a very complicate thing, you don't want to do it yourself. For example I tell java-image-scaling to use ResampleFilters.getLanczos3Filter() to have better result.
It also addresses how to save a JPG with a quality higher than the standard 75, which produces a bad result especially for a thumbnail.
I also wrote a small class, called MyImage to help with common tasks, such as reading an image from a byte array, from a file, scaling by specifying only width or only height, scaling by specifying a bounding box, scaling by specifying width and height and adding a white band to make the image not distorted and writing to JPG file.
Answer: Remove the hints VALUE_INTERPOLATION_BILINEAR, and VALUE_RENDERING_QUALITY.
Example:
public static BufferedImage resizeImage(BufferedImage image, int width, int height) {
// Temporary image
BufferedImage tmp = image;
// Result image
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Graphics object
Graphics2D graphics = (Graphics2D)result.createGraphics();
// Add rendering hints
graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
graphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
// Draw tmp
graphics.drawImage(tmp, 0, 0, width, height, null);
// Dispose of graphics object
graphics.dispose();
// Return image result
return result;
}
Note: For some reason, the hints VALUE_INTERPOLATION_BILINEAR and VALUE_RENDERING_QUALITY blur the image being resized.
All the methods posted does'nt work for me i have to reduce QrCode size, but with above methods the quality is poor and scanner doesn't work , if i take original picture and resize it in paint the scanner is working.
The do while loop used in The Perils of Image.getScaledInstance() will run into an infinite loop, given those values,
w = 606; h = 505, targetWidth = 677, targetHeight = 505
Here is a simplied testing code, you can try it.
public class LoopTest {
public static void main(String[] args) {
new LoopTest(true, 606, 505, 677, 505);
}
public LoopTest(boolean higherQuality, int w, int h, int targetWidth, int targetHeight) {
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
} while (w != targetWidth || h != targetHeight); // TODO Auto-generated constructor stub
}
}
A quick work around: define an index for loop count. If the index is >=10, break out of loop.

Categories

Resources