Java uses thumbnail to gracefully handle images -- image transcoding and thumbnail generation

Posted by nishmgopal on Sat, 22 Jan 2022 08:58:20 +0100

"This is the fourth day of my participation in 2022's first update challenge. See the activity details: 2022 first update challenge」.

Article introduction

Sometimes, our project needs to generate small thumbnails for business images or transcode the image format. What method will you use?

Directly use object storage and hand over business requirements to a third party? Or use javaScript to manipulate images?

Have you ever tried back-end direct processing?

This article introduces how to use Java to process pictures gracefully; Including: transcoding of mainstream image format, image compression (thumbnail generation), image with watermark, etc. External toolkits mainly used:

If you want to support more pictures, you can extend the Java picture IO stream, which will be discussed later

Video tutorial

A video is simply recorded. If necessary, you can go to station b:

6 minutes learn to use Java "hard core" to compress and transcode pictures -- picture transcoding and thumbnail generation

Image transcoding / thumbnail generation

principle

The thumbnail package used this time is actually encapsulated classes and methods, which are implemented by Java based API interfaces such as Image I/O API and Java 2D API.

So, because based on Java Image I/O API Therefore, the supported image formats are limited, but they have met most situations. Generally supported formats are as follows:

  • Read: JPEG 2000, JPG, tiff, bmp, PCX, gif, WBMP, PNG, RAW, JPEG, PNM, tif, TIFF, wbmp, jpeg, jbig2, jpg, JPEG2000, BMP, pcx, GIF, png, raw, JBIG2, pnm, TIF, jpeg2000, jpeg 2000
  • Write: JPEG 2000, JPG, tiff, bmp, PCX, gif, WBMP, PNG, RAW, JPEG, PNM, tif, TIFF, wbmp, jpeg, jpg, JPEG2000, BMP, pcx, GIF, png, raw, pnm, TIF, jpeg2000, jpeg 2000

Many article authors introduced this package and even said that they support Apple's HEIC format. This definitely does not support using thumbnail for processing. Unless you can write a transcoder directly. But in this way, I think it's better to call ImageMagick . I'll have a chance to share this with you in the future.

How to install

First, add the lib package. If you are a Maven project or a project managed by Maven, add dependencies:

<dependency>
  <groupId>net.coobird</groupId>
  <artifactId>thumbnailator</artifactId>
  <version>[0.4, 0.5)</version>
</dependency>
Copy code

The above dependency definition is to get 0.4 The latest available version of thumbnail in the X version range. If you need a specific version of thumbnail, replace [0.4, 0.5) with a specific version number, such as 0.4.13

In addition, if the download is too slow, you can change Maven to a domestic download source (for example: Ali Maven image source)

If you are not a Maven project, you can download the latest version of thumbnail. How to add lib package manually? Download the latest version of thumbnail: github.com/coobird/thu...

How to use

The use of thumbnail is very simple. Originally, you need to use Java's Image I/O API, BufferedImages and Graphics2D to process images. Thumbnail directly encapsulates the above operations. Simple use demonstration:

Thumbnails.of(new File("path/to/directory").listFiles())
    .size(640, 480)
    .outputFormat("jpg")
    .toFiles(Rename.PREFIX_DOT_THUMBNAIL);
Copy code
  • Original picture address: path/to/directory
  • Output picture size: 640 * 480
  • Output picture format: jpg
  • IO stream output address (output picture): Rename PREFIX_ DOT_ THUMBNAIL

Picture transcoding

Demo code:

Thumbnails.of(originalImage).scale(scale)
        .outputFormat("jpg")
        .outputQuality(compression)
        .toFile(thumbnailImage);
Copy code

Of which:

  • scale is the proportional scaling of the picture size, which is of type float.
  • outputFormat is the type of output picture. Note: webp is not supported by default. If webp needs to be used, it needs to be installed in advance webp-imageio-core , you can see how to make Java support Webp.
  • outputQuality is the quality of the output picture, i.e. definition / resolution.

Generate thumbnails using the original picture

Demo code:

Thumbnails.of(new File("original.jpg"))
        .size(160, 160)
        .toFile(new File("thumbnail.jpg"));
Copy code

For the original picture file, the String can be used instead of the address:

Thumbnails.of("original.jpg")
        .size(160, 160)
        .toFile("thumbnail.jpg");
Copy code

Usually, the thumbnail output volume is very small, but it can still be used Outputquality to reduce picture quality (resolution).

Rotate picture

It's simple; add to. Just rotate. For example:

Thumbnails.of(new File("original.jpg"))
        .rotate(90)
        .toFile(new File("image-with-watermark.jpg"));
Copy code

Add watermark

Adding watermark is also very simple Watermark:

Thumbnails.of(new File("original.jpg"))
        .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File("watermark.png")), 0.5f)
        .toFile(new File("image-with-watermark.jpg"));
Copy code

Practical demonstration

I used the above package on my website to build an online presentation address: tool.mintimate.cn/processIMG

Function: after the user uploads the picture, the system has the user's choice of output format and transcoding the picture.

Extension Judgment

When the front end transmits pictures to the background, we can judge the file extension:

String thumbnailImageName=originalImage.getName(); //Thumbnail output name
String thumbnailImagePath; //Thumbnail output address
switch (format){
           case "JPG":
               thumbnailImagePath=System.getProperty("user.dir") + "/file/Output/"
                       + thumbnailImageName.substring(0,thumbnailImageName.lastIndexOf("."))+".jpg";
               break;
           case "PNG":
               thumbnailImagePath=System.getProperty("user.dir") + "/file/Output/"
                       + thumbnailImageName.substring(0,thumbnailImageName.lastIndexOf("."))+".png";
               break;
           case "WEBP":
               thumbnailImagePath=System.getProperty("user.dir") + "/file/Output/"
                       + thumbnailImageName.substring(0,thumbnailImageName.lastIndexOf("."))+".webp";
               break;
           case "BMP":
               thumbnailImagePath=System.getProperty("user.dir") + "/file/Output/"
                       + thumbnailImageName.substring(0,thumbnailImageName.lastIndexOf("."))+".bmp";
               break;
           default:
               thumbnailImagePath=System.getProperty("user.dir") + "/file/Output/" + thumbnailImageName;
               break;
       }
Copy code

Create a blank thumbnail file

Although the thumbnail can directly and automatically create the corresponding file object according to the String, in order to facilitate our own control, we create it manually:

File thumbnailImage = new File(thumbnailImagePath);
        // Judge whether the path exists. If it does not exist, create it
        if (!thumbnailImage.getParentFile().exists()) {
            thumbnailImage.getParentFile().mkdirs();
        }
Copy code

Transcoding picture

try {
            switch (format){
                case "JPG":
                    Thumbnails.of(originalImage).scale(scale)
                            .addFilter(new ThumbnailsImgFilter())
                            .outputFormat("jpg")
                            .outputQuality(compression)
                            .toFile(thumbnailImage);
                    break;
                case "PNG":
                    Thumbnails.of(originalImage).scale(scale)
                            .outputFormat("png")
                            .outputQuality(compression)
                            .toFile(thumbnailImage);
                    break;
                case "WEBP":
                    Thumbnails.of(originalImage).scale(scale)
                            .imageType(ThumbnailParameter.DEFAULT_IMAGE_TYPE)
                            .outputFormat("webp")
                            .outputQuality(compression)
                            .toFile(thumbnailImage);
                    break;
                case "BMP":
                    Thumbnails.of(originalImage).scale(scale)
                            .addFilter(new ThumbnailsImgFilter())
                            .outputFormat("bmp")
                            .outputQuality(compression)
                            .toFile(thumbnailImage);
                    break;
                default:
                    Thumbnails.of(originalImage).scale(scale)
                            .imageType(ThumbnailParameter.DEFAULT_IMAGE_TYPE)
                            .outputQuality(compression)
                            .toFile(thumbnailImage);
                    break;
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
Copy code

Because I use Springboot to quickly build, I actually created a configuration rule addFilter, which can render the transparent background white when PNG transparent pictures are converted to JPG. (just to look good...). Implementation details:

import net.coobird.thumbnailator.filters.ImageFilter;

import java.awt.*;
import java.awt.image.BufferedImage;

public class ThumbnailsImgFilter implements ImageFilter {
    @Override
    public BufferedImage apply(BufferedImage bufferedImage) {
        int w = bufferedImage.getWidth();
        int h = bufferedImage.getHeight();
        BufferedImage newImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphic = newImage.createGraphics();
        graphic.setColor(Color.white);//The background is set to white
        graphic.fillRect(0, 0, w, h);
        graphic.drawRenderedImage(bufferedImage, null);
        graphic.dispose();
        return newImage;
    }
}
Copy code

In this way, the picture can be transcoded successfully (if used properly, the picture can also be compressed) Д`)):  

The left is the original image and the right is the transcoded image. When the size remains unchanged, the picture quality becomes 80% of the original; The main file size becomes smaller due to the Webp format. Below we introduce the Java transcoding Webp format.

Java handles Webp format

What is Webp format

According to Wiki Encyclopedia: webp (pronunciation: weppy]) is a picture file format that provides both lossy compression and lossless compression (reversible compression). It is derived from the image coding format. It is considered to be a sister project of WebM multimedia format. It was developed by Google after purchasing On2 Technologies and published under BSD license terms. The advantages of webp are obvious:

  • Better image data compression algorithm
  • Smaller picture size
  • No difference in image quality is recognized by naked eye
  • Lossless and lossy compression mode
  • Alpha transparency and animation properties

In short, it can preserve lossless image quality and maintain image transparency like PNG format; At the same time, you can compress pictures like JPG. Webp in the same case, the file size is smaller than PNG, even smaller than JPG.

Support Webp format

because Webp In fact, it was developed by Google, so the Webp format was not supported at the beginning of Java IO stream design.

There are many ways to make Java IO Streams Support Webp. Here is a method to accept more methods

Depending on the system, you need to install the corresponding dependent packages:

/natives
  /linux_32
     libxxx[-vvv].so
  /linux_64
     libxxx[-vvv].so
  /osx_32
     libxxx[-vvv].dylib
  /osx_64
     libxxx[-vvv].dylib
  /osx_arm64
     libxxx[-vvv].dylib
  /windows_32
     xxx[-vvv].dll
  /windows_64
     xxx[-vvv].dll
  /aix_32
     libxxx[-vvv].so
     libxxx[-vvv].a
  /aix_64
     libxxx[-vvv].so
     libxxx[-vvv].a
 Copy code

Refer to the following items: github.com/scijava/nat... Of course, you can also directly use the lib package integrated by God, such as: webp-imageio-core ; How to use it is explained in detail below.

Webp imageio core usage

Because webp imageio core is not published to Maven central warehouse, users using Maven skeleton need to add lib dependency. First, download the jar release package of webp imageio core at: github.com/nintha/webp... Then add custom < dependency >:

<dependency>  
    <groupId>com.github.nintha</groupId>  
    <artifactId>webp-imageio-core</artifactId>  
    <version>{version}</version>  
    <scope>system</scope>  
    <systemPath>${pom.basedir}/libs/webp-imageio-core-{version}.jar</systemPath>  
</dependency>
Copy code

For example, for my project, add local lib:

At this time, Java already supports the processing of Webp format images.

Practical use

The simplest way to use... Is to add the thumbnail dependency package mentioned above, and you can use thumbnail to directly process image IO streams. When used alone, we can use the most traditional method: image to WEBP:

public static void main(String args[]){
        String srcFile = System.getProperty("user.dir") + "/file/Input/"+"Input.png" //Original address
        String webpFile = System.getProperty("user.dir") + "/file/Output/"+"Output.png" //Output address
        encodingToWebp(srcFile, webpFile);
    }
    
    public static void encodingToWebp(String srcFile, String webpFile) {
        encodingToWebp(new File(srcFile),new File(webpFile) );
    }
 
    /**
     * @param: srcFile
     * @param: webpFile
     * @description: Encode the file in WEBP format
     * @author: Mintimate
     */
    public static void encodingToWebp(File srcFile, File webpFile) {
 
        try {
 
            // Obtain an image to encode from somewhere
            BufferedImage image = ImageIO.read(srcFile);
 
            // Obtain a WebP ImageWriter instance
            ImageWriter writer = ImageIO.getImageWritersByMIMEType("image/webp").next();
 
            // Configure encoding parameters
            WebPWriteParam writeParam = new WebPWriteParam(writer.getLocale());
            writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            writeParam.setCompressionType(writeParam.getCompressionTypes()[WebPWriteParam.LOSSLESS_COMPRESSION]);
 
            // Configure the output on the ImageWriter
            writer.setOutput(new FileImageOutputStream(webpFile));
 
            // Encode
            writer.write(null, new IIOImage(image, null, null), writeParam);
 
 
            //Release reader
            writer.dispose();
 
            //Close file stream
            fileImageOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
}
Copy code

last

At this point, images can be handled gracefully using thumbnail.

However, you will find that in some cases, the image processing (jpeg image processing) will be red. The processing method is very simple. You can use BufferedImage to write and read pictures, so that there will be no distortion of red or pink pictures.

Have the opportunity to accept other java image IO expansion packages to enable Java projects to support more image formats.

Topics: Java Back-end Programmer architecture