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…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
