当前位置: 首页 > news >正文

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&#xff1a;https://blog.csdn.net/qq_61950936/article/details/142880279?spm1001.2014.3001.5501 然后是配置pom.xml文件&#xff1a; <!--处理缩略图的--><dependency><groupId>o…...

Pytorch——pip下载安装pytorch慢的解决办法

一、找到需要下载的pytorch链接 运行&#xff1a;pip install torch1.11.0cu113 torchvision0.12.0cu113 torchaudio0.11.0 --extra-index-url https://download.pytorch.org/whl/cu113。然后得到&#xff1a; 我这里为&#xff1a;https://download.pytorch.org/whl/cu113/t…...

uniapp微信小程序调用百度OCR

uniapp编写微信小程序调用百度OCR 公司有一个识别行驶证需求&#xff0c;调用百度ocr识别 使用了image-tools这个插件&#xff0c;因为百度ocr接口用图片的base64 这里只是简单演示&#xff0c;accesstoken获取接口还是要放在服务器端&#xff0c;不然就暴露了自己的百度项目k…...

Vue3+TS项目---实用的复杂类型定义总结

namespace 概念 在TypeScript中&#xff0c;namespace是一种用于组织代码得结构&#xff0c;主要用于将相关得功能&#xff08;例如类、接口、函数等&#xff09;组合在一起。它可以帮助避免命名冲突&#xff0c;尤其是在大项目中。 用法 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 &#xff01; 相关文章&#xff1a; 1.HTTP 详解 2.WebSocket 详解 一、HTTP&#xff1a;请求/响应的主流协议 HTTP&#xff08;超文本传输协议&#xff09;是用于发送和接收网页数据的标准协议。它最早于1991年由Tim Berners-Lee提出来&…...

R语言医学数据分析实践-数据读写

【图书推荐】《R语言医学数据分析实践》-CSDN博客 《R语言医学数据分析实践 李丹 宋立桓 蔡伟祺 清华大学出版社9787302673484》【摘要 书评 试读】- 京东图书 (jd.com) R语言编程_夏天又到了的博客-CSDN博客 R编程环境的搭建-CSDN博客 在分析公共卫生数据时&#xff0c;数…...

JavaWeb环境下Spring Boot在线考试系统的优化策略

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了基于JavaWeb技术的在线考试系统设计与实现的开发全过程。通过分析基于Java Web技术的在线考试系统设计与实现管理的不足&#xff0c;创建了一个计算机管理基于Ja…...

ETL技术在金蝶云星空与旺店通WMS集成中的应用

金蝶云星空数据集成到旺店通WMS的技术案例分享 在数字化转型的背景下&#xff0c;现代企业对系统间的数据集成需求日益增加。本篇文章将以“组装入库>其他入库单-1”方案为例&#xff0c;详细解析如何通过轻易云数据集成平台&#xff0c;实现金蝶云星空与旺店通WMS之间的数…...

【力扣热题100】3194. 最小元素和最大元素的最小平均值【Java】

题目&#xff1a;3194.最小元素和最大元素的最小平均值 你有一个初始为空的浮点数数组 averages。另给你一个包含 n 个整数的数组 nums&#xff0c;其中 n 为偶数。 你需要重复以下步骤 n / 2 次&#xff1a; 从 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是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行&#xff0c;允许用户自由地复制、使用、修改和分发软件。openGauss内核深度融合了华为在数据库领域多年的研发经验&#xff0c;结合企业级场景需求&#xff0c;持续构建竞争力…...

【设计模式】深入理解Python中的原型设计模式

深入理解Python中的原型设计模式 在软件开发中&#xff0c;有时需要创建对象的过程非常复杂或者代价较高&#xff0c;而在同一类对象的实例之间有很多重复的属性。为了避免重复构造对象&#xff0c;提升性能和效率&#xff0c;原型设计模式&#xff08;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年开放式耳机哪个牌子好?推荐最好的顶级开放式耳机品牌

在当下&#xff0c;开放式耳机逐渐成为众多消费者的新宠。与传统入耳式耳机相比&#xff0c;开放式耳机展现出诸多独特之处。它可以呈现出更清晰的音质效果&#xff0c;让用户有更美妙的听觉体验。在佩戴感上&#xff0c;开放式耳机更为舒适&#xff0c;不会给耳朵带来压迫感。…...

零基础读懂Stable Diffusion!

前言 一文搞懂Stable Diffusion是什么&#xff0c;怎么训练和使用&#xff0c;语义信息影响生成图片的过程。>>[][加入极市CV技术交流群&#xff0c;走在计算机视觉的最前沿] 前几个月AIGC可谓是大热了一把&#xff0c;各种高质量的生成图片层出不穷&#xff0c;而其中…...

Hash Join 和 Index Join工作原理和性能差异

在数据库查询中&#xff0c;Hash Join 和 Index Join 是两种常见的表连接策略。了解它们的工作原理和性能差异有助于设计高效的数据库查询。我们可以使用 Java 模拟这两种不同的连接方式&#xff0c;并进行性能对比。 1. Hash Join 和 Index Join 的概念&#xff1a; Hash Joi…...

Apifox简介及使用

Apifox 是一款集 API文档管理、接口调试、接口自动化测试 和 Mock 功能于一体的全功能工具&#xff0c;旨在为开发者和测试人员提供一个高效的一站式解决方案。它融合了 Postman、Swagger、JMeter 等工具的优势&#xff0c;能够极大地提升团队协作和 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…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)

一、OpenBCI_GUI 项目概述 &#xff08;一&#xff09;项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台&#xff0c;其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言&#xff0c;首次接触 OpenBCI 设备时&#xff0c;往…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...