Java implementa el escalado y el recorte de imágenes, el cambio de proporción de píxeles de imagen y la conversión por lotes de proporciones de píxeles de imagen

descripción general

Muchas veces, la relación de tamaño (relación de aspecto) de las imágenes que obtenemos de diferentes lugares puede ser varios parámetros, y queremos cambiar a la relación que necesitamos, como: 9:16/16/9 Este tamaño, en este momento , se encuentra que es muy problemático usar herramientas y necesita ser procesado uno por uno, por lo que escribir un programa puede realizar el procesamiento por lotes.
Efecto : * Escale la imagen de acuerdo con el ancho y la altura de entrada, y luego recórtela desde el centro para generar un tamaño de imagen que satisfaga sus necesidades

implementación del código anterior

package com.xgf.file;

import com.xgf.common.LogUtil;
import com.xgf.constant.StringConstantUtil;
import com.xgf.constant.enumclass.FileTypeEnum;
import com.xgf.system.SystemUtil;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author strive_day
 * @create 2023-04-08 1:00
 * @description 图片文件工具类
 */
public class ImageFileUtil {
    
    


    public static void main(String[] args) {
    
    
    	// 处理目录下的所有jpg格式文件,裁剪图片尺寸变为3840:2160
        String sourceImgDir = "F:\\wqq";
        String targetImgDir = "F:\\wqq\\11";
        Arrays.stream(new File(sourceImgDir).listFiles()).filter(p -> p.getName().endsWith(".jpg"))
                .forEach(p -> {
    
    
                    try {
    
    
                        imgDpiModify(p, new File(StringConstantUtil.defaultEndWith(targetImgDir, SystemUtil.getFileSeparator()) + p.getName()), 3840, 2160, Boolean.TRUE);
                    } catch (Exception e) {
    
    
                        e.printStackTrace();
                    }
                });
    }


    public static void imgDpiModify(String sourceImgPath, String targetImgPath, Integer targetWidth, Integer targetHeight, Boolean dealOptimizeFlag) throws IOException {
    
    
        imgDpiModify(new File(sourceImgPath), new File(targetImgPath), targetWidth, targetHeight, dealOptimizeFlag);
    }

    /**
     * 图片按照中心点进行缩放和裁剪,达到需要的图片宽高像素比
     *
     * @param sourceImgFile 原图片文件
     * @param targetImgFile 目标图片文件
     * @param targetWidth 目标图片像素宽度
     * @param targetHeight 目标图片像素高度
     * @return true: 成功,false: 失败
     * @param dealOptimizeFlag 处理优化标识,true:处理优化,如果原图片宽高宽高比大,则自动替换宽和高的值
     *                         eg: 图片宽度: 1000,高度: 800,传入参数: targetWidth: 3840, targetHeight: 2160,那么会优化成:targetWidth: 2160, targetHeight: 3840,保证最佳展示
     * @throws IOException IO异常
     */
    public static boolean imgDpiModify(File sourceImgFile, File targetImgFile, Integer targetWidth, Integer targetHeight, Boolean dealOptimizeFlag) throws IOException {
    
    

        BufferedImage sourceBufImg = ImageIO.read(sourceImgFile);
        int originalWidth = sourceBufImg.getWidth();
        int originalHeight = sourceBufImg.getHeight();

        // 优化宽高比
        if (Boolean.TRUE.equals(dealOptimizeFlag)) {
    
    
            // 原图片宽度比高度大,那么应该满足: targetWidth >= targetHeight,如果不满足,则替换,反之,原图片宽度比高度小,那么应该满足: targetWidth <= targetHeight,否则替换
            if ((originalWidth > originalHeight && targetWidth < targetHeight)
                    || (originalWidth < originalHeight && targetWidth > targetHeight)) {
    
    
                Integer temp = targetWidth;
                targetWidth = targetHeight;
                targetHeight = temp;
            }
        }


        // 计算图片需要的缩放比例(宽高和原宽高做对比,取大值)【就是满足目标宽度和高度,需要对图片进行的缩放比】
        double imgScaleValue = Math.max((double) targetWidth / originalWidth, (double) targetHeight / originalHeight);
        // 缩放后的图片宽度
        int scaledImgWidth = (int) Math.round(originalWidth * imgScaleValue);
        // 缩放后的图片高度
        int scaledImgHeight = (int) Math.round(originalHeight * imgScaleValue);

        // 取中间区域的左上角坐标x,y开始截图下标【缩放后变更为要求的图片比例尺寸,进行截取的开始坐标】
        int xStartIndex = (scaledImgWidth - targetWidth) / 2;
        int yStartIndex = (scaledImgHeight - targetHeight) / 2;

        // 对图像进行缩放,获取缩放后的图片BufferedImage
        BufferedImage scaleBufImg = ImageScaleUtil.scale(sourceBufImg, scaledImgWidth, scaledImgHeight);

        // 对缩放后的图片,从中间区域开始截取(用于获取原始图像中指定区域的子图像),达到希望得到的目标图片的宽高比例
        // 参数分别是:截取图像左上角的 x 坐标、y 坐标, 截取图像的宽度、高度
        BufferedImage croppedTargetBufImg = scaleBufImg.getSubimage(xStartIndex, yStartIndex, targetWidth, targetHeight);

        // 将截取好的目标图像写入到目标文件中,参数1 (RenderedImage): 要写入文件或输出流的图像数据。
        // 参数2 (formatName): 指定要写入的图像格式的名称,如 jpg、png、bmp、gif,(这里默认使用JPG),告知 ImageIO 使用哪个图像编解码器来处理图像数据
        // 参数3 (output): 要写入的文件或输出流。可以是一个 File 对象或 OutputStream 对象
        boolean writeFlag = ImageIO.write(croppedTargetBufImg, FileTypeEnum.JPG.getCode(), targetImgFile);

        LogUtil.info("====== sourcePath = {}, targetPath = {}, result success = {}", sourceImgFile.getPath(), targetImgFile.getPath(), writeFlag);

        return writeFlag;
    }

    /**
     * 图片缩放工具类
     */
    public static class ImageScaleUtil {
    
    

        /**
         * 定义默认的最优插值算法映射表(根据图像类型自动选择最优插值算法,以获得最佳效果)
         *
         * 主要是三种插值算法:
         * 1. RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR(最近邻插值算法),特点: 像素值直接采用原来最近的像素值,优势和场景: 速度快,适用于图像放大倍数不超过2倍的情况
         * 2. RenderingHints.VALUE_INTERPOLATION_BILINEAR(双线性插值算法),特点:根据周围4个像素计算新像素值, 优势和场景:速度快,适用于图像放大倍数不超过2倍的情况
         * 3. RenderingHints.VALUE_INTERPOLATION_BICUBIC(双三次插值算法), 特点:根据周围16个像素计算新像素值,图像质量高,适用于图像放大倍数较大的情况
         */
        private static final Map<Integer, Object> DEFAULT_INTERPOLATION_MAP = new HashMap<>();

        static {
    
    
            // 将 BufferedImage.TYPE_BYTE_GRAY(表示一个 8 位灰度图像,每个像素值存储在 8 位无符号字节中) 类型的图像对应的最优插值算法设置为 NEAREST_NEIGHBOR(最近邻插值)
            DEFAULT_INTERPOLATION_MAP.put(BufferedImage.TYPE_BYTE_GRAY, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
            // 将 BufferedImage.TYPE_USHORT_GRAY(表示一个 16 位灰度图像,每个像素值存储在 16 位无符号 short 类型中) 类型的图像对应的最优插值算法设置为 NEAREST_NEIGHBOR(最近邻插值)
            DEFAULT_INTERPOLATION_MAP.put(BufferedImage.TYPE_USHORT_GRAY, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
            // 将 BufferedImage.TYPE_3BYTE_BGR(表示一个 8 位 BGR 颜色分量的图像,颜色存储在 24 位字节数组中) 类型的图像对应的最优插值算法设置为 BILINEAR(双线性插值)
            DEFAULT_INTERPOLATION_MAP.put(BufferedImage.TYPE_3BYTE_BGR, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            // 将 BufferedImage.TYPE_4BYTE_ABGR(表示一个 8 位 ABGR 颜色分量的图像,颜色存储在 32 位字节数组中) 类型的图像对应的最优插值算法设置为 BILINEAR(双线性插值)
            DEFAULT_INTERPOLATION_MAP.put(BufferedImage.TYPE_4BYTE_ABGR, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            // 将 BufferedImage.TYPE_INT_ARGB(表示一个 8 位 RGBA 颜色分量的图像,颜色存储在 32 位整数中) 类型的图像对应的最优插值算法设置为 BICUBIC(双三次插值)
            DEFAULT_INTERPOLATION_MAP.put(BufferedImage.TYPE_INT_ARGB, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            // 将 BufferedImage.TYPE_INT_RGB(表示一个 8 位 RGB 颜色分量的图像,颜色存储在 32 位整数中) 类型的图像对应的最优插值算法设置为 BICUBIC(双三次插值)
            DEFAULT_INTERPOLATION_MAP.put(BufferedImage.TYPE_INT_RGB, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            // 将 BufferedImage.TYPE_CUSTOM(表示一个自定义类型的图像,其颜色模型由开发人员指定) 类型的图像对应的最优插值算法设置为 BICUBIC(双三次插值)
            DEFAULT_INTERPOLATION_MAP.put(BufferedImage.TYPE_CUSTOM, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        }

        /**
         * 对图像进行最优缩放,返回缩放后的图片
         *
         * @param sourceImg 原始图像 BufferedImage
         * @param width 缩放后的宽度
         * @param height 缩放后的高度
         * @return 缩放后的图像 BufferedImage
         */
        public static BufferedImage scale(BufferedImage sourceImg, int width, int height) {
    
    
            // 根据图像类型选择最优插值算法,默认为 RenderingHints.VALUE_INTERPOLATION_BICUBICC(双三次插值)
            RenderingHints renderingHints = new RenderingHints(RenderingHints.KEY_INTERPOLATION,
                    DEFAULT_INTERPOLATION_MAP.getOrDefault(sourceImg.getType(), RenderingHints.VALUE_INTERPOLATION_BICUBIC));

            // 创建 BufferedImage,对图片宽高进行缩放,并使用原始图像相同的颜色模型,参数分别为:图像宽度、图像高度、图像的颜色模型类型(这里使用原图像类型)
            BufferedImage scaledImage = new BufferedImage(width, height, sourceImg.getType());

            // 创建缩放图像上进行绘制操作的 Graphics2D 对象(图形上下文)
            Graphics2D graphics2D = scaledImage.createGraphics();
            try {
    
    
                // 设置 最优插值算法
                graphics2D.setRenderingHints(renderingHints);
                // 缩放裁剪图片,将指定的图像绘制在Graphics2D对象的当前坐标系中,在目标矩形区域内进行缩放和裁剪。
                // 就是:如果目标矩形区域和源图像的宽高比不同,则源图像将按照目标区域的宽高比例进行缩放。如果目标矩形区域超出了源图像的边界,则会进行裁剪
                // observer为null,表示使用默认的图像观察者(java.awt.image.ImageObserver)
                graphics2D.drawImage(sourceImg, 0, 0, width, height, null);
            } finally {
    
    
                // 释放 Graphics2D 资源
                graphics2D.dispose();
            }

            // 返回缩放完成后的图片内容
            return scaledImage;
        }
    }

}

El principal método de uso.

1. El método drawImage de la clase Graphics2D

public abstract boolean drawImage(Image img, int x, int y,
                                   int width, int height,
                                   ImageObserver observer);

Significado del método :
hacer zoom y recortar la imagen: dibuje la imagen especificada en el sistema de coordenadas actual del objeto Graphics2D, y haga zoom y recorte en el área rectangular de destino. Por ejemplo:
si la relación de aspecto del área rectangular de destino y la imagen de origen son diferentes , la fuente La imagen se escalará de acuerdo con la relación de aspecto del área de destino. Si el área rectangular de destino supera los límites de la imagen de origen, se recortará

Parámetros :

  • img : el objeto de imagen para dibujar
  • x : la coordenada x de la esquina superior izquierda del área rectangular de destino
  • y : la coordenada y de la esquina superior izquierda del área rectangular de destino
  • ancho : el ancho del área rectangular de destino
  • altura : la altura del área rectangular objetivo
  • observador : es una ImageObserverinterfaz para recibir notificaciones sobre la carga y el dibujo de imágenes. La interfaz se puede personalizar ImageObservery el objeto será notificado del progreso de carga y dibujo de la imagen. Si es nulo, significa usar el observador de imágenes predeterminado (java.awt.image.ImageObserver se implementa de forma predeterminada)

2. El método getSubimage de la clase BufferedImage

public BufferedImage getSubimage (int x, int y, int w, int h) {
     
     
     return new BufferedImage (colorModel,
                               raster.createWritableChild(x, y, w, h,
                                                          0, 0, null),
                               colorModel.isAlphaPremultiplied(),
                               properties);
 }

Significado del método :
java.awt.image.BufferedImage.BufferedImagees una imagen con un búfer de datos de imagen accesible o modificable. El método getSubimage() se usa para obtener la subimagen del área especificada en la imagen original

parámetro:

  • xStartIndex : la coordenada x de la esquina superior izquierda de la subimagen
  • yStartIndex : la coordenada y de la esquina superior izquierda de la subimagen
  • targetWidth : el ancho de la subimagen
  • targetHeight : la altura de la subimagen

[Nota] Cuando utilice getSubimage()el método, asegúrese de que el área de la subimagen especificada no exceda el límite de la imagen original. De lo contrario, RasterFormatExceptionse lanzará una excepción.

3. El método de escritura de la clase ImageIO

public static boolean write(RenderedImage im,
                             String formatName,
                             File output) throws IOException {}

Significado del método :
javax.imageio.ImageIOla clase proporciona un conjunto de métodos estáticos para leer, escribir y procesar imágenes, y el método write() se usa para BufferedImageescribir el objeto en el archivo de destino

parámetros :

  • BufferedImageRenderedImage: objeto que se escribirá en el archivo de destino

  • formatName: formato de archivo de destino, como jpg (jpeg), png, bmp, gif

  • salida: información de la ruta del archivo de destino

    ImageIO.write()[Nota] Al usar el método, debe asegurarse de que el BufferedImageobjeto especificado se haya cargado completamente en la memoria y que la ruta del archivo de destino ya exista y se pueda escribir . Esto puede hacer que el método genere una excepción si la imagen es demasiado grande o si no se puede escribir en la ruta del archivo de destino.

El valor de formatName, formatos de imagen de uso común y escenarios de uso: si desea reducir el tamaño del archivo de imagen tanto como sea posible mientras garantiza la calidad de la imagen, puede considerar usar

  • jpg : el formato jpg/jpeg puede controlar el tamaño y la calidad del archivo de imagen ajustando la relación de compresión, pero habrá problemas de distorsión con una relación de compresión alta
  • png : el formato PNG admite canales de transparencia y el tamaño del archivo comprimido es relativamente pequeño, pero la velocidad de lectura y escritura puede ser lenta. Es adecuado para escenas que necesitan admitir fondos transparentes o necesitan guardar archivos de imagen muy pequeños.
  • bmp : el formato BMP no tiene compresión para datos de imagen, por lo que se puede conservar la máxima calidad de imagen, pero el tamaño del archivo es relativamente grande. Si desea guardar imágenes de mapa de bits, puede considerar usar el formato BMP.
  • gif : el formato GIF admite animación de varios cuadros y fondo transparente, y el tamaño del archivo es relativamente pequeño, pero solo admite 256 colores, lo que no es adecuado para imágenes complejas. Si desea guardar animaciones o elementos gráficos simples, puede considerar usar el formato GIF.

Selección de algoritmo de interpolación de escala de imagen

constante de tipo de imagen Tipo de imagen Constante Descripción constante de algoritmo de interpolación óptima Nombre del algoritmo de interpolación
Imagen Buferada.TYPE_BYTE_GRAY Una imagen en escala de grises de 8 bits, con cada valor de píxel almacenado en bytes sin firmar de 8 bits RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR interpolación del vecino más cercano
Imagen Buferada.TYPE_USHORT_GRAY Una imagen en escala de grises de 16 bits, con cada valor de píxel almacenado en un formato corto sin firmar de 16 bits RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR
Imagen Buferada.TYPE_3BYTE_BGR Una imagen con componentes de color BGR de 8 bits, los colores se almacenan en una matriz de bytes de 24 bits Sugerencias de representación.VALUE_INTERPOLATION_BILINEAR interpolación bilineal
Imagen Buferada.TYPE_4BYTE_ABGR Una imagen con componentes de color ABGR de 8 bits, los colores se almacenan en una matriz de bytes de 32 bits Sugerencias de representación.VALUE_INTERPOLATION_BILINEAR
Imagen Buferada.TYPE_INT_ARGB Una imagen con componentes de color RGBA de 8 bits, los colores se almacenan en números enteros de 32 bits Sugerencias de representación.VALUE_INTERPOLATION_BICUBIC interpolación bicúbica
Imagen Buferada.TYPE_INT_RGB Una imagen con componentes de color RGB de 8 bits, los colores se almacenan en números enteros de 32 bits Sugerencias de representación.VALUE_INTERPOLATION_BICUBIC
Imagen Buferada.TYPE_CUSTOM Una imagen de un tipo personalizado cuyo modelo de color es especificado por el desarrollador Sugerencias de representación.VALUE_INTERPOLATION_BICUBIC

Algoritmos de interpolación comunes y sus ventajas y desventajas

algoritmo de interpolación Descripción del algoritmo Ventajas y desventajas Principales escenarios de aplicación

Interpolación del vecino más cercano
El píxel adopta directamente el valor del píxel original más cercano El cálculo es simple y rápido, pero es propenso a efectos irregulares y con alias. Escenarios como la reducción de imágenes y la computación en tiempo real

Interpolación bilineal
Realizar una interpolación lineal en las direcciones horizontal y vertical La calidad de la imagen es alta, pero la complejidad del cálculo es alta y el efecto de suavizado puede no ser lo suficientemente bueno Escalado de imágenes, rotación de imágenes y otros escenarios

Interpolación bicúbica
Realice la interpolación de funciones cúbicas horizontal y verticalmente, utilizando los 16 píxeles circundantes para calcular nuevos valores de píxeles La calidad de la imagen es la más alta , lo que puede resolver eficazmente los problemas de distorsión y cambios de nitidez cuando se escala la imagen, pero la complejidad computacional es la más alta, evitando artefactos irregulares en la imagen. Ampliación de imagen, reparación de imagen y otras escenas que requieren alta calidad de imagen
Interpolación de Lanczos
(remuestreo de Lanczos)
Método de interpolación spline basado en la ventana y el truncamiento de la función Sinc 图像质量较高,能有效地处理图像拉伸、缩小等过程中出现的失真和锐度变化的问题,但计算复杂度较高 图像缩放、图像旋转等需要考虑图像质量的场景
立方卷积插值
(Cubic Convolution Interpolation
使用立方体函数进行插值 计算速度较快,能有效地处理图像缩放时出现的失真和锐度变化问题,但图像质量可能稍逊于双三次插值 图像缩放、图像修复等对图像质量要求不太高的场景
Spline 插值
基于样条函数进行插值,包括自然边界、非自然边界、周期性等多种类型 具有平滑效果较好、插值精度较高的优点,但计算复杂度较高 图像缩放、图像修复等需要保持连续性和平滑性的场景

Supongo que te gusta

Origin blog.csdn.net/qq_40542534/article/details/130041026
Recomendado
Clasificación