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…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
