I am trying to rotate a buffered image in Java (a plane icon on the map) around its centre using help from here:
Rotating BufferedImage instances
When I use this code:
AffineTransform at = new AffineTransform();
at.rotate(Math.toRadians(planeHeading),origImage.getWidth() / 2, origImage.getHeight() / 2);
AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
origImage = op.filter(origImage, null);
g.drawImage(origImage, x-origImage.getWidth() / 2, y-origImage.getHeight() / 2, null);
on rotation of 180-270 degree, the image is placed higher and a bit to the left of its centre:
If I use this code:
AffineTransform at = new AffineTransform();
at.translate(x, y);
at.rotate(Math.toRadians(planeHeading));
at.translate(-origImage.getWidth()/2, -origImage.getHeight()/2);
g.drawImage(origImage, at, null);
the image is rotated correctly, however the image itself gets very pixelated on its edges.
Can someone please help find the culprit?
This is the whole method:
#Override
public void paintWaypoint(Graphics2D g, JXMapViewer viewer, MapPlane w)
{
g = (Graphics2D)g.create();
try
{
origImage = ImageIO.read(getClass().getResource("/images/map/mapPLANE.png"));
Point2D point = viewer.getTileFactory().geoToPixel(w.getPosition(), viewer.getZoom());
// Center coordinates
int x = (int)point.getX();
int y = (int)point.getY();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Get heading of the plane and rotate the image
String planeHeadingStr = w.getHeading();
try
{
double planeHeading = Double.parseDouble(planeHeadingStr);
AffineTransform at = new AffineTransform();
//Do the actual rotation
at.rotate(Math.toRadians(planeHeading),origImage.getWidth() / 2, origImage.getHeight() / 2);
AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
origImage = op.filter(origImage, null);
// Draw the image
g.drawImage(origImage, x-origImage.getWidth() / 2, y-origImage.getHeight() / 2, null);
}
catch(NumberFormatException e)
{
}
g.dispose();
}
catch (Exception ex)
{
log.warn("couldn't read mapPLANE.png", ex);
}
}
Thanks a lot!
To achieve the same bilinear interpolation that you got for your AffineTransformOp in the second case where you draw directly using an AffineTransform, you should set another RenderingHint:
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
Otherwise, in your case, it defaulted to NEAREST_NEIGHBOUR interpolation.
Java 8 here. Trying to stick with the BufferedImage API and not delve into JavaFx land.
I have a JPG image that is 768 pixel wide and 432 pixels in height. I want to crop a centered 400x400 pixel square out of the center of it using BufferedImage#getSubimage(...).
I have the following code:
BufferedImage image = ImageIO.read(imageTempFile);
int dim = 400;
int xCropBuffer, yCropBuffer;
xCropBuffer = (image.getWidth() - dim) / 2;
yCropBuffer = (image.getHeight() - dim) / 2;
log.info("width = " + image.getWidth() + ", height = " + image.getHeight() + ", dim = " + dim + ", xCropBuffer = " + xCropBuffer + ", yCropBuffer = " + yCropBuffer);
image = image.getSubimage(xCropBuffer, yCropBuffer + dim, dim, dim);
At runtime this throws the following exception:
java.awt.image.RasterFormatException: (y + height) is outside of Raster
at sun.awt.image.ByteInterleavedRaster.createWritableChild(ByteInterleavedRaster.java:1248)
at java.awt.image.BufferedImage.getSubimage(BufferedImage.java:1202)
at java_awt_image_BufferedImage$getSubimage.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:149)
at com.myapp.DefaultMediaService.uploadImage(DefaultMediaService.java:56)
at com.myapp.MediaService$uploadImage.call(Unknown Source)
And the logs print the following message right before the exception is thrown:
width = 768, height = 432, dim = 400, xCropBuffer = 184, yCropBuffer = 16
So I'm passing in image.getSubimage(184, 416, 400, 400) arguments...shouldn't this be OK?! The image is 768x432, so (184, 416) should be a valid coordinate for its upper-left corner, and 184 + 400 < 768 and 416 - 400 > 0. So all these parameters should map to a valid 400x400 rectangle inside the image, right?
Drawing an image is not like drawing a text, where the ascend runs from baseline to smaller y coordinates. The image is specified by its upper left corner and extending towards the greater coordinates for both, x and y. Hence, you should not specify + dim for the y coordinate.
BufferedImage oldImage = ImageIO.read(imageFile),
newImage = oldImage.getSubimage(
(oldImage.getWidth()-dim)/2, (oldImage.getHeight()-dim)/2, dim, dim);
ImageIO.write(newImage, "png", imageFile);
For completeness, since there seem to have been some confusion in your previous question, here some alternative for achieves the same image transformation, which can be used as template for other image operations:
via BufferedImageOp
BufferedImage oldImage = ImageIO.read(imageFile),
newImage = new BufferedImage(dim, dim, oldImage.getType());
BufferedImageOp op = new AffineTransformOp(
AffineTransform.getTranslateInstance(
-(oldImage.getWidth()-dim)/2, -(oldImage.getHeight()-dim)/2),
AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
op.filter(oldImage, newImage);
ImageIO.write(newImage, "png", imageFile);
via Graphics
BufferedImage oldImage = ImageIO.read(imageFile),
newImage = new BufferedImage(dim, dim, oldImage.getType());
Graphics2D gfx = newImage.createGraphics();
gfx.drawImage(oldImage,
-(oldImage.getWidth()-dim)/2, -(oldImage.getHeight()-dim)/2, null);
gfx.dispose();
ImageIO.write(newImage, "png", imageFile);
In either case, the cropping to a desired size is implied by making the target image smaller than the original one.
Then, the source image only has to get translated by (-x, -y) to select the desired detail rectangle.
This seems pretty simple. The height is 432, and you are specifying the upper-left corner as (182, 416). Next, you ask it to get 400 pixels down. So you get 432 < 416 + 400 which means that the bottom of the frame you want to select is greater than the height of the image.
I am trying to rotate and image left and right by 90 degrees.
For some reason though, the output of this process results in corruption.
Here is my code:
(its groovy but it doesnt take much imagination to pretend its java)
void rotate(File file){
def image = ImageIO.read(file);
double theta = Math.PI / 2;
def w = image.width / 2;
def h = image.height / 2;
def transform = new AffineTransform();
transform.rotate(theta, h, w);
def op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
image = op.filter(image, null);
def name = file.getName();
def type = name.substring(name.lastIndexOf(".") + 1, name.length());
ImageIO.write(image,type,file);
}
original:
rotated:
If by corruption you are referring to the color change, take out the filter. That's giving you a negative image if I'm understanding the syntax properly.
Whenever I use transforms I leave filters off and do them by hand. It does take a lot of time, but they always turn out being more useful. Just a suggestion.
The filter() method requires a src and dst BufferedImage, which must be different.
Image image = null;
try {
image = ImageIO.read(new File("gZtC3.jpg"));
} catch (IOException ex) {
ex.printStackTrace(System.err);
}
double theta = Math.PI / 2;
int w = image.getWidth(null);
int h = image.getHeight(null);
AffineTransform at = AffineTransform.getRotateInstance(theta, w / 2, h / 2);
BufferedImage src = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = src.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC);
BufferedImage dst = op.filter(src, null);
this.add(new JLabel(new ImageIcon(dst), JLabel.CENTER));
Can someone please help with some code for creating a thumbnail for a JPEG in Java.
I'm new at this, so a step by step explanation would be appreciated.
Image img = ImageIO.read(new File("test.jpg")).getScaledInstance(100, 100, BufferedImage.SCALE_SMOOTH);
This will create a 100x100 pixels thumbnail as an Image object. If you want to write it back to disk simply convert the code to this:
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
img.createGraphics().drawImage(ImageIO.read(new File("test.jpg")).getScaledInstance(100, 100, Image.SCALE_SMOOTH),0,0,null);
ImageIO.write(img, "jpg", new File("test_thumb.jpg"));
Also if you are concerned about speed issues (the method described above is rather slow if you want to scale many images) use these methods and the following declaration :
private BufferedImage scale(BufferedImage source,double ratio) {
int w = (int) (source.getWidth() * ratio);
int h = (int) (source.getHeight() * ratio);
BufferedImage bi = getCompatibleImage(w, h);
Graphics2D g2d = bi.createGraphics();
double xScale = (double) w / source.getWidth();
double yScale = (double) h / source.getHeight();
AffineTransform at = AffineTransform.getScaleInstance(xScale,yScale);
g2d.drawRenderedImage(source, at);
g2d.dispose();
return bi;
}
private BufferedImage getCompatibleImage(int w, int h) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
BufferedImage image = gc.createCompatibleImage(w, h);
return image;
}
And then call :
BufferedImage scaled = scale(img,0.5);
where 0.5 is the scale ratio and img is a BufferedImage containing the normal-sized image.
As you might have found out "easy" and "good looking result" are two very different things. I have encapsulated both of these requirements into a very simple java image scaling library (Apache 2 license) that just does everything right for you.
Example code to create a thumbnail looks like this:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 150);
Your image proportions are honored, the library makes a best-guess at the method it should use based on the amount of change in the image due to scaling (FASTEST, BALANCED or QUALITY) and the best supported Java2D image types are always used to do the scaling to avoid the issue of "black" results or really terrible looking output (e.g. overly dithered GIF images).
Also, if you want to force it to output the best looking thumbnail possible in Java, the API call would look like this:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY,
150, 100, Scalr.OP_ANTIALIAS);
Not only will the library use the Java2D recommended incremental scaling for you to give you the best looking result, it will also apply an optional antialiasing effect to the thumbnail (ConvolveOp with a very fine-tuned kernel) to every-so-slightly soften the transitions between pixel values so make the thumbnail look more uniform and not sharp or poppy as you might have seen when you go from very large images down to very small ones.
You can read through all the comments in the library (the code itself is doc'ed heavily) to see all the different JDK bugs that are worked around or optimizations that are made to improve the performance or memory usage. I spent a LOT of time tuning this implementation and have had a lot of good feedback from folks deploying it in web apps and other Java projects.
This is simple way of creating a 100 X 100 thumbnail without any stretch or skew in image.
private void saveScaledImage(String filePath,String outputFile){
try {
BufferedImage sourceImage = ImageIO.read(new File(filePath));
int width = sourceImage.getWidth();
int height = sourceImage.getHeight();
if(width>height){
float extraSize= height-100;
float percentHight = (extraSize/height)*100;
float percentWidth = width - ((width/100)*percentHight);
BufferedImage img = new BufferedImage((int)percentWidth, 100, BufferedImage.TYPE_INT_RGB);
Image scaledImage = sourceImage.getScaledInstance((int)percentWidth, 100, Image.SCALE_SMOOTH);
img.createGraphics().drawImage(scaledImage, 0, 0, null);
BufferedImage img2 = new BufferedImage(100, 100 ,BufferedImage.TYPE_INT_RGB);
img2 = img.getSubimage((int)((percentWidth-100)/2), 0, 100, 100);
ImageIO.write(img2, "jpg", new File(outputFile));
}else{
float extraSize= width-100;
float percentWidth = (extraSize/width)*100;
float percentHight = height - ((height/100)*percentWidth);
BufferedImage img = new BufferedImage(100, (int)percentHight, BufferedImage.TYPE_INT_RGB);
Image scaledImage = sourceImage.getScaledInstance(100,(int)percentHight, Image.SCALE_SMOOTH);
img.createGraphics().drawImage(scaledImage, 0, 0, null);
BufferedImage img2 = new BufferedImage(100, 100 ,BufferedImage.TYPE_INT_RGB);
img2 = img.getSubimage(0, (int)((percentHight-100)/2), 100, 100);
ImageIO.write(img2, "jpg", new File(outputFile));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The JMagick library (and implementation of ImageMagick in Java) will have what you need.
the Java code above (with the scale / getCompatibleImage methods) worked great for me, but when I deployed to a server, it stopped working, because the server had no display associated with it -- anyone else with this problem can fix it by using:
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
instead of
BufferedImage bi = getCompatibleImage(w, h);
and deleting the getCompatibleImage method
(later note -- it turns out this works great for most images, but I got a bunch from my companys marketing department that are 32 bit color depth jpeg images, and the library throws an unsupported image format exception for all of those :( -- imagemagick / jmagick are starting to look more appealing)
I have writtena util class with static methods years ago using JAI. Java Advanced Imaging API is the most reliable API in Java to deal with images. It's vector interpolation is closest thing to Photoshop in Java world. Here is one of them:
public static ByteArrayOutputStream resize(InputStream inputStream , int IMG_WIDTH,
int IMG_HEIGHT) throws Exception {
BufferedImage originalImage = ImageIO.read(inputStream);
int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB
: originalImage.getType();
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);
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(resizedImage, "png", bos);
return bos;
}
I know this is a pretty old post. I have been looking for a solution to generate the thumbnail so end up using this
Thumbnails.of(originalImage).scale(0.25).asBufferedImage();
if you are using for mobile would suggest to set the scale to 0.45
Thumbnails.of(originalImage).scale(0.45).asBufferedImage();
https://github.com/coobird/thumbnailator
This is certainly much faster using the Graphics2D as have tested the both options.
I've used Thumbnailator! It solved my problem with two lines of code.
https://github.com/coobird/thumbnailator
Simple way to create a thumbnail without stretching or a library. Works with transparency in pngs, too.
public File createThumbnail(String imageUrl, String targetPath) {
final int imageSize = 100;
File thumbnail = new File(targetPath);
try {
thumbnail.getParentFile().mkdirs();
thumbnail.createNewFile();
BufferedImage sourceImage = ImageIO.read(new File(imageUrl));
float width = sourceImage.getWidth();
float height = sourceImage.getHeight();
BufferedImage img2;
if (width > height) {
float scaledWidth = (width / height) * (float) imageSize;
float scaledHeight = imageSize;
BufferedImage img = new BufferedImage((int) scaledWidth, (int) scaledHeight, sourceImage.getType());
Image scaledImage = sourceImage.getScaledInstance((int) scaledWidth, (int) scaledHeight, Image.SCALE_SMOOTH);
img.createGraphics().drawImage(scaledImage, 0, 0, null);
int offset = (int) ((scaledWidth - scaledHeight) / 2f);
img2 = img.getSubimage(offset, 0, imageSize, imageSize);
}
else if (width < height) {
float scaledWidth = imageSize;
float scaledHeight = (height / width) * (float) imageSize;
BufferedImage img = new BufferedImage((int) scaledWidth, (int) scaledHeight, sourceImage.getType());
Image scaledImage = sourceImage.getScaledInstance((int) scaledWidth, (int) scaledHeight, Image.SCALE_SMOOTH);
img.createGraphics().drawImage(scaledImage, 0, 0, null);
int offset = (int) ((scaledHeight - scaledWidth) / 2f);
img2 = img.getSubimage(0, offset, imageSize, imageSize);
}
else {
img2 = new BufferedImage(imageSize, imageSize, sourceImage.getType());
Image scaledImage = sourceImage.getScaledInstance(imageSize, imageSize, Image.SCALE_SMOOTH);
img2.createGraphics().drawImage(scaledImage, 0, 0, null);
}
ImageIO.write(img2, "png", thumbnail);
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return thumbnail;
}
I have created a application called fotovault (sourceforge.net) which can upload images and create thumbnails in java using imagej apis.
Please read my blog below
http://www.gingercart.com/Home/java-snippets/create-image-thumbnail-in-java-using-imagej-api
I have gone through a blog according to which you have following options -
For simple RGB files use ImageScalr . ImageIO class is used for reading files and ImageScalr to create thumbnails
For supporting RGB + CYMK, use ImageIO and JAI (Java Advanced Imaging) API for reading files and ImageScalr to create thumbnail.
In case you don’t know what file formats, color mode you are going to deal with, safest option is to use ImageMagick.
Here is link that gives a complete answer with code snippets.
There are many image processing frameworks available that you can do this with just a few lines. The example below generates the thumbnails in different resolutions (given a width as reference) using Marvin Framework. The three thumbnails were generated in 92 ms.
input:
output:
import static marvin.MarvinPluginCollection.*;
MarvinImage image = MarvinImageIO.loadImage("./res/input.jpg");
MarvinImage scaledImage = new MarvinImage(1,1);
scale(image, scaledImage, 250);
MarvinImageIO.saveImage(scaledImage, "./res/output_x250.jpg");
scale(image, scaledImage, 150);
MarvinImageIO.saveImage(scaledImage, "./res/output_x150.jpg");
scale(image, scaledImage, 50);
MarvinImageIO.saveImage(scaledImage, "./res/output_x50.jpg");
Maybe the simplest approach would be:
static public BufferedImage scaleImage(BufferedImage image, int max_width, int max_height) {
int img_width = image.getWidth();
int img_height = image.getHeight();
float horizontal_ratio = 1;
float vertical_ratio = 1;
if(img_height > max_height) {
vertical_ratio = (float)max_height / (float)img_height;
}
if(img_width > max_width) {
horizontal_ratio = (float)max_width / (float)img_width;
}
float scale_ratio = 1;
if (vertical_ratio < horizontal_ratio) {
scale_ratio = vertical_ratio;
}
else if (horizontal_ratio < vertical_ratio) {
scale_ratio = horizontal_ratio;
}
int dest_width = (int) (img_width * scale_ratio);
int dest_height = (int) (img_height * scale_ratio);
BufferedImage scaled = new BufferedImage(dest_width, dest_height, image.getType());
Graphics graphics = scaled.getGraphics();
graphics.drawImage(image, 0, 0, dest_width, dest_height, null);
graphics.dispose();
return scaled;
}
Solution for the case when you want to create a quadrate (75x75) thumbnail from the non-quadrate source.
Code below first crop original image to quadrate using smaller size than resizes the quadrate image.
public static void generateThumbnailWithCrop(String imgPath, String thumbnailPath, int size) throws IOException {
BufferedImage sourceImage = ImageIO.read(new File(imgPath));
int width = sourceImage.getWidth();
int height = sourceImage.getHeight();
int smallerSize = width > height ? height : width;
BufferedImage quadrateImage = cropToQuadrate(sourceImage, smallerSize);
int type = quadrateImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : quadrateImage.getType();
BufferedImage resizedImage = resizeImageWithHint(quadrateImage, type, size, size);
File thumb = new File(thumbnailPath);
thumb.getParentFile().mkdirs();
ImageIO.write(resizedImage, "jpg", thumb);
}
private static BufferedImage cropToQuadrate(BufferedImage sourceImage, int size) {
BufferedImage img = sourceImage.getSubimage(0, 0, size, size);
BufferedImage copyOfImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = copyOfImage.createGraphics();
g.drawImage(img, 0, 0, null);
return copyOfImage;
}
private static BufferedImage resizeImageWithHint(BufferedImage originalImage, int type, int width, int height) {
BufferedImage resizedImage = new BufferedImage(width, height, type);
Graphics2D g = resizedImage.createGraphics();
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.setComposite(AlphaComposite.Src);
g.drawImage(originalImage, 0, 0, width, height, null);
g.dispose();
return resizedImage;
}
Thumbnails4j (I'm a maintainer, but it's owned by Elastic) is a java library that can be used to create thumbnails from image files, as well as from other file types.
File input = new File("/path/to/my_file.jpeg");
Thumbnailer thumbnailer = new ImageThumbnailer("png"); // or "jpg", whichever output format you want
List<Dimensions> outputDimensions = Collections.singletonList(new Dimensions(100, 100));
BufferedImage output = thumbnailer.getThumbnails(input, outputDimensions).get(0);