springboot使用GDAL获取tif文件的缩略图并转为base64
springboot使用GDAL获取tif文件的缩略图并转为base64
首先需要安装gdal:https://blog.csdn.net/qq_61950936/article/details/142880279?spm=1001.2014.3001.5501
然后是配置pom.xml文件:
<!--处理缩略图的--><dependency><groupId>org.gdal</groupId><artifactId>gdal</artifactId><version>3.9.0</version></dependency>
更新maven。
效果:

TiffThumbnailGenerator类:
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);}
}
使用:
/*** @Description: 根据文件id获取该tif图的缩略图** @Param: [fileId]* @Return: String base64Image* @Author yanghaoxing* @Date 2024/10/12 10:24*/private String getTiffImageBuffered(List<String> fileIds) {int width = 255, height = 255;FileDownloadVo fileDownloadVo = uploadService.getFile(fileIds.get(0));if (fileDownloadVo != null && fileDownloadVo.getFileStream() != null) {InputStream inputStream = fileDownloadVo.getFileStream();try {// 传入InputStream,读取缩略图的BufferedImageBufferedImage bufferedImage = TiffThumbnailGenerator.generateThumbnail(inputStream, width, height);// 转为base64格式String base64Image = TiffThumbnailGenerator.bufferedImageToBase64(bufferedImage, "png");return base64Image != null ? "data:image/png;base64," + base64Image : null;} catch (IOException e) {return null;}}return null;}
相关文章:
springboot使用GDAL获取tif文件的缩略图并转为base64
springboot使用GDAL获取tif文件的缩略图并转为base64 首先需要安装gdal:https://blog.csdn.net/qq_61950936/article/details/142880279?spm1001.2014.3001.5501 然后是配置pom.xml文件: <!--处理缩略图的--><dependency><groupId>o…...
Pytorch——pip下载安装pytorch慢的解决办法
一、找到需要下载的pytorch链接 运行:pip install torch1.11.0cu113 torchvision0.12.0cu113 torchaudio0.11.0 --extra-index-url https://download.pytorch.org/whl/cu113。然后得到: 我这里为:https://download.pytorch.org/whl/cu113/t…...
uniapp微信小程序调用百度OCR
uniapp编写微信小程序调用百度OCR 公司有一个识别行驶证需求,调用百度ocr识别 使用了image-tools这个插件,因为百度ocr接口用图片的base64 这里只是简单演示,accesstoken获取接口还是要放在服务器端,不然就暴露了自己的百度项目k…...
Vue3+TS项目---实用的复杂类型定义总结
namespace 概念 在TypeScript中,namespace是一种用于组织代码得结构,主要用于将相关得功能(例如类、接口、函数等)组合在一起。它可以帮助避免命名冲突,尤其是在大项目中。 用法 1.定义命名空间 使用namespace关键…...
尚硅谷rabbitmq2024 工作模式路由篇 第11节 答疑
String exchangeName "test_direct"; /! 创建交换机 人图全 channel.exchangeDeclare(exchangeName,BuiltinExchangeType.DIREcT, b: true, b1: false, b2: false, map: null); /1 创建队列 String queue1Name "test_direct_queue1"; String queue2Name &q…...
HTTP vs WebSocket
本文将对比介绍HTTP 和 WebSocket ! 相关文章: 1.HTTP 详解 2.WebSocket 详解 一、HTTP:请求/响应的主流协议 HTTP(超文本传输协议)是用于发送和接收网页数据的标准协议。它最早于1991年由Tim Berners-Lee提出来&…...
R语言医学数据分析实践-数据读写
【图书推荐】《R语言医学数据分析实践》-CSDN博客 《R语言医学数据分析实践 李丹 宋立桓 蔡伟祺 清华大学出版社9787302673484》【摘要 书评 试读】- 京东图书 (jd.com) R语言编程_夏天又到了的博客-CSDN博客 R编程环境的搭建-CSDN博客 在分析公共卫生数据时,数…...
JavaWeb环境下Spring Boot在线考试系统的优化策略
摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了基于JavaWeb技术的在线考试系统设计与实现的开发全过程。通过分析基于Java Web技术的在线考试系统设计与实现管理的不足,创建了一个计算机管理基于Ja…...
ETL技术在金蝶云星空与旺店通WMS集成中的应用
金蝶云星空数据集成到旺店通WMS的技术案例分享 在数字化转型的背景下,现代企业对系统间的数据集成需求日益增加。本篇文章将以“组装入库>其他入库单-1”方案为例,详细解析如何通过轻易云数据集成平台,实现金蝶云星空与旺店通WMS之间的数…...
【力扣热题100】3194. 最小元素和最大元素的最小平均值【Java】
题目:3194.最小元素和最大元素的最小平均值 你有一个初始为空的浮点数数组 averages。另给你一个包含 n 个整数的数组 nums,其中 n 为偶数。 你需要重复以下步骤 n / 2 次: 从 nums 中移除 最小 的元素 minElement 和 最大 的元素 maxElement…...
机器学习拟合过程
import numpy as np import matplotlib.pyplot as plt# 步骤1: 生成模拟数据 np.random.seed(0) X 2 * np.random.rand(100, 1) y 4 3 * X 2 * X**2 np.random.randn(100, 1)# 步骤2: 定义线性模型 (我们从随机权重开始) w np.random.randn(2, 1) b np.random.randn(1)#…...
如何快速部署一套智能化openGauss测试环境
一、openGauss介绍 openGauss是一款开源关系型数据库管理系统,采用木兰宽松许可证v2发行,允许用户自由地复制、使用、修改和分发软件。openGauss内核深度融合了华为在数据库领域多年的研发经验,结合企业级场景需求,持续构建竞争力…...
【设计模式】深入理解Python中的原型设计模式
深入理解Python中的原型设计模式 在软件开发中,有时需要创建对象的过程非常复杂或者代价较高,而在同一类对象的实例之间有很多重复的属性。为了避免重复构造对象,提升性能和效率,原型设计模式(Prototype Pattern&…...
Django CORS配置方案
参考 https://pypi.org/project/django-cors-headers/ 在setting.py中设置 INSTALLED_APPS [......corsheaders, #添加此行 ]MIDDLEWARE[......corsheaders.middleware.CorsMiddleware, #添加此行django.middleware.common.CommonMiddleware,#django.middleware.csrf.CsrfVi…...
2024年开放式耳机哪个牌子好?推荐最好的顶级开放式耳机品牌
在当下,开放式耳机逐渐成为众多消费者的新宠。与传统入耳式耳机相比,开放式耳机展现出诸多独特之处。它可以呈现出更清晰的音质效果,让用户有更美妙的听觉体验。在佩戴感上,开放式耳机更为舒适,不会给耳朵带来压迫感。…...
零基础读懂Stable Diffusion!
前言 一文搞懂Stable Diffusion是什么,怎么训练和使用,语义信息影响生成图片的过程。>>[][加入极市CV技术交流群,走在计算机视觉的最前沿] 前几个月AIGC可谓是大热了一把,各种高质量的生成图片层出不穷,而其中…...
Hash Join 和 Index Join工作原理和性能差异
在数据库查询中,Hash Join 和 Index Join 是两种常见的表连接策略。了解它们的工作原理和性能差异有助于设计高效的数据库查询。我们可以使用 Java 模拟这两种不同的连接方式,并进行性能对比。 1. Hash Join 和 Index Join 的概念: Hash Joi…...
Apifox简介及使用
Apifox 是一款集 API文档管理、接口调试、接口自动化测试 和 Mock 功能于一体的全功能工具,旨在为开发者和测试人员提供一个高效的一站式解决方案。它融合了 Postman、Swagger、JMeter 等工具的优势,能够极大地提升团队协作和 API 开发的效率。 在实际开…...
十、IPD 实施细节(产品设计与开发管理)
产品设计与开发管理 产品设计与开发管理是IPD(集成产品开发)实施过程中的核心环节。它确保从概念设计到最终产品的实现能够按照预定的质量、成本、进度目标顺利完成,并与市场需求、技术发展及企业战略保持一致。IPD强调产品设计与开发管理过程中跨职能团队的协作、流程的系…...
MySQL-13.DQL-聚合函数
一.DQL-分组查询 二.聚合函数 -- DQL:分组查询 -- 聚合函数 -- 1.统计该企业员工数量 count select count(id) from tb_emp; select count(job) from tb_emp;select count(A) from tb_emp; select count(*) from tb_emp;-- 2.统计该企业最早入职的员工 min select min(entr…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
DiscuzX3.5发帖json api
参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...
若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...
