Various processing of pictures (reading, writing, picture interception, binarization, grayscale, marginalization, multi-image stitching, picture binarization and digital conversion, scaling, fusion, mirroring, rotation, watermarking)

This article is more about providing an idea and a single implementation method. If there is something that does not meet the requirements, you can further integrate and adjust the relevant methods

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Arrays;
import java.util.Objects;

public class ImageUtils {<!-- -->

    /**
     * Grayscale method: grayscale by maximum value (brighter)
     */
    public static final GrayType maxGrayType = new GrayType(1);

    /**
     * Grayscale method: grayscale by average value (softer)
     */
    public static final GrayType avgGrayType = new GrayType(2);

    /**
     * Grayscale method: grayscale by weight (default rgb weight configuration is soft, weight value [r:0.3, g:0.59, b:0.11])
     * To customize the weight value of rgb, please use the weightGrayType(r, g, b) method
     */
    public static final GrayType defaultWeightGrayType = new GrayType(3, 0.3, 0.59, 0.11);

    /**
     *Merge method: horizontal merge
     */
    public static final JointType lateral = new JointType(0, 0);

    /**
     * Merger method: vertical merger
     */
    public static final JointType longitudinal = new JointType(1, 0);

    /**
     * Merge method: Horizontal adaptive merge (using the largest size)
     */
    public static final JointType lateralMaxAdaptive = new JointType(0, 1);

    /**
     * Merge method: vertical adaptive merge (use the largest size)
     */
    public static final JointType longitudinalMaxAdaptive = new JointType(1, 1);

    /**
     * Merge method: Horizontal adaptive merge (use the smallest size)
     */
    public static final JointType lateralMinAdaptive = new JointType(0, 2);

    /**
     * Merge method: vertical adaptive merge (use the smallest size)
     */
    public static final JointType longitudinalMinAdaptive = new JointType(1, 2);

    /**
     * Grayscale method: weight method (custom weight value)
     * @param redRatio red weight (0-1)
     * @param greenRatio green weight (0-1)
     * @param blueRatio blue weight (0-1)
     */
    public static GrayType weightGrayType(double redRatio, double greenRatio, double blueRatio) {<!-- -->
        GrayType grayType = new GrayType(3);
        grayType.redRatio = Math.max(redRatio, 0);
        grayType.greenRatio = Math.max(greenRatio, 0);
        grayType.blueRatio = Math.max(blueRatio, 0);
        return grayType;
    }


    /**
     * read pictures
     * @param imagePath image path
     * @return image object
     */
    public static BufferedImage readImage(String imagePath) {<!-- -->
        BufferedImage image = null;
        File imageFile = new File(imagePath);
        if (!imageFile. exists()) {<!-- -->
            return null;
        }
        try {<!-- -->
            image = ImageIO. read(imageFile);
        } catch (Exception e) {<!-- -->
            e.printStackTrace();
        }
        return image;
    }

    /**
     * Image capture
     * @param image image object
     * @param x The x coordinate of the upper left corner to be intercepted
     * @param y The y coordinate of the upper left corner to be intercepted
     * @param width The length to be intercepted
     * @param height The height to be intercepted
     * @return the captured image object
     */
    public static BufferedImage subImage(BufferedImage image, int x, int y, int width, int height) {<!-- -->
        if (image == null) {<!-- -->
            return null;
        }
        return image.getSubimage(x, y, width, height);
    }

    /**
     * Image capture
     * @param imagePath image path
     * @param x The x coordinate of the upper left corner to be intercepted
     * @param y The y coordinate of the upper left corner to be intercepted
     * @param width The length to be intercepted
     * @param height The height to be intercepted
     * @return the captured image object
     */
    public static BufferedImage subImage(String imagePath, int x, int y, int width, int height) {<!-- -->
        BufferedImage image = readImage(imagePath);
        return subImage(image, x, y, width, height);
    }

    /**
     * Image binarization
     * @param image image object
     * @return binarized image object
     */
    public static BufferedImage binaryImage(BufferedImage image) {<!-- -->
        if (image == null) {<!-- -->
            return null;
        }
        int width = image. getWidth();
        int height = image. getHeight();
        BufferedImage binaryImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
        for (int x = 0; x < width; x ++ ) {<!-- -->
            for (int y = 0; y < height; y ++ ) {<!-- -->
                binaryImage.setRGB(x, y, image.getRGB(x, y));
            }
        }
        return binaryImage;
    }

    /**
     * Image binarization
     * @param imagePath image path
     * @return binarized image object
     */
    public static BufferedImage binaryImage(String imagePath) {<!-- -->
        BufferedImage image = readImage(imagePath);
        return binaryImage(image);
    }


    /**
     * Image grayscale processing
     * @param imagePath image path
     * @param grayType grayscale method
     * @return grayscale image
     */
    public static BufferedImage grayImage(String imagePath, GrayType grayType) {<!-- -->
        BufferedImage image = readImage(imagePath);
        return grayImage(image, grayType);
    }

    /**
     * Image grayscale processing
     * @param image image object
     * @param grayType grayscale method (default: ImageUtils.avgGrayType)
     * @return grayscale image
     */
    public static BufferedImage grayImage(BufferedImage image, GrayType grayType) {<!-- -->
        if (image == null) {<!-- -->
            return null;
        }
        if (grayType == null) {<!-- -->
            grayType = avgGrayType;
        }
        int width = image. getWidth();
        int height = image. getHeight();
        BufferedImage grayImage = new BufferedImage(width, height, image.getType());
        for (int x = 0; x < width; x ++ ) {<!-- -->
            for (int y = 0; y < height; y ++ ) {<!-- -->
                int rgb = image.getRGB(x, y);
                int red = (rgb >> 16) & 0xff;
                int green = (rgb >> 8) & 0xff;
                int blue = rgb & 0xff;
                int gray;
                switch (grayType. type) {<!-- -->
                    case 1:
                        gray = Math.max(Math.max(red, green), blue);
                        break;
                    case 3:
                        gray = (int) (grayType. getRedRatio() * red +
                                grayType. getGreenRatio() * green +
                                grayType. getBlueRatio() * blue);
                        break;
                    case 2:
                    default:
                        gray = (red + green + blue) / 3;
                        break;
                }
                grayImage.setRGB(x, y, 255 << 24 | gray << 16 | gray << 8 | gray);
            }
        }
        return grayImage;
    }

    /**
     * Image marginalization
     * @param imagePath image path
     * @return marginalized image
     */
    public static BufferedImage marginalization(String imagePath) {<!-- -->
        BufferedImage image = readImage(imagePath);
        return marginalization(image);
    }

    /**
     * Image marginalization
     * @param image image object
     * @return marginalized image
     */
    public static BufferedImage marginalization(BufferedImage image) {<!-- -->
        if (image == null) {<!-- -->
            return null;
        }
        int width = image. getWidth();
        int height = image. getHeight();
        BufferedImage marginalizationImage = new BufferedImage(width, height, image.getType());
        for (int x = 0; x < width - 1; x ++ ) {<!-- -->
            for (int y = 0; y < height - 1; y ++ ) {<!-- -->
                int rgb = image.getRGB(x, y);
                int red = (rgb >> 16) & 255;
                int green = (rgb >> 8) & 255;
                int blue = rgb & 255;
                int nextRgb = image.getRGB(x + 1, y + 1);
                int nextRed = (nextRgb >> 16) & 255;
                int nextGreen = (nextRgb >> 8) & 255;
                int nextBlue = nextRgb & 255;
                // Grayscale operation (R=G=B)
                int gray = (red + green + blue) / 3;
                int nextGray = (nextRed + nextGreen + nextBlue) / 3;
                if(Math.abs(gray - nextGray)>15) {<!-- -->
                    marginalizationImage.setRGB(x, y, Color.black.getRGB());
                } else {<!-- -->
                    marginalizationImage.setRGB(x, y, Color.white.getRGB());
                }
            }
        }
        return marginalizationImage;
    }

    /**
     * Picture stitching
     * @param imagePaths sequential image paths (stitching will be done in this order)
     * @param jointType Sorting method: Horizontal: ImageUtils.lateral; Vertical: ImageUtils.longitudinal
     * @return spliced image object
     */
    public static BufferedImage jointImage(String[] imagePaths, JointType jointType) {<!-- -->
        if (imagePaths == null || imagePaths.length == 0) {<!-- -->
            return null;
        }
        BufferedImage[] images = Arrays.stream(imagePaths).filter(Objects::nonNull).map(ImageUtils::readImage).toArray(BufferedImage[]::new);
        return jointImage(images, jointType);
    }

    /**
     * Picture stitching
     * @param images sequential image objects (will be spliced in this order)
     * @param jointType Sorting method: Horizontal: ImageUtils.lateral; Vertical: ImageUtils.longitudinal
     * @return spliced image object
     */
    public static BufferedImage jointImage(BufferedImage[] images, JointType jointType) {<!-- -->
        if (images == null || images. length == 0) {<!-- -->
            return null;
        }
        if (images. length == 1) {<!-- -->
            return images[0];
        }
        int allImageWidth = 0;
        int allImageHeight = 0;
        Integer maxImageWidth = null;
        Integer maxImageHeight = null;
        Integer minImageWidth = null;
        Integer minImageHeight = null;

        for (BufferedImage image : images) {<!-- -->
            if (image == null) {<!-- -->
                continue;
            }
            int imageWidth = image. getWidth();
            int imageHeight = image. getHeight();
            allImageWidth += imageWidth;
            allImageHeight += imageHeight;
            if (maxImageWidth == null || maxImageWidth < imageWidth) {<!-- -->
                maxImageWidth = imageWidth;
            }
            if (maxImageHeight == null || maxImageHeight < imageHeight) {<!-- -->
                maxImageHeight = imageHeight;
            }
            if (minImageWidth == null || minImageWidth > imageWidth) {<!-- -->
                minImageWidth = imageWidth;
            }
            if (minImageHeight == null || minImageHeight > imageHeight) {<!-- -->
                minImageHeight = imageHeight;
            }
        }
        if (maxImageWidth == null) {<!-- -->
            return null;
        }

        // recalculate and process the image
        if (jointType. adaptiveType != 0) {<!-- -->
            for (int index = 0; index < images. length; index ++ ) {<!-- -->
                BufferedImage image = images[index];
                allImageWidth -= image. getWidth();
                allImageHeight -= image. getHeight();
                if (jointType. position == 0 & amp; & amp; jointType. adaptiveType == 1) {<!-- -->
                    image = zoomImage(image, -1, maxImageHeight);
                } else if (jointType. position == 1 & amp; & amp; jointType. adaptiveType == 1) {<!-- -->
                    image = zoomImage(image, maxImageWidth, -1);
                } else if (jointType. position == 0 & amp; & amp; jointType. adaptiveType == 2) {<!-- -->
                    image = zoomImage(image, -1, maxImageHeight);
                } else if (jointType. position == 1 & amp; & amp; jointType. adaptiveType == 2) {<!-- -->
                    image = zoomImage(image, maxImageWidth, -1);
                }
                allImageWidth += image.getWidth();
                allImageHeight += image.getHeight();
                images[index] = images;
            }
        }
        BufferedImage jointImage = null;
        if (jointType. position == 0 & amp; & amp; jointType. adaptiveType != 3) {<!-- -->
            jointImage = new BufferedImage(allImageWidth, maxImageHeight, BufferedImage.TYPE_INT_ARGB);
        }
        else if(jointType. position == 0) {<!-- -->
            jointImage = new BufferedImage(allImageWidth, minImageHeight, BufferedImage.TYPE_INT_ARGB);
        }
        else if (jointType.position == 1 & amp; & amp; jointType.adaptiveType != 3){<!-- -->
            jointImage = new BufferedImage(maxImageWidth, allImageHeight, BufferedImage.TYPE_INT_ARGB);
        }
        else if(jointType. position == 1) {<!-- -->
            jointImage = new BufferedImage(minImageWidth, allImageHeight, BufferedImage.TYPE_INT_ARGB);
        }
        int xIndex = 0;
        int yIndex = 0;
        for (BufferedImage image : images) {<!-- -->
            if (image == null) {<!-- -->
                continue;
            }
            int imageWidth = image. getWidth();
            int imageHeight = image. getHeight();
            // read RGB from image
            int[] imagePoints = new int[imageWidth * imageHeight];
            imagePoints = image.getRGB(0, 0, imageWidth, imageHeight, imagePoints, 0, imageWidth);
            // landscape
            if (jointType. position == 0) {<!-- -->
                jointImage.setRGB(xIndex, 0, imageWidth, imageHeight, imagePoints, 0, imageWidth);
                xIndex += imageWidth;
            }
            // vertical
            else if (jointType. position == 1) {<!-- -->
                jointImage.setRGB(0, yIndex, imageWidth, imageHeight, imagePoints, 0, imageWidth);
                yIndex += imageHeight;
            }
        }
        return jointImage;
    }

    /**
     * Binarize the image and return it in the form of 0, 1
     * @param imagePath image path
     * @return byte[column][row]{0, 1}
     */
    public static byte[][] binaryImageToZero(String imagePath) {<!-- -->
        BufferedImage image = readImage(imagePath);
        return binaryImageToZero(image);
    }

    /**
     * Binarize the image and return it in the form of 0, 1
     * @param image image object
     * @return byte[column][row]{0, 1}
     */
    public static byte[][] binaryImageToZero(BufferedImage image) {<!-- -->
        BufferedImage binaryImage = binaryImage(image);
        if (binaryImage == null) {<!-- -->
            return null;
        }
        int width = binaryImage.getWidth();
        int height = binaryImage. getHeight();
        byte[][] imageByte = new byte[height][width];
        Integer color = null;
        for (int y = 0; y < height; y ++ ) {<!-- -->
            for (int x = 0; x < width; x ++ ) {<!-- -->
                int rgb = binaryImage.getRGB(x, y);
                if (color == null) {<!-- -->
                    color = rgb;
                }
                imageByte[y][x] = color == rgb ? (byte)0 : 1;
            }
        }
        return imageByte;
    }

    /**
     * Scale the image proportionally
     * @param imagePath image path
     * @param scale zoom ratio
     * @return zoomed image
     */
    public static BufferedImage scaleZoomImage(String imagePath, double scale) {<!-- -->
        BufferedImage image = readImage(imagePath);
        return scaleZoomImage(image, scale);
    }

    /**
     * Scale the image proportionally
     * @param image image object
     * @param scale zoom ratio
     * @return zoomed image
     */
    public static BufferedImage scaleZoomImage(BufferedImage image, double scale) {<!-- -->
        if (image == null) {<!-- -->
            return null;
        }
        int imageWidth = (int)(image. getWidth() * scale);
        int imageHeight = (int)(image. getHeight() * scale);
        return zoomImage(image, imageWidth, imageHeight);
    }

    /**
     * Image scaling
     * @param imagePath image path
     * @param width Scaled width (set to -1 means use high proportional scaling)
     * @param height Scaled height (set to -1 means use proportional scaling of width)
     * @return zoomed image
     */
    public static BufferedImage zoomImage(String imagePath, int width, int height) {<!-- -->
        BufferedImage image = readImage(imagePath);
        return zoomImage(image, width, height);
    }

    /**
     * Image scaling
     * @param image image object
     * @param width Scaled width (set to -1 means use high proportional scaling)
     * @param height Scaled height (set to -1 means use proportional scaling of width)
     * @return zoomed image
     */
    public static BufferedImage zoomImage(BufferedImage image, int width, int height) {<!-- -->
        if (image == null) {<!-- -->
            return null;
        }
        if (image.getWidth() == -1 & amp; & amp; image.getHeight() == -1) {<!-- -->
            return image;
        }
        if (width == -1) {<!-- -->
            width = (height / image.getHeight()) * image.getWidth();
        }
        if (height == -1) {<!-- -->
            height = (width / image.getWidth()) * image.getHeight();
        }
        if(image.getWidth() == width & amp; & amp; image.getHeight() == height) {<!-- -->
            return image;
        }
        BufferedImage zoomImage = new BufferedImage(width, height, image.getType());
        Graphics g = zoomImage. getGraphics();
        g. drawImage(image, 0, 0, width, height, null);
        g. dispose();
        return zoomImage;
    }

    /**
     * Image fusion (the fused image will use the size of the base map)
     * @param baseImagePath basemap image path
     * @param coverImagePath Overlay image path
     * @param alpha The transparency of the overlay image (0-1, the smaller the more transparent)
     * @return fused image object
     */
    public static BufferedImage overlay(String baseImagePath, String coverImagePath, double alpha) {<!-- -->
        BufferedImage baseImage = readImage(baseImagePath);
        BufferedImage coverImage = readImage(coverImagePath);
        return overlay(baseImage, coverImage, alpha);
    }

    /**
     * Image fusion (the overlay image will use the size of the base image)
     * @param baseImage basemap image object
     * @param coverImage superimposed picture object
     * @param alpha The transparency of the overlay image (0-1, the smaller the more transparent)
     * @return fused image object
     */
    public static BufferedImage overlay(BufferedImage baseImage, BufferedImage coverImage, double alpha) {<!-- -->
        if (baseImage == null) {<!-- -->
            return coverImage;
        }
        if (coverImage == null) {<!-- -->
            return baseImage;
        }
        if (alpha > 1 || alpha < 0) {<!-- -->
            alpha = 0.5;
        }
        int width = baseImage. getWidth();
        int height = baseImage. getHeight();
        coverImage = zoomImage(coverImage, width, height);
        BufferedImage overlayImage = new BufferedImage(width, height, baseImage.getType());
        int[] baseImagePoints = new int[width * height];
        baseImagePoints = baseImage.getRGB(0,0, width, height, baseImagePoints, 0, width);
        overlayImage.setRGB(0, 0, width, height, baseImagePoints, 0, width);
        Graphics2D graphics = overlayImage. createGraphics();
        graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, (float) alpha));
        graphics. drawImage(coverImage, 0, 0, width, height, null);
        graphics. dispose();
        return overlayImage;
    }

/**
     * mirror image
     * @param imagePath image path
     * @return mirror image object
     */
    public static BufferedImage mirrorImage(String imagePath) {<!-- -->
        BufferedImage image = readImage(imagePath);
        return mirrorImage(image);
    }

    /**
     * mirror image
     * @param image original image object
     * @return mirror image object
     */
    public static BufferedImage mirrorImage(BufferedImage image) {<!-- -->
        if (image == null) {<!-- -->
            return null;
        }
        int width = image. getWidth();
        int height = image. getHeight();
        BufferedImage mirrorImage = new BufferedImage(width, height, image.getType());
        for (int x = 0; x < width; x ++ ) {<!-- -->
            for (int y = 0; y < height; y ++ ) {<!-- -->
                int rgb = image.getRGB(x, y);
                mirrorImage.setRGB(width - x - 1, y, rgb);
            }
        }
        return mirrorImage;
    }

    /**
     * Add watermark to pictures
     * @param filePath original image path
     * @param content watermark content
     * @param coord coordinates [x, y] (the default is the center)
     * @return the watermarked image
     */
    public static BufferedImage watermarkImage(String filePath, String content, int[] coord) {<!-- -->
        BufferedImage image = readImage(filePath);
        return watermarkImage(image, content, coord);
    }
    
    /**
     * Add watermark to pictures
     * @param image original image object
     * @param content watermark content
     * @param coord coordinates [x, y] (the default is the center)
     * @return the watermarked image
     */
    public static BufferedImage watermarkImage(BufferedImage image, String content, int[] coord) {<!-- -->
        if (image == null) {<!-- -->
            return null;
        }
        int width = image. getWidth();
        int height = image. getHeight();
        BufferedImage watermarkImage = new BufferedImage(width, height, image.getType());
        Graphics2D graphics = watermarkImage. createGraphics();
        //srcImg is the image object of the original image obtained above
        graphics. drawImage(image, 0, 0, width, height, null);
        //Set the watermark color according to the background of the picture
        graphics.setColor(new Color(255,255,255,128));
        //Set the font. The brush font style is Microsoft Yahei, bold, and the text size is 60pt
        graphics.setFont(new Font("Microsoft Yahei", Font.BOLD, 40));
        //Set the coordinates of the watermark
        if (coord == null || coord.length == 0) {<!-- -->
            int contentLength = graphics.getFontMetrics(graphics.getFont()).charsWidth(content.toCharArray(), 0, content.length());
            int x = (width - contentLength) / 2;
            int y = height / 2;
            coord = new int[]{<!-- -->x, y};
        }
        if (coord. length == 1) {<!-- -->
            coord = new int[]{<!-- -->coord[0], coord[0]};
        }
        graphics. drawString(content, coord[0], coord[1]);
        graphics. dispose();
        return watermarkImage;
    }

    /**
     * Image rotation (png does not support transparent color, do not save by png!!!)
     * @param filePath original image path
     * @param angle rotation angle
     * @return rotated image object (png does not support transparent color, do not save by png!!!)
     */
    public static BufferedImage rotateImage(String filePath, double angle) {<!-- -->
        BufferedImage image = readImage(filePath);
        return rotateImage(image, angle);
    }

    /**
     * Image rotation (png does not support transparent color, do not save by png!!!)
     * @param image original image object
     * @param angle rotation angle
     * @return rotated image object (png does not support transparent color, do not save by png!!!)
     */
    public static BufferedImage rotateImage(BufferedImage image, double angle) {<!-- -->
        if (image == null) {<!-- -->
            return null;
        }
        if (angle >= 360) {<!-- -->
            angle = angle % 360;
        }
        if (angle == 0) {<!-- -->
            return image;
        }
        // Set the rotation angle (in radians)
        angle = Math. toRadians(angle);
        // Get the size of the image
        int width = image. getWidth();
        int height = image. getHeight();
        // Calculate the size of the rotated image
        double rotatedWidth = Math.abs(Math.cos(angle)) * width + Math.abs(Math.sin(angle)) * width;
        double rotatedHeight = Math.abs(Math.sin(angle)) * height + Math.abs(Math.cos(angle)) * height;
        // Create a new image with the size of the rotated image and set the transparent color
        BufferedImage rotatedImage = new BufferedImage((int) rotatedWidth, (int) rotatedHeight, BufferedImage.TYPE_INT_ARGB_PRE);
        Graphics2D graphics = rotatedImage. createGraphics();
        // Set the rotation center as the image center
        graphics.translate(rotatedWidth / 2, rotatedHeight / 2);
        // perform rotation
        graphics. rotate(angle);
        // draw the original image onto the rotated image
        graphics.drawImage(image, -width / 2, -height / 2, null);
        graphics. dispose();
        return rotatedImage;
    }
    
    /**
     * Write the picture
     * @param image image object
     * @param filePath write path
     * @return Whether to write successfully
     */
    public static b
    oolean writeImage(BufferedImage image, String filePath) {<!-- -->
        if (image == null || filePath == null || filePath.isEmpty()) {<!-- -->
            return false;
        }
        File file = new File(filePath);
        if(!file.getParentFile().exists()) {<!-- -->
            boolean mkdirs = file.getParentFile().mkdirs();
            if (!mkdirs) {<!-- -->
                return false;
            }
        }
        String suffix = file. getName(). substring(file. getName(). indexOf(".") + 1);
        try {<!-- -->
            return ImageIO.write(image, suffix, file);
        } catch (Exception e) {<!-- -->
            e.printStackTrace();
        }
        return false;
    }

    private static class GrayType {<!-- -->

        private final int type;

        private double redRatio;

        private double greenRatio;

        private double blueRatio;

        private GrayType(int type) {<!-- -->
            this.type = type;
        }

        private GrayType(int type, double redRatio, double greenRatio, double blueRatio) {<!-- -->
            this.type = type;
            this.redRatio = redRatio;
            this.greenRatio = greenRatio;
            this. blueRatio = blueRatio;
        }

        private double getRedRatio() {<!-- -->
            return redRatio / (redRatio + greenRatio + blueRatio);
        }

        private double getBlueRatio() {<!-- -->
            return blueRatio / (redRatio + greenRatio + blueRatio);
        }

        private double getGreenRatio() {<!-- -->
            return greenRatio / (redRatio + greenRatio + blueRatio);
        }

    }

    private static class JointType {<!-- -->
        /**
         * Merge method: 0 horizontal merge; 1: vertical merge
         */
        private final int position;
        /**
         * Adaptive mode: 0: disable adaptive, 1: use the largest size, 2: use the smallest size
         */
        private final int adaptiveType;
        private JointType(int position, int adaptiveType) {<!-- -->
            this.position = position;
            this.adaptiveType = adaptiveType;
        }
    }

}