I am using the following codes to resize an image, I can easily get same aspect ratio as the original image by using the getScaledInstance function by setting one parameter to negative, so that it automatically maintains the other paramter to get the aspect ratio. But the problem that I am facing is with the BufferedImage, I cannot set it to the desired aspect ratio because I don't know beforehand what values of height it will generate(width I have set to 500).I have also included the image that I can getting as a result of running the following code. BufferedImage I have set to 800x600 because I have no other option.My previous question was resizing an image without compromising the aspect ratio
public class ResizeImage {
public static void main(String[] args) throws IOException {
BufferedImage img1 = new BufferedImage(800, 600,
BufferedImage.TYPE_INT_RGB);
img1.createGraphics()
.drawImage(
ImageIO.read(
new File(
"C:/Users/Public/Pictures/Sample Pictures/Desert.jpg"))
.getScaledInstance(500, -1, Image.SCALE_SMOOTH),
0, 0, null);
ImageIO.write(img1, "jpg", new File(
"C:/Users/Public/Pictures/Sample Pictures/test/Desert.jpg"));
}
}
You can do the math to get the original image aspect ratio
double ratio = img1.getWidth() / img1.getHeigth();
and invoke the getScaledInstance with this ratio
blabla.getScaledInstance(500, (int)500 / ratio, Image.SCALE_SMOOTH);
Related
I create an image that is an extract of a PDF and i make an OCR with tesseract on it. Everything works good until a decide to change the dpi of my image. I was excpecting to have an error by doing this and i tried to rescale my image in order to make my OCR work well again.
I have no idea about how I can rescale my image. I know there is some methods with the BufferedImage class but i can't find a way to dynamicly rescale it.
I don't know if I'm clear but imagine a 300 dpi image. If I want to change it to 600 I have to rescale my image to make my OCR work again, my question here is how can I rescale it dynamicly ? Is there a sort of a ratio between the original dpi and the new one that i can use to get a new width and height? Or something else?
To help you understand me here is my code:
public double ratioDPI() {
int ratio = 0;
int minimal_dpi = 300;
int dpi = ERXProperties.intForKey("dpi.image");
return ratio = (dpi/minimal_dpi);
}
public BufferedImage rescale(BufferedImage img) {
int width_img = img.getWidth();
int height_img = img.getHeight();
double factor_width = ERXProperties.doubleForKey("factor.size.width.image.republique.francaise");
double factor_height = ERXProperties.doubleForKey("factor.size.height.image.republique.francaise");
return (BufferedImage) img.getScaledInstance((int)(width_img*ratio), (int)(height_img*ratio), BufferedImage.SCALE_SMOOTH);
}
If you change the DPI of an image, you change the size when outputting it to a printer, for example. If you increase the DPI from 300 to 600, the image in the output only takes up half the width and half the height. If you resize the picture now it only takes up more memory, the quality of the picture would not be better.
For scaling it is best to use AffineTransform, so you can filter the image bilinear so that the pixelation is not so noticeable:
A scaling function:
public static BufferedImage scale(BufferedImage source, double scale, boolean bilinearFiltering){
try{
BufferedImage destination = new BufferedImage((int)(source.getWidth() * scale), (int)(source.getHeight() * scale), source.getType());
AffineTransform at = new AffineTransform();
at.scale(scale, scale);
AffineTransformOp scaleOp = new AffineTransformOp(at, getInterpolationType(bilinearFiltering));
return scaleOp.filter(source, destination);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static int getInterpolationType(boolean bilinearFiltering){
return bilinearFiltering ? AffineTransformOp.TYPE_BILINEAR : AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
}
Maybe that's a solution for you.
Original
https://drive.google.com/file/d/1B3xxfWkGsMs2_MQ_bUQ8_ALYI0DL-LIo/view?usp=sharing
When saved to file
https://drive.google.com/file/d/1z5euXupeHmiFebch4A39fVqGukoUiK0p/view?usp=sharing
When printed to canvas
https://drive.google.com/file/d/1VouD-ygf0pPXFFx9Knr4pv44FHMtoqcV/view?usp=sharing
BufferedImage temp = bImg.getSubimage(100, 100, (int)imgWidth - 100, (int)imgHeight - 100);
try{
ImageIO.write(temp, "png", new File("test.png"));
}catch(Exception e){
e.printStackTrace();
}
gc.drawImage(SwingFXUtils.toFXImage(temp, null), 100, 100);
For some reason if I print an image to the canvas, it is different than if I save the same image to a file. When I save it to a file it correctly calculates the subImage but when I print it to the canvas it disregards the x and y coords I give it and takes a subImage using (0,0) as (x,y) with the given width and height.
From the documentation of the getSubimage method:
Returns a subimage defined by a specified rectangular region. The returned BufferedImage shares the same data array as the original image.
The sub-image is just a “window” into the original image; they are using the same pixel data.
The SwingFXUtils.toFXImage documentation states:
Snapshots the specified BufferedImage and stores a copy of its pixels into a JavaFX Image object, creating a new object if needed.
While it would certainly make sense to only copy the pixels in the source image’s dimensions, the above words don’t make it completely clear that it won’t copy the entire pixel data buffer, thus ignoring the boundaries of a sub-image. I would consider this a bug, but I can see where there might be an argument that it’s not.
In the meantime, you can work around this by extracting a sub-image yourself:
BufferedImage cropped = new BufferedImage(
(int) imgWidth - 100,
(int) imgHeight - 100,
bImg.getType());
Graphics g = cropped.getGraphics();
g.drawImage(bImg, -100, -100, null);
g.dispose();
gc.drawImage(SwingFXUtils.toFXImage(cropped, null), 100, 100);
I'm looking for a way to create a square thumbnail (250px × 250px) in Java without destroying the aspect ratio, that means if the image is rectangular with one side longer than the other it should just cut off whatever doesn't fit in the square. Currently I'm doing this:
public static void createThumbnail(File file, String extension)
throws IOException {
BufferedImage img = new BufferedImage(
250, 250, BufferedImage.TYPE_INT_RGB);
img.createGraphics().drawImage(
ImageIO.read(file).getScaledInstance(
250, 250, Image.SCALE_SMOOTH), 0, 0, null);
ImageIO.write(img, extension, new File(
"./public/images/thumbs/" + file.getName()));
}
However, it is not cutting of parts of the image, instead it is squeezing it to fit inside the 250 × 250 square.
You are using getScaledInstance() which will just expand or shrink your image to fit it in the size you are giving it.
Have a look at getSubimage(). You most probably want to first get a sub image which has the same aspect ratio of your target size (a square), then apply getScaledInstance() on it. This way you just shrink with the same aspect ratio and don't get any squeezing effect.
So something like this should work. Assuming you want to keep the middle part when cropping.
Image getThumbnail(File file) {
BufferedImage original = ImageIO.read(file);
//assuming we want a square thumbnail here
int side = Math.min(original.getWidth(), original.getHeight());
int x = (original.getWidth() - side) / 2;
int y = (original.getHeight() - side) / 2;
BufferedImage cropped = original.getSubimage(x, y, side, side);
return cropped.getScaledInstance(250, 250, Image.SCALE_SMOOTH);
}
(I haven't tried it myself, let me know if there are any problems with it.)
You can then pass it to your drawImage() creating the new rendered BufferedImage, and save it to a file.
BufferedImage img = new BufferedImage(250, 250, BufferedImage.TYPE_INT_RGB);
img.createGraphics().drawImage(getThumbnail(file), 0, 0, null);
ImageIO.write(img, extension, new File("./public/images/thumbs/" + file.getName()));
How could I resize an image and still keep it's aspect ratio?
This is the method that I use :
private static BufferedImage resizeImage(BufferedImage originalImage,
int type) {
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();
return resizedImage;
}
The type variable :
BufferedImage original = ImageIO.read(new File(imagePath));
int type = original.getType() == 0 ? BufferedImage.TYPE_INT_ARGB
: original.getType();
The problem is that some images are correctly resized but others lose their aspect ratio because of the IMG_WIDTH and IMG_HEIGHT.
Is there a way to get the original image dimensions and then apply some kind of proportion resize to maintain the aspect ratio of the resized image?
Why don't you use originalImage.getWidth() and originalImage.getHeight()? Then you can easily calculate aspect ratio. Don't forget that int/int = int, so you need to do
double ratio = 1.0 * originalImage.getWidth() / originalImage.getHeight();
or
double ratio = (double) originalImage.getWidth() / originalImage.getHeight();
Regarding the additional math, you can calculate
int height = (int) IMG_WIDTH/ratio;
int width = (int) IMG_HEIGHT*ratio;
Then see which one fits your needs better and resize to (IMG_WIDTH, height) or (width, IMG_HEIGHT)
To get the image size, see getWidth()/getHeight(). The rest is just some relatively simple math.
Presuming the IMG_WIDTH & IMG_HEIGHT represent the largest size desired:
Find which is going to hit the limit first.
Calculate the ratio between the natural size and that maximum size.
Multiply the other image dimension by the same ratio.
I am aware of BufferedImage.getSubimage However, it cant deal with cropping images that are smaller than the cropping size throwing the exception:
java.awt.image.RasterFormatException: (y + height) is outside raster
I want to be able to crop either a PNG/JPG/GIF to a certain size however if the image is smaller than the cropping area centre itself on a white background. Is there a call to do this? Or do I need to create an image manually to centre the image on if so, how would I go about this?
Thanks
You cannot crop an image larger, only smaller. So, you start with the goal dimension,let's say 100x100. And your BufferedImage (bi), let's say 150x50.
Create a rectangle of your goal:
Rectangle goal = new Rectangle(100, 100);
Then intersect it with the dimensions of your image:
Rectangle clip = goal.intersection(new Rectangle(bi.getWidth(), bi.getHeight());
Now, clip corresponds to the portion of bi that will fit within your goal. In this case 100 x50.
Now get the subImage using the value of clip.
BufferedImage clippedImg = bi.subImage(clip,1, clip.y, clip.width, clip.height);
Create a new BufferedImage (bi2), the size of goal:
BufferedImage bi2 = new BufferedImage(goal.width, goal.height);
Fill it with white (or whatever bg color you choose):
Graphics2D big2 = bi2.getGraphics();
big2.setColor(Color.white);
big2.fillRect(0, 0, goal.width, goal.height);
and draw the clipped image onto it.
int x = goal.width - (clip.width / 2);
int y = goal.height - (clip.height / 2);
big2.drawImage(x, y, clippedImg, null);