欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

springboot 使用 GDAL 获取 tif 文件缩略图并转换为 base64

最编程 2024-10-18 10:43:45
...
package com.geofly.dataservicecenter.api.common.util; import java.awt.*; import org.gdal.gdal.Dataset; import org.gdal.gdal.gdal; import org.gdal.gdal.Band; import org.gdal.gdalconst.gdalconstConstants; import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import java.io.*; import java.util.Base64; /** * @Description: 读取 tif文件的InputStream 并生成缩略图 * * @Auther: yanghaoxing * @Date: 2024/10/12 */ public class TiffThumbnailGenerator { static { System.loadLibrary("gdal"); gdal.AllRegister(); } // 将 InputStream 写入临时文件 private static File inputStreamToFile(InputStream inputStream) throws IOException { File tempFile = File.createTempFile("tempTiff", ".tif"); try (FileOutputStream out = new FileOutputStream(tempFile)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } } return tempFile; } /** * @Description: 传入InputStream,读取缩略图的BufferedImage * * @Param: [inputStream, width, height] * @Return: java.awt.image.BufferedImage * @Author yanghaoxing * @Date 2024/10/12 16:25 */ public static BufferedImage generateThumbnail(InputStream inputStream, int width, int height) throws IOException { File tiffFile = inputStreamToFile(inputStream); Dataset dataset = gdal.Open(tiffFile.getAbsolutePath(), gdalconstConstants.GA_ReadOnly); if (dataset == null) { // 无法打开 TIFF 文件 return null; } int xSize = dataset.getRasterXSize(); int ySize = dataset.getRasterYSize(); // 读取三个波段(假设分别为 R、G、B) Band bandRed = dataset.GetRasterBand(1); // 红波段 Band bandGreen = dataset.GetRasterBand(2); // 绿波段 Band bandBlue = dataset.GetRasterBand(3); // 蓝波段 // 分配数组来存储波段数据 int[] redData = new int[xSize * ySize]; int[] greenData = new int[xSize * ySize]; int[] blueData = new int[xSize * ySize]; // 读取波段数据 bandRed.ReadRaster(0, 0, xSize, ySize, redData); bandGreen.ReadRaster(0, 0, xSize, ySize, greenData); bandBlue.ReadRaster(0, 0, xSize, ySize, blueData); // 创建 RGB 图像 BufferedImage originalImage = new BufferedImage(xSize, ySize, BufferedImage.TYPE_INT_RGB); for (int y = 0; y < ySize; y++) { for (int x = 0; x < xSize; x++) { int r = redData[y * xSize + x]; int g = greenData[y * xSize + x]; int b = blueData[y * xSize + x]; int rgb = (r << 16) | (g << 8) | b; originalImage.setRGB(x, y, rgb); } } // 先裁剪掉黑边 BufferedImage croppedImage = cropBlackBorders(originalImage); // 然后缩放并居中图像 BufferedImage resizedImage = resizeAndCenterImage(croppedImage, width, height); // 关闭数据集 dataset.delete(); return resizedImage; } // 缩放图片以适应指定大小并保持宽高比 public static BufferedImage resizeAndCenterImage(BufferedImage originalImage, int targetWidth, int targetHeight) { int originalWidth = originalImage.getWidth(); int originalHeight = originalImage.getHeight(); // 计算缩放比例,保持宽高比 double scaleFactor = Math.min((double) targetWidth / originalWidth, (double) targetHeight / originalHeight); int scaledWidth = (int) (originalWidth * scaleFactor); int scaledHeight = (int) (originalHeight * scaleFactor); // 创建一个新的目标图像(透明背景) BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_ARGB); // 在新图像上进行绘制 Graphics2D g2d = resizedImage.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); // 计算居中位置 int x = (targetWidth - scaledWidth) / 2; int y = (targetHeight - scaledHeight) / 2; // 填充透明背景并绘制缩放后的图像 g2d.drawImage(originalImage, x, y, scaledWidth, scaledHeight, null); g2d.dispose(); return resizedImage; } // 裁剪黑边(裁剪掉纯黑的区域) public static BufferedImage cropBlackBorders(BufferedImage image) { int width = image.getWidth(); int height = image.getHeight(); int top = 0, bottom = height - 1, left = 0, right = width - 1; boolean foundTop = false, foundBottom = false, foundLeft = false, foundRight = false; // 检测上边界 for (int y = 0; y < height && !foundTop; y++) { for (int x = 0; x < width; x++) { if (!isBlack(image.getRGB(x, y))) { top = y; foundTop = true; break; } } } // 检测下边界 for (int y = height - 1; y >= 0 && !foundBottom; y--) { for (int x = 0; x < width; x++) { if (!isBlack(image.getRGB(x, y))) { bottom = y; foundBottom = true; break; } } } // 检测左边界 for (int x = 0; x < width && !foundLeft; x++) { for (int y = 0; y < height; y++) { if (!isBlack(image.getRGB(x, y))) { left = x; foundLeft = true; break; } } } // 检测右边界 for (int x = width - 1; x >= 0 && !foundRight; x--) { for (int y = 0; y < height; y++) { if (!isBlack(image.getRGB(x, y))) { right = x; foundRight = true; break; } } } // 裁剪图像 return image.getSubimage(left, top, right - left + 1, bottom - top + 1); } // 判断一个像素是否是黑色(你可以根据需要调整黑色的判断标准) private static boolean isBlack(int rgb) { Color color = new Color(rgb); // 理论上应该是:color.getRed() == 0 && color.getGreen() == 0 && color.getBlue() == 0; 但是目前测试数据的黑边是色值是:1,1,0 // 白边也去掉 return (color.getRed() == 1 && color.getGreen() == 1 && color.getBlue() == 0) || (color.getRed() == 0 && color.getGreen() == 0 && color.getBlue() == 0) || (color.getRed() == 255 && color.getGreen() == 255 && color.getBlue() == 255); } // 将 BufferedImage 转换为 Base64 字符串 public static String bufferedImageToBase64(BufferedImage image, String format) throws IOException { if (image == null) { return null; } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ImageIO.write(image, format, outputStream); byte[] imageBytes = outputStream.toByteArray(); String base64Image = Base64.getEncoder().encodeToString(imageBytes); outputStream.close(); return base64Image; } /** * @Description: 将BufferedImage写入指定路径 * * @Param: [thumbnail, outputFilePath] * @Return: void * @Author yanghaoxing * @Date 2024/10/12 16:24 */ public static void saveThumbnailToFile(BufferedImage thumbnail, String outputFilePath) throws IOException { File outputfile = new File(outputFilePath); ImageIO.write(thumbnail, "png", outputfile); } }