Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Can you resize with BufferedImage

141 views
Skip to first unread message

Cecil Westerhof

unread,
Apr 12, 2017, 4:44:12 AM4/12/17
to
In my code I have the following code:
dest = image.getSubimage(x, y, dispWidth, dispHeight);

This is the part that I want to select, but I want to resize it at the
same time. In this case I would like to scale to 50%.
How should I do that?

--
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof

Martin Gregorie

unread,
Apr 12, 2017, 6:22:03 AM4/12/17
to
On Wed, 12 Apr 2017 10:34:04 +0200, Cecil Westerhof wrote:

> In my code I have the following code:
> dest = image.getSubimage(x, y, dispWidth, dispHeight);
>
> This is the part that I want to select, but I want to resize it at the
> same time. In this case I would like to scale to 50%.
> How should I do that?

Use an AffineTransform.

The image resizing code should look something like this fragment:

AffineTransform at = new AffineTransform();
at.scale(scale, scale);
AffineTransformOp op = new AffineTransformOp(at,
AffineTransformOp.TYPE_BICUBIC);
Double nw = new Double(w * scale);
Double nh = new Double(h * scale);
om = new BufferedImage(nw.intValue(),
nh.intValue(),
BufferedImage.TYPE_3BYTE_BGR);
op.filter(im, om);

where:
im is a BufferedImage containing the old image
om is a BufferedImage to hold the modified image
scale is a double containing the required scaling factor

I'm scaling images to a width of 400 pixels, so I calculate scale as
400.0 / image_width. You would just set scale to 0.5. I want to keep the
image proportion, so I use the same scale value for both width and height.


--
martin@ | Martin Gregorie
gregorie. | Essex, UK
org |

Cecil Westerhof

unread,
Apr 12, 2017, 8:14:10 AM4/12/17
to
Does not look to difficult. Just to be sure I understand it correctly.
I first cut the part I want and then I do the scale. So my dest
becomes im. And then I use your code.

Martin Gregorie

unread,
Apr 12, 2017, 9:10:05 AM4/12/17
to
That sounds right - first select what you want to have in the image and
then scale it to whatever size you want.

I don't remember where I would have found the example code that became
what I published, but I do remember going to look for it once I realised
that the Java tutorials, package and class-level comments was absolutely
bloody useless when it comes to explaining how to choose the classes
you'll need to carry out a task or the best order to apply them in. They
make the unstated assumption that any programmer who needs to use the
classes in java.awt.image and java.awt.image.renderable will already
understand everything they need to know about the purpose of the classes
they contain and which ones to use for any given task.

I, for one, don't have an image processing background apart from being a
self-taught GIMP user and, on the face of it, nor does Cecil, so if
anybody reading this can recommend a book or a website that covers the
basics of programming with these tools, please post the link here.

Speaking entirely for myself, something with the approach and content of
the better O'Reilly books would be perfect.

Knute Johnson

unread,
Apr 12, 2017, 11:06:08 AM4/12/17
to
On 4/12/2017 08:06, Martin Gregorie wrote:

If you find a good book, let us all know please.

I wrote a library to do a few common image mods so I don't have to
remember how to do it every time:

package com.knutejohnson.classes;

import java.awt.*;
import java.awt.color.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import java.util.*;
import javax.imageio.*;
import javax.imageio.stream.*;
import javax.imageio.plugins.jpeg.*;

/**
* ImageUtilities is an assortment of static methods to manipulate
* BufferedImages
*
* @author Knute Johnson
*/
public class ImageUtilities {
/**
* Writes a BufferedImage to a JPEG file with the specified quality
*
* @param image BufferedImage to write
* @param quality image quality, a float value between 0.0f and 1.0f
* @param file destination File
*
* @throws IOException if an error occurs writing to the file
* @throws IllegalArgumentException if quality is not in the range
* 0.0f to 1.0f
*/
public static void writeJPEG(RenderedImage image, float quality,
File file)
throws IOException {
if (quality < 0.0f || quality > 1.0f)
throw new IllegalArgumentException("0.0 < Quality < 1.0");
ImageWriter writer = null;
Iterator iter = ImageIO.getImageWritersByFormatName("JPEG");
if (!iter.hasNext())
throw new IOException("No Writers Available");
writer = (ImageWriter)iter.next();
if (file.exists())
file.delete();
ImageOutputStream ios = ImageIO.createImageOutputStream(file);
writer.setOutput(ios);
JPEGImageWriteParam iwp = new JPEGImageWriteParam(null);
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(quality);
writer.write(null,new IIOImage(image,null,null),iwp);
ios.flush();
writer.dispose();
ios.close();
}

/**
* Converts a colored image to gray scale
*
* @param image source BufferedImage
* @param hints any rendering hints for the conversion or
{@code null}
*
* @return gray scale BufferedImage
*/
public static BufferedImage convertToGray(BufferedImage image,
RenderingHints hints) {
BufferedImage gray =
createCompatible(image.getWidth(),image.getHeight());

ColorConvertOp op = new ColorConvertOp(
image.getColorModel().getColorSpace(),
ColorSpace.getInstance(ColorSpace.CS_GRAY),hints);

return op.filter(image,gray);
}

/**
* Converts a color image to sepia tone
*
* @param image source image
* @param hints any rendering hints for the conversion or
{@code null}
*
* @return sepia tone image
*/
public static BufferedImage convertToSepia(BufferedImage image,
RenderingHints hints) {
BufferedImage sepia =
createCompatible(image.getWidth(),image.getHeight());

// convert to grayscale and back to compatible RGB
ColorSpace imgCS = image.getColorModel().getColorSpace();
ColorSpace grayCS = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorSpace sepiaCS = sepia.getColorModel().getColorSpace();
ColorConvertOp cop = new ColorConvertOp(imgCS,grayCS,hints);
image = cop.filter(image,null);
cop = new ColorConvertOp(grayCS,sepiaCS,hints);
sepia = cop.filter(image,null);

// slightly enhance the red, slightly reduce the green and
// remove half the blue
float[] factor = new float[] { 1.35f,1f,.7f };
float[] offset = new float[] { 0f,0f,0f };
RescaleOp rop = new RescaleOp(factor,offset,hints);

return rop.filter(sepia,null);
}

/**
* Scales a BufferedImage
*
* @param src source BufferedImage
* @param sx x scale factor
* @param sy y scale factor
* @param interpolationType the interpolation type
*
* @return scaled BufferedImage
*
* @see AffineTransformOp
*/
public static BufferedImage scaleImage(BufferedImage src, double sx,
double sy, int interpolationType) {
AffineTransformOp op = new AffineTransformOp(
AffineTransform.getScaleInstance(sx,sy),interpolationType);

return op.filter(src,null);
}

/**
* Scales a BufferedImage
*
* @param src source BufferedImage
* @param sx x scale factor
* @param sy y scale factor
* @param hints any rendering hints or {@code null}
*
* @return scaled BufferedImage
*
* @see RenderingHints
*/
public static BufferedImage scaleImage(BufferedImage src, double sx,
double sy, RenderingHints hints) {
AffineTransformOp op = new AffineTransformOp(
AffineTransform.getScaleInstance(sx,sy),hints);

return op.filter(src,null);
}

/**
* Creates a compatible BufferedImage with the specified dimensions
*
* @param width width of the image
* @param height height of the image
*
* @return compatible BufferedImage
*/
public static BufferedImage createCompatible(int width, int height) {
GraphicsConfiguration gc = getDefaultConfiguration();

return gc.createCompatibleImage(width,height);
}

/**
* Creates a compatible BufferedImage with the specified dimensions and
* transparency
*
* @param width image width
* @param height image height
* @param transparency transparency of the image
*
* @return compatible BufferedImage
*
* @see BufferedImage
* @see Transparency
*/
public static BufferedImage createCompatible(int width, int height,
int transparency) {
GraphicsConfiguration gc = getDefaultConfiguration();

return gc.createCompatibleImage(width,height,transparency);
}

/**
* Converts an existing image to a compatible ColorModel
*
* @param image source BufferedImage
* @param hints any RenderingHints or {@code null}
*
* @return compatible BufferedImage
*
* @see ColorModel
* @see RenderingHints
*/
public static BufferedImage convertToCompatible(BufferedImage image,
RenderingHints hints) {
if (isCompatible(image))
return image;

ColorConvertOp op = new ColorConvertOp(
image.getColorModel().getColorSpace(),
getDefaultConfiguration().getColorModel().getColorSpace(),hints);

return op.filter(image,null);
}

/**
* Converts an existing BufferedImage to a compatible ColorModel
*
* @param image source BufferedImage
*
* @return compatible BufferedImage
*/
public static BufferedImage convertToCompatible(BufferedImage image) {
return convertToCompatible(image,null);
}

/**
* Checks if a BufferedImage is compatible
*
* @param src source BufferedImage
*
* @return {@code true} if image is compatible
*/
public static boolean isCompatible(BufferedImage src) {
GraphicsConfiguration gc = getDefaultConfiguration();

return src.getColorModel().equals(gc.getColorModel());
}

/**
* Gets the default graphics configuration. Used by several methods in
* this class.
*
* @return default graphics configuration
*/
public static GraphicsConfiguration getDefaultConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().getDefaultConfiguration();
}
}



--

Knute Johnson

Eric Douglas

unread,
Apr 12, 2017, 11:20:53 AM4/12/17
to
I've never used an AffineTransformOp.
To change the size of an image I just generate a new image of the width/height/transparency I want. Then I get the Graphics of the new image, cast it to a Graphics2D, create a new AffineTransform, and set the translate/scale/rotate I want. Then I call .setComposite(AlphaComposite.SrcOver) on the new image's Graphics2D, then just .drawImage() passing in the old image, the AffineTransform I just created, and the observer (normally null).

Normally you want to maintain proportions, your x and y for the scale are both Math.min(newWidth / originalImageWidth, newHeight / originalImageHeight).

Martin Gregorie

unread,
Apr 12, 2017, 12:16:42 PM4/12/17
to
On Wed, 12 Apr 2017 10:05:54 -0500, Knute Johnson wrote:

> On 4/12/2017 08:06, Martin Gregorie wrote:
>
> If you find a good book, let us all know please.
>
> I wrote a library to do a few common image mods so I don't have to
> remember how to do it every time:
>
> package com.knutejohnson.classes;

Useful stuff. Thanks for posting it.

Knute Johnson

unread,
Apr 13, 2017, 12:10:22 PM4/13/17
to
On 4/12/2017 10:20, Eric Douglas wrote:
>
> I've never used an AffineTransformOp. To change the size of an image
> I just generate a new image of the width/height/transparency I want.
> Then I get the Graphics of the new image, cast it to a Graphics2D,
> create a new AffineTransform, and set the translate/scale/rotate I
> want. Then I call .setComposite(AlphaComposite.SrcOver) on the new
> image's Graphics2D, then just .drawImage() passing in the old image,
> the AffineTransform I just created, and the observer (normally
> null).
>
> Normally you want to maintain proportions, your x and y for the scale
> are both Math.min(newWidth / originalImageWidth, newHeight /
> originalImageHeight).
>

I've done it that way too. I don't really know which is faster.

--

Knute Johnson
0 new messages