Directorio de artículos
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
ImageObserver
interfaz para recibir notificaciones sobre la carga y el dibujo de imágenes. La interfaz se puede personalizarImageObserver
y 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.BufferedImage
es 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 originalpará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,RasterFormatException
se 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.ImageIO
la clase proporciona un conjunto de métodos estáticos para leer, escribir y procesar imágenes, y el método write() se usa paraBufferedImage
escribir el objeto en el archivo de destinoparámetros :
BufferedImage
RenderedImage: objeto que se escribirá en el archivo de destinoformatName: 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 elBufferedImage
objeto 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 genereuna 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 插值 |
基于样条函数进行插值,包括自然边界、非自然边界、周期性等多种类型 | 具有平滑效果较好、插值精度较高的优点,但计算复杂度较高 | 图像缩放、图像修复等需要保持连续性和平滑性的场景 |