package org.sxkj.common.utils; import it.geosolutions.imageio.plugins.tiff.TIFFImageWriteParam; import org.apache.commons.imaging.ImageFormats; import org.apache.commons.imaging.ImageReadException; import org.apache.commons.imaging.ImageWriteException; import org.apache.commons.imaging.Imaging; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.plugins.jpeg.JPEGImageWriteParam; import javax.imageio.stream.ImageOutputStream; import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * tif 图片压缩工具类 * @author zhongrj * @date 2025-05-20 */ public class TifCompressorUtils { /** * tif 图片压缩 * @param inputFile * @param targetSizeKB * @param targetName 目标名称 "_samll.tif" * @return * @throws IOException */ public static File compressTif(File inputFile, int targetSizeKB,String targetName) throws IOException { // 读取原始图像并保留原始类型 BufferedImage originalImage = null; try (InputStream is = new BufferedInputStream(new FileInputStream(inputFile))) { originalImage = Imaging.getBufferedImage(is); } catch (ImageReadException e) { e.printStackTrace(); } // 创建与原始图像类型相同的新图像 BufferedImage image = new BufferedImage( originalImage.getWidth(), originalImage.getHeight(), originalImage.getType() == 0 ? BufferedImage.TYPE_INT_RGB : originalImage.getType() ); // 绘制原始图像到新图像,填充白色背景 Graphics2D g = image.createGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.drawImage(originalImage, 0, 0, null); g.dispose(); // 计算压缩参数 long targetSizeBytes = targetSizeKB * 1024L; long originalSizeBytes = inputFile.length(); double compressionRatio = (double) targetSizeBytes / originalSizeBytes; File outputFile = new File(inputFile.getParent(), inputFile.getName().replace(".tif", targetName)); int attempts = 0; int maxAttempts = 5; float quality = 0.8f; do { // 调整尺寸 int targetWidth = (int) (image.getWidth() * Math.sqrt(compressionRatio)); int targetHeight = (int) (image.getHeight() * Math.sqrt(compressionRatio)); // 创建压缩图像并确保白色背景 BufferedImage compressedImage = new BufferedImage( targetWidth, targetHeight, image.getType() == 0 ? BufferedImage.TYPE_INT_RGB : image.getType() ); Graphics2D g2 = compressedImage.createGraphics(); g2.setColor(Color.WHITE); g2.fillRect(0, 0, targetWidth, targetHeight); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.drawImage(image, 0, 0, targetWidth, targetHeight, null); g2.dispose(); // 保存为TIF saveAsTiff(compressedImage, outputFile, quality); // 检查文件大小 long compressedSize = outputFile.length(); if (compressedSize <= targetSizeBytes || attempts >= maxAttempts) { break; } quality *= 0.7f; compressionRatio *= 0.8f; attempts++; } while (true); return outputFile; } /** * 将图像保存为TIF格式 */ private static void saveAsTiff(BufferedImage image, File outputFile, float quality) throws IOException { ImageWriter writer = ImageIO.getImageWritersByFormatName("TIFF").next(); try (ImageOutputStream ios = ImageIO.createImageOutputStream(outputFile)) { writer.setOutput(ios); TIFFImageWriteParam param = new TIFFImageWriteParam(null); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionType("LZW"); writer.write(null, new IIOImage(image, null, null), param); } } /** * 两阶段转换:先压缩TIF,再转JPEG * @param inputFile 原始TIF文件 * @param targetSizeKB 目标大小(KB) * @param tifSuffix TIF输出后缀(如 "_compressed.tif") * @param jpegSuffix JPEG输出后缀(如 "_final.jpg") * @return 最终的JPEG文件 * @throws IOException */ public static File convertTifToJpeg(File inputFile, int targetSizeKB, String tifSuffix, String jpegSuffix) throws IOException { // 第一阶段:压缩TIF File compressedTif = compressTif(inputFile, targetSizeKB, tifSuffix); // 第二阶段:TIF转JPEG return convertTifToJpeg(compressedTif, jpegSuffix); } /** * 将TIF转换为JPEG(新增方法) * @param tifFile 输入的TIF文件 * @param jpegSuffix JPEG文件后缀(如 "_converted.jpg") * @return 生成的JPEG文件 */ public static File convertTifToJpeg(File tifFile, String jpegSuffix) throws IOException { // 读取TIF图像 BufferedImage tifImage; try (InputStream is = new BufferedInputStream(new FileInputStream(tifFile))) { tifImage = Imaging.getBufferedImage(is); } catch (ImageReadException e) { throw new IOException("Failed to read TIFF image", e); } // 创建输出文件路径 File jpegFile = new File(tifFile.getParent(), tifFile.getName().replace(".tif", jpegSuffix)); // 配置JPEG编码参数 float jpegQuality = 0.95f; // 默认质量(0.7-0.9之间最佳) saveAsJpeg(tifImage, jpegFile, jpegQuality); return jpegFile; } /** * 优化后的JPEG保存方法 */ private static void saveAsJpeg(BufferedImage image, File outputFile, float quality) throws IOException { // 确保图像是RGB格式(JPEG不支持透明通道) BufferedImage rgbImage = ensureRgbFormat(image); // 获取JPEG编码器 Iterator writers = ImageIO.getImageWritersByFormatName("jpeg"); if (!writers.hasNext()) { throw new IllegalStateException("No JPEG encoder available"); } ImageWriter writer = writers.next(); try (ImageOutputStream ios = ImageIO.createImageOutputStream(outputFile)) { writer.setOutput(ios); // 配置压缩参数 JPEGImageWriteParam jpegParams = new JPEGImageWriteParam(null); jpegParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); jpegParams.setCompressionQuality(quality); // 高级设置(优化压缩) jpegParams.setOptimizeHuffmanTables(true); // 启用Huffman优化 jpegParams.setProgressiveMode(ImageWriteParam.MODE_DISABLED); // 非渐进式 // 执行编码 writer.write(null, new IIOImage(rgbImage, null, null), jpegParams); } finally { writer.dispose(); } } /** * 确保图像为RGB格式(处理可能的透明度问题) */ private static BufferedImage ensureRgbFormat(BufferedImage image) { if (image.getType() == BufferedImage.TYPE_INT_RGB) { return image; } // 转换格式并填充白色背景 BufferedImage rgbImage = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D g = rgbImage.createGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, rgbImage.getWidth(), rgbImage.getHeight()); g.drawImage(image, 0, 0, null); g.dispose(); return rgbImage; } /** * 将图像保存为TIF格式 */ private static void saveAsTiffByImaging(BufferedImage image, File outputFile, float quality) throws IOException { // 设置TIF压缩参数 Map params = new HashMap<>(); params.put("compression", "LZW"); // 或 "Deflate", "PackBits" params.put("quality", quality); // 保留原始元数据(如果存在) try (OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile))) { Imaging.writeImage(image, os, ImageFormats.TIFF); } catch (ImageWriteException e) { e.printStackTrace(); } } /** * 获取图像的内存大小估算值 */ private static long getImageSize(BufferedImage image) { return (long) image.getWidth() * image.getHeight() * 3; // 假设RGB 3字节/像素 } }