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; } } }