I simply want to enable the user of my web site to change the orientation of a submitted photo from horizontal to vertical. Here's my code:
public static final void rotatePhoto(String jpgFilename){
BufferedImage originalImage = null, newImage=null;
try{
File file = new File(jpgFilename);
originalImage = ImageIO.read(file);
System.out.println("Photo.rotatePhoto(" +jpgFilename +") originalImage.getWidth(null)=" +originalImage.getWidth(null) +" originalImage.getHeight(null)=" +originalImage.getHeight(null) );
java.awt.image.AffineTransformOp opRotated = new java.awt.image.AffineTransformOp( java.awt.geom.AffineTransform.getQuadrantRotateInstance(1), null );
newImage = opRotated.createCompatibleDestImage(originalImage, originalImage.getColorModel());
opRotated.filter(originalImage, newImage);
}catch (IOException e){
}
/// Write result to file::::::::::::::::::::::::::::::::::::::::::::::::::::
try{
File outputfile = new File(testFilename);
ImageIO.write(newImage, "jpg", outputfile);
}catch(IOException ioE){
}
}
Problem is I get this error even though the System.out.println shows the width and height to be 640x480
java.awt.image.RasterFormatException: Transformed width (0) is less than or equal to 0.
java.awt.image.AffineTransformOp.createCompatibleDestImage(AffineTransformOp.java:447)
base.Photo.rotatePhoto(Photo.java:135)
base.ProcessContent.handleInput(ProcessContent.java:245)
servlets.ProcessServlet.doPost(ProcessServlet.java:74)
servlets.ProcessServlet.doGet(ProcessServlet.java:33)
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
Any ideas or workarounds?
Try creating a new AffineTransform from scratch and using it in your AffineTransformOp constructor:
AffineTransform tx = new AffineTransform();
tx.rotate(Math.PI / 2, originalImage.getWidth() / 2, originalImage.getHeight() / 2);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
newImage = op.filter(originalImage, newImage);
You also have to make sure newImage contains the data returned by the filter() method.
Oddly enough, this will only work when you set the formatName in ImageIO.write() to "png". I tried using jpg and the result was a black picture.
By using: AffineTransform.getQuadrantRotateInstance(1);
Your AffineTransform is rotating by a positive number of quadrants by axis. That will mess up the Transform Operation since it depends on the x and y, whenever it creates the compatible image.
int w = r.x + r.width;
int h = r.y + r.height;
if (w <= 0) {
throw new RasterFormatException("Transformed width ("+w+
") is less than or equal to 0.");
}
I would recommend doing it yourself:
public final void rotatePhoto(String jpgFilename) throws IOException {
File file = new File(jpgFilename);
BufferedImage originalImage = ImageIO.read(file);
// You could use Math.PI / 2, depends on your input.
AffineTransform affineTransform = new AffineTransform();
affineTransform.rotate(Math.toRadians(90), originalImage.getWidth() / 2, originalImage.getHeight() / 2);
// Now lets make that transform an operation, and we use it.
AffineTransformOp opRotated = new AffineTransformOp(affineTransform, AffineTransformOp.TYPE_BILINEAR);
BufferedImage newImage = opRotated.filter(originalImage, null);
// Save the image.
File outputfile = new File("rotated.jpg");
ImageIO.write(newImage, "jpg", outputfile);
}
UPDATE: Btw, it has been answered before on How do I write a servlet which rotates images?
Related
I am working on an web application to embed signature to pdf document. I am using following library Zetakey Sign & Send.From the signature pad signature is captured using:
var dataURL = canvas.toDataURL("image/png",1);
And in the server side(first Base64 decoding of signature string):
public String createSignature(String mySignature,int width,int height) throws IOException {
String filePath = SIGNATURE_PATH +"signature_" + new Date().getTime() + ".png";
byte[] imageByteArray = decodeImage(mySignature);
try(FileOutputStream imageOutFile = new FileOutputStream(filePath)){
imageOutFile.write(imageByteArray);
}
try {
BufferedImage image = ImageIO.read(new File(filePath));
image = scaleImage(image, width, height);
ImageIO.write(image, "png", new File(filePath));
for(Iterator<ImageWriter> iw = ImageIO.getImageWritersByFormatName("png"); iw.hasNext();) {
ImageWriter writer = iw.next();
ImageWriteParam writeParam = writer.getDefaultWriteParam();
ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
if(metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
continue;
}
setDPI(metadata,500,width,height);
try (ImageOutputStream stream = ImageIO.createImageOutputStream(new FileOutputStream(filePath))){
writer.setOutput(stream);
writer.write(metadata, new IIOImage(image, null, metadata), writeParam);
}
break;
}
} catch (IOException e) {
logger.error(e.getMessage());
}
return filePath;
}
private static void setDPI(IIOMetadata metadata, int value, int width, int height) throws IIOInvalidTreeException {
double dotsPerMilli = value/(INCH_TO_CM*10);
IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
horiz.setAttribute(VAL, Double.toString(dotsPerMilli));
IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
vert.setAttribute(VAL, Double.toString(dotsPerMilli));
IIOMetadataNode horizScreenSize = new IIOMetadataNode("HorizontalScreenSize");
horizScreenSize.setAttribute(VAL, Integer.toString(width));
IIOMetadataNode vertScreenSize = new IIOMetadataNode("VerticalScreenSize");
vertScreenSize.setAttribute(VAL, Integer.toString(height));
IIOMetadataNode dim = new IIOMetadataNode("Dimension");
dim.appendChild(horiz);
dim.appendChild(vert);
dim.appendChild(horizScreenSize);
dim.appendChild(vertScreenSize);
IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
root.appendChild(dim);
metadata.mergeTree("javax_imageio_1.0", root);
}
public static BufferedImage scaleImage(BufferedImage original, int width, int height){
BufferedImage scaled = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
AffineTransform at = new AffineTransform();
at.scale(((float)width)/original.getWidth(), ((float)height)/original.getHeight());
Map<RenderingHints.Key, Object> map = new HashMap<>();
map.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
map.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
RenderingHints hints = new RenderingHints(map);
AffineTransformOp scaleOp = new AffineTransformOp(at, hints);
scaled = scaleOp.filter(original, scaled);
return scaled;
}
But the quality of signature image printed on pdf is not at all satisfactory even after attempting 500 DPI.Am I missing something?
So, using the ZetaKey's live-demo, the signature capture returned an acceptable quality PNG image...
Consider the following things:
Can I just use the original, unaltered image?
If not, can I change the order of my transformations? (DPI -> Scale Vs Scale -> DPI )
Can I forgo scaling? (often the cause of resolution loss)
I suspect that you are losing image resolution because you're adjusting DPI after you scale the image.
Effects of Scaling and DPI:
Effects of Image Transformations (DPI)
Excerpt: (not directly related, but may give insight into problem)
Now suppose you want to print your image using a regular offset press, which can print up to 300dpi images. Suppose you try to print it exactly at the original image dimension in inches. The press needs 300 dots for every inch to do a good job. The image you are supplying has only 72 dots for every inch, so the press (or more accurately, the software that prepares the plates for the press) has to make up the missing dots. The result will be blurry or noisy because there will be a lot of "transitional" dots that the software will creatively add to fill the missing gaps.
I'm creating a domino game in java. I have the following code that loads, resizes and then display the domino image on the screen:
ImageIcon imageIcon = new ImageIcon("images\\4-4.png");
Image image = imageIcon.getImage();
Image newimg = image.getScaledInstance(60, 120, java.awt.Image.SCALE_SMOOTH);
imageIcon = new ImageIcon(newimg);
JLabel img = new JLabel(imageIcon);
img.setBounds(100, 100, 60, 120);
getContentPane().add(img);
What I want to do is rotate the image either 90 or -90 degrees. I've searched the internet but the examples I've found seems very complicated.
Any idea how I can rotate my image?
Btw, if you think that this is not the correct way to display dominoes in a domino game then please let me know. I'me a java newbie.
Rotating an image is non-trival, even just 90 degrees requires a certain amount of work.
So, based on pretty much every other question about rotating images, I'd start with something like...
public BufferedImage rotate(BufferedImage image, Double degrees) {
// Calculate the new size of the image based on the angle of rotaion
double radians = Math.toRadians(degrees);
double sin = Math.abs(Math.sin(radians));
double cos = Math.abs(Math.cos(radians));
int newWidth = (int) Math.round(image.getWidth() * cos + image.getHeight() * sin);
int newHeight = (int) Math.round(image.getWidth() * sin + image.getHeight() * cos);
// Create a new image
BufferedImage rotate = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotate.createGraphics();
// Calculate the "anchor" point around which the image will be rotated
int x = (newWidth - image.getWidth()) / 2;
int y = (newHeight - image.getHeight()) / 2;
// Transform the origin point around the anchor point
AffineTransform at = new AffineTransform();
at.setToRotation(radians, x + (image.getWidth() / 2), y + (image.getHeight() / 2));
at.translate(x, y);
g2d.setTransform(at);
// Paint the originl image
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
return rotate;
}
While you're only rotate 90 degrees, this takes care of calculating the required size the new image needs in order to be able to paint the rotated image, at any angle.
It then simply makes use of AffineTransform to manipulate the origin point from which painting occurs - get use to this, you will do it a lot.
Then, I load the images, rotate them and display them...
try {
BufferedImage original = ImageIO.read(getClass().getResource("domino.jpg"));
BufferedImage rotated90 = rotate(original, 90.0d);
BufferedImage rotatedMinus90 = rotate(original, -90.0d);
JPanel panel = new JPanel();
panel.add(new JLabel(new ImageIcon(original)));
panel.add(new JLabel(new ImageIcon(rotated90)));
panel.add(new JLabel(new ImageIcon(rotatedMinus90)));
JOptionPane.showMessageDialog(null, panel, null, JOptionPane.PLAIN_MESSAGE, null);
} catch (IOException ex) {
ex.printStackTrace();
}
I prefer to use ImageIO to load images, because it throws an IOException when something goes wrong, rather then failing silently like ImageIcon.
You should also be embedding your resources within your application's context, this makes it easier to load them at runtime. Depending on IDE and how your project is set up, how you do this will change, but in "most" cases, you should be able to add the resource directly to your source directory (preferably in sub directory) and the IDE will make it available for you and package it when you export the project
Solution from: http://www.java2s.com/Code/Java/Advanced-Graphics/RotatingaBufferedImage.htm
AffineTransform tx = new AffineTransform();
tx.rotate(0.5, bufferedImage.getWidth() / 2, bufferedImage.getHeight() / 2);
AffineTransformOp op = new AffineTransformOp(tx,
AffineTransformOp.TYPE_BILINEAR);
bufferedImage = op.filter(bufferedImage, null);
I want to convert my picture from colored to Black and white which seems to be created from scratch.
Here is the code which i tried as described on the different post:
BufferedImage bi = ImageIO.read(new File("/Users/***/Documents/Photograph.jpg"));
ColorConvertOp op =
new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
ImageIO.write(bi, "PNG", new File("/Users/bng/Documents/rendered2.png"));
op.filter(bi, bi);
But still my image is not converted to the Black and white. Additionally, this code is increasing the rendered2.png image size to 10 folds.
Also, it would be great if i could find some Java 8 way of doing this.
Any suggestions?
Here is the code which worked for me:
BufferedImage input = ImageIO.read(new File("/Users/bng/Documents/Photograph.jpg"));
// Create a black-and-white image of the same size.
BufferedImage im = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
// Get the graphics context for the black-and-white image.
Graphics2D g2d = im.createGraphics();
// Render the input image on it.
g2d.drawImage(input, 0, 0, null);
// Store the resulting image using the PNG format.
ImageIO.write(im, "PNG", new File("/Users/bng/Documents/rendered.png"));
It was BufferedImage.TYPE_BYTE_BINARY which provided me the exact solution.
Lokking for the Java 8 Version for above code.
You have to find RGB of the existing colors of the image you want to change it.
Fyi, you want to change it as white RGB value is (255,255,255) and for black RGB value is (0,0,0)
Following method easily do the color change if you apply correct way of your requirement
private BufferedImage changeColor(BufferedImage image, int srcColor, int replaceColor)
{
BufferedImage destImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = destImage.createGraphics();
g.drawImage(image, null, 0, 0);
g.dispose();
for (int width = 0; width < image.getWidth(); width++)
{
for (int height = 0; height < image.getHeight(); height++)
{
if (destImage.getRGB(width, height) == srcColor)
{
destImage.setRGB(width, height, replaceColor);
}
}
}
return destImage;
}
you have to use the ColorConvertOp in a proper way:
create Source image
apply filter
save dest
example:
BufferedImage src = ImageIO.read(new File("/Users/***/Documents/Photograph.jpg"));
ColorConvertOp op =
new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
BufferedImage dest = op.filter(src, null);
ImageIO.write(dest, "PNG", new File("/Users/bng/Documents/rendered2.png"));
src:
dest:
In the code i'm setting the alpha value of a pixel to 100 for entire image and I want the Alpha value to be 100 while reading the image. But at the retrieving part it gives me 255(Default Value) . What is wrong ? and how to solve it ? Any Help would be appreciated...
class Demo
{
Demo()
{
try
{
BufferedImage im2 = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
File f2 = new File("test2.jpg");
im2 = ImageIO.read(f2);
int width1 = im2.getWidth();
int height1 = im2.getHeight();
for(int i=0;i<height1;i++)
{
for(int j=0;j<width1;j++)
{
Color c = new Color(50,0,0,100); //Set the alpha value to 100
im2.setRGB(j,i,c.getRGB()); // for every pixel
}
}
File f = new File("Demo_copy.jpg");
ImageIO.write(im2,"jpg",f);
// Retrieving.........
BufferedImage im1;
File f1 = new File("Demo_copy.jpg");
im1 = ImageIO.read(f1);
int width = im1.getWidth();
int height = im1.getHeight();
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
int pixel = im1.getRGB(j,i);
Color c = new Color(pixel,true);
int a = c.getAlpha();
System.out.println("Alpha value is :"+a); // Printing Alpha : 255 for every pixel
}
}
}catch(Exception e){}
}
public static void main(String [] ar)
{
new Demo();
}
}
The new BufferedImage(...) you assign to im2 is just thrown away (garbage collected) after you assign a new value from ImageIO.read(..). As the new value is a JPEG and doesn't have alpha, it does not matter what alpha values you set. They will always stay 255 (completely opaque).
Instead, you probably want to do something like this:
// Read opaque image...
BufferedImage img = ImageIO.read(new File("test2.jpg"));
// ...convert image to TYPE_INT_ARGB...
BufferedImage im2 = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = im2.createGraphics();
try {
g.drawImage(img, 0, 0, null);
}
finally {
g.dispose();
}
// ... loop over and change alpha in im2 as before
Finally, you should write the image in a format that supports lossless alpha, like PNG instead of JPEG, to be sure you get the values you expect:
ImageIO.write(im2,"PNG", new File("Demo_copy.png"));
PS: It might just work using JPEG too, as the built-in Java ImageIO JPEG plugin supports reading/writing JPEGs with alpha values. However, most other software will misinterpret these as CMYK JPEGs, and the colors will look all wrong. Also, JPEG is lossy, so you will most likely not see the exact alpha value (100) as you would expect on the receiving end. That's why I suggest using PNG. TIFF or other format that supports alpha would also work, but requires extra plugins.
i am using Graphics2D in java to resize images, it works perfect with jpg,png and other formats.
my problem is the animated GIF images, after re-sizing the animation is gone!
here is the method am using:
private BufferedImage doResize(int newWidth, int newHeight, double scaleX,
double scaleY, BufferedImage source) {
GraphicsConfiguration gc = getDefaultConfiguration();
BufferedImage result = gc.createCompatibleImage(newWidth, newHeight, source.getColorModel().getTransparency());
Graphics2D g2d = null;
try {
g2d = result.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
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.scale(scaleX, scaleY);
g2d.drawImage(source, 0, 0, null);
} finally {
if (g2d != null) {
g2d.dispose();
}
}
return result;
}
so, any clues how can i keep on the animated gif after re-sizing?
Thanks.
So I know this is old but I found a solution, I am using Java 8 not sure if it will work with other versions.
ImageIcon image = ? (whatever/wherever your gif is)
int width = 100;
int height = 100;
image.setImage(image.getImage().getScaledInstance(width, height, Image.SCALE_DEFAULT));
you can change SCALE_DEFAULT to the ones listed here except for SCALE_SMOOTH and SCALE_AREA_AVREAGING didn't work for me, it was blank
https://docs.oracle.com/javase/7/docs/api/java/awt/Image.html
I found two sources which when combined can be used to resize the image while keeping the animation.
On this question (
Convert each animated GIF frame to a separate BufferedImage ) look for the answer by Alex Orzechowski. His code takes a gif file and converts it to an array of ImageFrames (which is a class he made which wraps a BufferedImage). Then look at this code which converts a sequence of BufferedImages to a gif file
( http://elliot.kroo.net/software/java/GifSequenceWriter/ ).
As you could probably guess, all you need to do is upload the gif, use Alex's code to convert it to an array of ImageFiles/BufferedImages, use your Graphics2D code to resize each frame (you'll need to add a setImage method to Alex's ImageFrame class), then use Elliot's code to convert the array to a gif! Here is what mine looks like:
public static void main( String[] args )
{
try {
File imageFile = new File( "InputFile" );
FileInputStream fiStream = new FileInputStream( imageFile );
ImageFrame[] frames = readGif( fiStream );
for( int i = 0; i < frames.length; i++ ){
//code to resize the image
BufferedImage image = ImageUtilities.resizeImage( frames[ i ].getImage(), newWidth, newHeight);
frames[ i ].setImage( image );
}
ImageOutputStream output =
new FileImageOutputStream( new File( "OutputFile" ) );
GifSequenceWriter writer =
new GifSequenceWriter( output, frames[0].getImage().getType(), frames[0].getDelay(), true );
writer.writeToSequence( frames[0].getImage() );
for ( int i = 1; i < frames.length; i++ ) {
BufferedImage nextImage = frames[i].getImage();
writer.writeToSequence( nextImage );
}
writer.close();
output.close();
}
catch ( FileNotFoundException e ) {
System.out.println( "File not found" );
}
catch ( IOException e ) {
System.out.println( "IO Exception" );
}
}
This code, however, does not account for gif images with different amount of time elapsing between frames.
Jonny March's solution did not work for me because it processes and outputs only the first image of GIF file.
Here is my solution, it keeps the animation while resizing.
File f = new File("path of your animated gif");
URL img = f.toURL();
ImageIcon icon = new ImageIcon(img);
//You have to convert it to URL because ImageIO just ruins the animation
int width = 100;
int height = 100;
icon.setImage(icon.getImage().getScaledInstance(width, height,Image.SCALE_DEFAULT));
BufferedImage origBuffImg = ImageIO.read(orignalImage);
int type = origBuffImg.getType() == 0? BufferedImage.TYPE_INT_ARGB : origBuffImg.getType();
BufferedImage resizedBuffImg = new BufferedImage(width, height, type);
Graphics2D g = resizedBuffImg.createGraphics();
g.drawImage(origBuffImg, 0, 0, width, height, null);
g.dispose();
String newFile = orignalImage.getAbsolutePath().substring(0,orignalImage.getAbsolutePath().lastIndexOf("."))+"_"+width+"x"+height+"."+extension;
ImageIO.write(resizedBuffImg, extension, new File(newFile));
System.out.println("File created : "+newFile);