How to copy and compress and then paste multiple jpg images [duplicate] - java

From pagespeed I am getting only image link and possible optimizations in bytes & percentage like,
Compressing and resizing https://example.com/…ts/xyz.jpg?036861 could save 212KiB (51% reduction).
Compressing https://example.com/…xyz.png?303584508 could save 4.4KiB (21% reduction).
For an example I have image of size 300kb and for this image pagespeed is displaying 100kb & 30% of reduction.
This is only for one image but I am sure I will have lots of images for compression.
so how can I compress image by passing bytes or percentage as a parameter or using anyother calculations in java
(by using API or image-processing Tool) so,that I can get compressed version of image as suggested by google.
Thanks in advance.

You can use Java ImageIO package to do the compression for many images formats, here is an example
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Iterator;
import javax.imageio.*;
import javax.imageio.stream.*;
public class Compresssion {
public static void main(String[] args) throws IOException {
File input = new File("original_image.jpg");
BufferedImage image = ImageIO.read(input);
File compressedImageFile = new File("compressed_image.jpg");
OutputStream os = new FileOutputStream(compressedImageFile);
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
ImageWriter writer = (ImageWriter) writers.next();
ImageOutputStream ios = ImageIO.createImageOutputStream(os);
writer.setOutput(ios);
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.05f); // Change the quality value you prefer
writer.write(null, new IIOImage(image, null, null), param);
os.close();
ios.close();
writer.dispose();
}
}
You can find more details about it here
Also there are some third party tools like these
https://collicalex.github.io/JPEGOptimizer/
https://github.com/depsypher/pngtastic
EDIT: If you want to use Google PageSpeed in your application, it is available as web server module either for Apache or Nginx, you can find how to configure it for your website here
https://developers.google.com/speed/pagespeed/module/
But if you want to integrate the PageSpeed C++ library in your application, you can find build instructions for it here.
https://developers.google.com/speed/pagespeed/psol
It also has a Java Client here
https://github.com/googleapis/google-api-java-client-services/tree/main/clients/google-api-services-pagespeedonline/v5

There is colour compression ("compression quality") and there is resolution compression ("resizing"). Fujy's answer deals with compression quality, but this is not where the main savings come from: the main savings come from resizing down to a smaller size. E.g. I got a 4mb photo to 207K using the maximum compression quality using fujy's answer, and it looked awful, but I got it down to 12K using a reasonable quality but a smaller size.
So the above code should be used for "compression quality", but this is my recommendation for resizing:
https://github.com/rkalla/imgscalr/blob/master/src/main/java/org/imgscalr/Scalr.java
I wish resizing was part of the standard Java libraries, but it seems it's not, (or there are image quality problems with the standard methods?). But Riyad's library is really small - it's just one class. I just copied this class into my project, because I never learnt how to use Maven, and it works great.

One liner java solution: thumbnailator.
Maven dependency:
<!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.17</version>
</dependency>
The one liner:
Thumbnails.of(inputImagePathString).scale(scalingFactorFloat).outputQuality(qualityFactorFloat).toFile(outputImagePathString);

As a solution for this problem I can recommend the API of TinyPNG.
You can use it for compressing as well as resizing the image.
Documentation: tinypng.com/developers/reference/java

Related

Convert JPEG image to TIFF without increasing file size

I am trying to convert a JPEG image to TIFF. The converted TIFF image is three times larger.
Can someone help me get a TIFF image with the size of the original JPEG?
import com.sun.media.jai.codec.FileSeekableStream;
import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.ImageDecoder;
import com.sun.media.jai.codec.ImageEncoder;
import com.sun.media.jai.codec.JPEGDecodeParam;
import com.sun.media.jai.codec.SeekableStream;
import com.sun.media.jai.codec.TIFFEncodeParam;
import java.io.FileOutputStream;
public class ConvertJPEGtoTIFF{
public static void main(String args[]) throws Exception{
// read input JPEG file
SeekableStream s = new FileSeekableStream("C:\\Testsmall\\Desert.jpg");
JPEGDecodeParam jpgparam = new JPEGDecodeParam();
ImageDecoder dec = ImageCodec.createImageDecoder("jpeg", s, jpgparam);
RenderedImage op = dec.decodeAsRenderedImage(0);
FileOutputStream fos = new FileOutputStream("C:\\Testsmall\\index33.tiff");
TIFFEncodeParam param = new TIFFEncodeParam();
ImageEncoder en = ImageCodec.createImageEncoder("tiff", fos, param);
en.encode(op);
fos.flush();
fos.close();
}
}
As explained by Erwin Bolwidt in a comment:
TIFF is a container format that can contain different kinds of images, compressed or uncompressed. However, you are using the default settings of TIFFEncodeParam. As you can read in the Javadoc for the class, that means no compression:
This class allows for the specification of encoding parameters. By
default, the image is encoded without any compression, and is written
out consisting of strips, not tiles.
As a consequence, your TIFF file is much larger than the JPEG file, which uses lossy image compression.
If you want a smaller file size, you must specify a compression (using setCompression). You can use DEFLATE for a lossless compression, or JPEG for JPEG compression (then you should also set JPEG parameters using setJPEGEncodeParam). The latter should yield a file size similar to the JPEG file.
Note, however, that TIFF is typically used with lossless compression. If you want to use JPEG compression, first check whether the intended recipient of the TIFF files you produce can handle it.
You can control the quality by setting the compression quality level. Unfortunately converting the files this way is lossy. The ideal way is to find a program that does not decode then re-encode the jpeg data but rather transfers it to the tiff format without modifying the compression. I have not seen such a utility.
I am not sure how to set the quality in your coding case, but you could try "jpeg:75" or "jpeg:50" in place of just "Jpeg" for compression (I am basing this on the tiffcp command).

Decoding of old style JPEG-in-TIFF data is not supported

I need to display the 3rd page of scanned tiff files. i used the code
TIFFReader reader = new TIFFReader(new File(pathOfFile));
RenderedImage image = reader.getPage(2);
its sometimes work. and show error : Decoding of old style JPEG-in-TIFF data is not supported.
I used aspriseTIFF.jar
then how i solve this problem.
please reply.
thanks in advance
The problem you have run into is that "old style" JPEG compression in the TIFF format (compression == 6), is not supported in the library you use.
This is quite common I guess, as "old-style" JPEG compression is deprecated in TIFF, because it was never fully specified. And because of this under-specification, various vendors implemented it in different, incompatible ways. Support was dropped in favor for TIFF compression 7, JPEG.
Unfortunately, old TIFF files using this compression still exists, so you need to find another library. The good news is that you can use ImageIO and a proper plug-in.
Using a TIFF ImageReader plug-in, like the one from my TwelveMonkeys ImageIO open source project, you should be able to do this:
// Create input stream
try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
// Get the reader
ImageReader reader = ImageIO.getImageReaders(input).next();
try {
reader.setInput(input);
// Read page 2 of the TIFF file
BufferedImage image = reader.read(2, null);
}
finally {
reader.dispose();
}
}
(sorry about the try/finally boiler-plate, but it is important to avoid resource/memory leaks).

Convert SVG to PDF

How would one go about converting a SVG file to a PDF programatically? (I need to alter the SVG in certain respects before generating the PDF so simply pre-converting it using a tool won't be sufficient.)
Ideally using Java but Perl or PHP would be fine too.
Obviously I am basically considering Apache FOP and Batik with Java. However no matter how long I search I cannot find a simple introduction on how to do it. Things like SVGConverter have descriptions like "Defines the interface for classes that are able to convert part or all of a GraphicContext", but I don't really know what that means.
I have this feeling there must be an API to do this quite simply, provided by FOP or Batik, but I'm just not able to find it at the moment (or perhaps it really doesn't exist.)
In terms of the supported SVG features I need, the file has some paths which are filled with some linear gradients.
Ideally if I could pass the SVG in as a DOM Document that would be ideal; then I would load my template SVG file, change it as specified by the user, and then generate the PDF.
Thanks to Adrian for showing how the Batik rasterizer API is supposed to be used. However, I needed a more lightweight solution--- I can't write to temporary files, and I want fewer dependencies. So, starting from the methods he pointed to, I found a way to access the lower-level code to do the conversion and nothing else.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import org.apache.batik.transcoder.Transcoder;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.fop.svg.PDFTranscoder;
public class Test {
public static void main(String[] argv) throws TranscoderException, FileNotFoundException {
Transcoder transcoder = new PDFTranscoder();
TranscoderInput transcoderInput = new TranscoderInput(new FileInputStream(new File("/tmp/test.svg")));
TranscoderOutput transcoderOutput = new TranscoderOutput(new FileOutputStream(new File("/tmp/test.pdf")));
transcoder.transcode(transcoderInput, transcoderOutput);
}
}
The compile-and-run commands are
javac -cp batik-rasterizer.jar -d build Test.java
java -cp build:batik-rasterizer.jar Test
The important point is that TranscoderInput and TranscoderOutput can work with any InputStream and OutputStream, not just file streams. Note that one of the constructors takes a org.w3c.dom.Document, which means that you don't even need to serialize an SVG DOM into an SVG string, saving an additional step.
This version also doesn't write anything to stdout/stderr, unlike the high-level API.
For JPEG, PNG, or TIFF output, replace org.apache.fop.svg.PDFTranscoder with org.apache.batik.transcoder.image.JPEGTranscoder, PNGTranscoder, or TIFFTranscoder (note that these raster formats are in a different package).
(I'm not quite sure how Java finds the org.apache.batk.transcoder.* and org.apache.fop.svg.PDFTranscoder classes, since I don't see them in the batik-rasterizer.jar.)
Edit:
Although the simple commandline-compilation works with the batik-rasterizer.jar only, it's doing some sort of classloader magic to find all the necessary classes. In a more realistic case (building a project with Ant), you have to find the classes by hand. They can be found in batik-1.7.zip from the Batik project and fop-1.1.zip from the FOP project. From Batik, you need to compile with batik-transcoder.jar and run with
batik-transcoder.jar
batik-anim.jar
batik-awt-util.jar
batik-bridge.jar
batik-css.jar
batik-dom.jar
batik-ext.jar
batik-gvt.jar
batik-parser.jar
batik-script.jar
batik-svg-dom.jar
batik-util.jar
batik-xml.jar
xml-apis-ext.jar
From FOP, you need to compile with fop.jar and run with
fop.jar
avalon-framework-4.2.0.jar
xmlgraphics-commons-1.5.jar
I finally managed to find the appropriate lines of code to solve this using the Batik.
You need to have the SVG file and the resulting PDF as files on the disk, i.e. I couldn't find a way to do it in-memory (I am writing a HTTP Servlet so I have no intrinsic need to write anything as a file, ideally I would stream the result to the HTTP client). I used File.createTemporaryFile to create a file to dump out my SVG to a file, and for the resulting PDF to be written to.
So the lines I used are the following:
import org.apache.batik.apps.rasterizer.DestinationType;
import org.apache.batik.apps.rasterizer.SVGConverter;
import ...
// SVG available as a DOM object (created programatically by my program)
Document svgXmlDoc = ...
// Save this SVG into a file (required by SVG -> PDF transformation process)
File svgFile = File.createTempFile("graphic-", ".svg");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
DOMSource source2 = new DOMSource(svgXmlDoc);
FileOutputStream fOut = new FileOutputStream(svgFile);
try { transformer.transform(source2, new StreamResult(fOut)); }
finally { fOut.close(); }
// Convert the SVG into PDF
File outputFile = File.createTempFile("result-", ".pdf");
SVGConverter converter = new SVGConverter();
converter.setDestinationType(DestinationType.PDF);
converter.setSources(new String[] { svgFile.toString() });
converter.setDst(outputFile);
converter.execute();
And I have the following JARs (search using Google to find the projects and download them):
avalon-framework-4.2.0.jar
batik-all-1.7.jar
commons-io-1.3.1.jar
commons-logging-1.0.4.jar
fop-0.95.jar
log4j-1.2.15.jar
xml-apis-ext.jar
xmlgraphics-commons-1.3.1.jar
you will need a libray for rendering svg's and pdf's.
I recommend SVG salamander for the former, and iText for the latter. With svg salamander you can to read the svg and create an image object, and with itext you can write that image to a pdf.
I use Altsoft Xml2PDF. If I understood correctly all your needs and requirement, you'd better try their Server version of Xml2PDF.
All you need is phantomjs. You don't need the unwieldy Batik for this at all; just get to a point where you can run phantomjs, calling rasterize.js, using the url of the pdf as a source, and a location as the output. Depending on what you want to do with the .pdf, you don't even need Java.
http://phantomjs.org/screen-capture.html
Look at the part starting with "Beside PNG format, PhantomJS supports JPEG, GIF, and PDF."

How do we convert WMF/EMF (MS metafiles) into standard images like JPG or PNG using any Java API?

I have been stuck in converting WMF/EMF images into standard image format such as JPG or PNG using Java.
What are the best options available?
The Batik library is a toolkit to handle SVG in Java. There are converters included like WMFTranscoder to convert from WMF to SVG and JPEGTranscoder and PNGTranscoder to convert SVG to JPEG/PNG. See Transcoder API Docs for more details.
Another alternative is ImageMagick. It's not Java but has Java bindings: im4java and JMagick.
wmf is a vector file format. For best results, convert them to .svg or .pdf format.
I did it in two stages
1) wmf2fig --auto XXXX.wmf
2) fig2pdf --nogv XXXX.fig
I created a python script for bulk conversion
import subprocess as sbp
a = sbp.Popen("ls *.wmf",shell=True, stderr=sbp.PIPE, stdout=sbp.PIPE)
filelist = a.communicate()[0].splitlines()
for ele in filelist:
cmdarg = 'wmf2fig --auto '+ ele.rsplit('.',1)[0]+'.wmf'
a = sbp.Popen(cmdarg, shell=True, stderr=sbp.PIPE, stdout=sbp.PIPE)
out = a.communicate()
for ele in filelist:
cmdarg = 'fig2pdf --nogv '+ ele.rsplit('.',1)[0]+'.fig'
a = sbp.Popen(cmdarg, shell=True, stderr=sbp.PIPE, stdout=sbp.PIPE)
out = a.communicate()
cmdarg = 'rm *.fig'
a = sbp.Popen(cmdarg, shell=True, stderr=sbp.PIPE, stdout=sbp.PIPE)
out = a.communicate()
If you are deploying your application in a Windows environment, then SWT can handle the conversion for you.
Image image = new Image(Display.getCurrent(), "test.wmf");
ImageLoader loader = new ImageLoader();
loader.data = new ImageData[] { image.getImageData() };
try(FileOutputStream stream = new FileOutputStream("test.png"))
{
loader.save(stream, SWT.IMAGE_PNG);
}
image.dispose();
The purpose of SWT is to provide a Java wrapper around native functionality, and in this case it is calling the windows GDI directly to get it to render the WMF.
I've created some wrappers around the Batik package (as mentioned by vanje's answer) some time ago, that provides ImageIO support for SVG and WMF/EMF.
With these plugins you should be able to write:
ImageIO.write(ImageIO.read(wmfFile), pngFile, "png");
Source code on GitHub.
While the ImageIO plugins are convenient, im4java and JMagick might still have better format support.
Here is one way.
Get (or make) a Java component that can render the files in question.
Create a BufferedImage the same size as the component needs to display the image.
Get the Graphics object from the BufferedImage.
Call renderComponent.paintComponent(Graphics)
Save the image using one of the ImageIO.write() variants.
See my answer to Swing: Obtain Image of JFrame for steps 2-5. Step 1. is something I'd ask Google about.

Is there a 100% Java alternative to ImageIO for reading JPEG files?

We are using Java2D to resize photos uploaded to our website, but we run into an issue (a seemingly old one, cf.: http://forums.sun.com/thread.jspa?threadID=5425569) - a few particular JPEGs raise a CMMException when we try to ImageIO.read() an InputStream containing their binary data:
java.awt.color.CMMException: Invalid image format
at sun.awt.color.CMM.checkStatus(CMM.java:131)
at sun.awt.color.ICC_Transform.<init>(ICC_Transform.java:89)
at java.awt.image.ColorConvertOp.filter(ColorConvertOp.java:516)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.acceptPixels(JPEGImageReader.java:1114)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImage(Native Method)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1082)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:897)
at javax.imageio.ImageIO.read(ImageIO.java:1422)
at javax.imageio.ImageIO.read(ImageIO.java:1326)
...
(snipped the remainder of the stack trace, which is our ImageIO.read() call, servlet code and such)
We narrowed it down to photos taken on specific cameras, and I selected a photo that triggers this error: http://img214.imageshack.us/img214/5121/estacaosp.jpg.
We noticed that this only happens with Sun's JVM (on Linux and Mac, just tested it on 1.6.0_20) - a test machine with OpenJDK reads the same photos without a hitch, possibly due to a different implementation of the JPEG reader.
Unfortunately, we are unable to switch JVMs in production, nor to use native-dependent solutions such as ImageMagick ( http://www.imagemagick.org/ ).
Considering that, my question is: Does a replacement for ImageIOs JPEG reader which can handle photos such as the linked one exist? If not, is there another 100% pure Java photo resizing solution which we can use?
Thank you!
One possibly useful library for you could be the Java Advanced Imaging Library (JAI)
Using this library can be quite a bit more complicated than using ImageIO but in a quick test I just ran, it did open and display the problem image file you linked.
public static void main(String[] args) {
RenderedImage image = JAI.create("fileload", "estacaosp.jpg");
float scale=(float) 0.5;
ParameterBlock pb = new ParameterBlock();
pb.addSource(image);
pb.add(scale);
pb.add(scale);
pb.add(1.0F);
pb.add(1.0F);
pb.add(new InterpolationNearest() );// ;InterpolationBilinear());
image = JAI.create("scale", pb);
// Create an instance of DisplayJAI.
DisplayJAI srcdj = new DisplayJAI(image);
JScrollPane srcScrollPaneImage = new JScrollPane(srcdj);
// Use a label to display the image
JFrame frame = new JFrame();
frame.getContentPane().add(srcScrollPaneImage, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
After running this code the image seems to load fine. It is then resized by 50% using the ParamaterBlock
And finally if you wish to save the file you can just call :
String filename2 = new String ("tofile.jpg");
String format = new String ("JPEG");
RenderedOp op = JAI.create ("filestore", image, filename2, format);
I hope this helps you out. Best of luck.
Old post, but for future reference:
Inspired by this question and links found here, I've written a JPEGImageReader plugin for ImageIO that supports JPEG images with these kind of "bad" ICC color profiles (the "issue" is the rendering intent in the ICC profile is incompatible with Java's ColorConvertOp). It's plain Java and does not require JAI.
The source code and linked binary builds are freely available from the TwelveMonkeys project on GitHub.
I faced the same issue. I was reluctant to use JAI as it is outdated but it looks like it's the shortest solution.
This code converts an InputStream to a BufferedImage, using sun's ImageIO (fast) or in the few cases where this problem occur, using JAI:
public static BufferedImage read(InputStream is) throws IOException {
try {
// We try it with ImageIO
return ImageIO.read(ImageIO.createImageInputStream(is));
} catch (CMMException ex) {
// If we failed...
// We reset the inputStream (start from the beginning)
is.reset();
// And use JAI
return JAI.create("stream", SeekableStream.wrapInputStream(is, true)).getAsBufferedImage();
}
}

Categories