文章目录

  • 需求
  • 修改细节
    • 前端
      • 主要修改点说明:
      • 前端传递格式
    • 后端
      • ArtifactItem 类:
      • ScrollServiceImpl 类:
      • 修改 `InfoPanel` 结构
      • 重构 `ScrollHorizontalRollComposer`
      • 修改后的 `ScrollHorizontalRollComposer`
      • 移除冗余代码
      • 修改总结
    • 数据流
    • 图片格式兼容性问题
  • 成果展示

需求

由于图片和文字交流是相互独立的,故仅保留文字交互信息,然后根据文字中心词,匹配图床上的相应url,进行游览画卷构建

  1. 数据结构:前端传递给后端的是一个对象数组,每个对象包含:
    • description:文物/展品的文字描述(如"陶瓷"、“青铜器”)
    • imageUrl:与该描述对应的默认图片URL(如陶瓷描述对应陶瓷图片URL)
  2. 后端处理
    • 接收包含descriptionimageUrl的对象数组
    • 对每个对象:
      • 获取imageUrl对应的图片
      • 将图片和描述组合显示在画卷的同一个面板中(图片上方/下方显示对应文字)
  3. 展示效果:最终生成的画卷中,每个文物/展品都是一个图文结合的面板,而不是图片和文字分离显示

修改细节

前端

generateScroll()

async generateScroll() {try {// 禁用按钮防止重复点击this.generating = true;uni.showLoading({ title: '生成中...', mask: true });// 构建记录数据 - 只处理文字类型const records = this.interactionRecords.filter(record => record.type === 'text') // 只保留文字类型记录.map(record => ({type: 'text', // 强制设置为text类型content: record.content, // 文字内容imageUrl: this.getDefaultImageForText(record.content) // 根据内容匹配默认图片}));console.log('发送给后端的记录数据:', JSON.stringify(records, null, 2));// 调用后端接口const res = await post('/api/scroll/generate', records);if (!res) {throw new Error('未获取到有效响应');}// 预览生成的画卷uni.previewImage({current: res,urls: [res],success: () => {// 记录生成历史this.interactionRecords.push({type: 'scroll',content: '生成游览画卷',imageUrl: res,timestamp: new Date().getTime(),});},fail: (err) => {throw new Error('图片预览失败: ' + (err.errMsg || '未知错误'));},});} catch (error) {console.error('生成失败:', error);uni.showToast({title: '生成失败: ' + (error.message || '请稍后重试'),icon: 'none',duration: 2000,});} finally {this.generating = false;uni.hideLoading();}
},// 根据文本内容返回匹配的默认图片URL
getDefaultImageForText(text) {const defaultImages = {'佛像': 'https://i.ibb.co/fGH1bnHs/OIP-C-1.webp','佛教': 'https://i.ibb.co/fGH1bnHs/OIP-C-1.webp','陶瓷': 'https://i.ibb.co/R4kywTQs/OIP-C.webp','青铜器': 'https://i.ibb.co/fV1xCcYd/25bb-hyrtarw2279586.jpg','书画': 'https://example.com/default-painting.jpg', // 替换为实际URL'文物': 'https://example.com/default-artifact.jpg' // 替换为实际URL};// 查找匹配的关键词const matchedKey = Object.keys(defaultImages).find(key => text.includes(key));// 返回匹配的图片URL或默认URLreturn matchedKey ? defaultImages[matchedKey] : 'https://example.com/default-museum.jpg';
}

主要修改点说明:

  1. 过滤非文字类型记录
    • 使用filter(record => record.type === 'text')只保留文字类型的交互记录
  2. 统一数据结构
    • 所有记录都设置为type: 'text'
    • content字段包含原始文字内容
    • imageUrl字段根据文字内容自动匹配默认图片
  3. 改进图片匹配逻辑
    • 使用对象映射方式匹配关键词和图片URL
    • 支持多个关键词匹配同一图片(如"佛像"和"佛教")
    • 提供默认图片URL作为后备
  4. 增强日志输出
    • 在发送请求前打印完整的数据结构,便于调试
  5. 错误处理
    • 保留原有的错误处理逻辑,确保用户体验

前端传递格式

[{"type": "text","content": "这是第一段文字","imageUrl": "https://example.com/background1.jpg"},{"type": "text","content": "这是第二段文字","imageUrl": "https://example.com/background2.jpg"}
]

后端

ArtifactItem 类:

  • 当前设计同时支持图片和文字类型,但如果只接受文字类型,可以简化这个类
  • 可以移除 type 字段和 imageUrl 字段,因为不再需要区分类型
public class ArtifactItem {private String content; // 只需要保留文字内容public String getContent() {return content;}public void setContent(String content) {this.content = content;}
}

ScrollServiceImpl 类:

  • generate() 方法中的处理逻辑可以简化,因为不再需要处理图片类型
  • 移除图片下载相关代码(因为现在传递的是图片url,而不是图片格式)
  • 背景生成也需要调整
@Override
public String generate(List<ArtifactItem> records) throws Exception {List<InfoPanel> panels = new ArrayList<>();// 1. 生成背景(可选,如果仍需动态背景)BufferedImage bg = generateNewBackground();BufferedImage frame = loadResourceImage(FRAME_IMAGE_PATH);// 2. 直接使用前端传递的 imageUrlfor (ArtifactItem record : records) {if ("text".equals(record.getType())) {panels.add(new InfoPanel(record.getImageUrl(), record.getContent()));}}// 3. 修改 ScrollHorizontalRollComposer.compose() 方法//    现在它需要处理 URL 而不是 BufferedImageBufferedImage content = ScrollHorizontalRollComposer.compose(bg, panels);BufferedImage finalRoll = ScrollFramer.embed(content, frame);// 其余代码保持不变...return uploadToImageHost(finalRoll);
}

修改 InfoPanel 结构

  • BufferedImage image 改为 String imageUrl
package com.museum.pojo;/** 拼画卷时用的“小面板”包装类 */
public class InfoPanel {private String imageUrl; // 改为存储图片URLprivate String text;public InfoPanel(String imageUrl, String text) {this.imageUrl = imageUrl;this.text = text;}public String getImageUrl() { return imageUrl; }public String getText() { return text; }
}

重构 ScrollHorizontalRollComposer

  • 动态加载图片(URLImageLoader.load())。
  • 添加图片加载失败的降级处理(占位图)。
package com.museum.utils;import com.museum.pojo.InfoPanel;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.CubicCurve2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;public class ScrollHorizontalRollComposer {// 配置参数(保持不变)private static final int PANEL_WIDTH = 560;private static final int PANEL_HEIGHT = 400;private static final int PANEL_VGAP = 50;private static final int TOP_PADDING = 30;private static final int BOTTOM_PADDING = 30;private static final int CARD_MARGIN = 30;private static final int CARD_ROUND = 25;private static final int CARD_ALPHA = 190;private static final int ZIGZAG_OFFSET = 40;private static final int TEXT_PADDING = 40;private static final int FONT_SIZE = 22;private static final int IMAGE_SIZE = 180;// HTTP客户端(用于动态加载图片)private static final OkHttpClient httpClient = new OkHttpClient();public static BufferedImage compose(BufferedImage bg, List<InfoPanel> panels) {int panelCount = panels.size();int totalHeight = TOP_PADDING + BOTTOM_PADDING + panelCount * PANEL_HEIGHT + (panelCount - 1) * PANEL_VGAP;BufferedImage scroll = new BufferedImage(PANEL_WIDTH, totalHeight, BufferedImage.TYPE_INT_ARGB);Graphics2D g = scroll.createGraphics();// 1. 绘制背景(平铺)for (int y = 0; y < totalHeight; y += bg.getHeight()) {g.drawImage(bg, 0, y, PANEL_WIDTH, bg.getHeight(), null);}// 2. 设置字体和抗锯齿g.setFont(new Font("Serif", Font.PLAIN, FONT_SIZE));g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);FontMetrics fm = g.getFontMetrics();int lineHeight = fm.getHeight();// 3. 绘制每个面板int cursorY = TOP_PADDING;List<Point> centers = new ArrayList<>();for (int i = 0; i < panelCount; i++) {InfoPanel panel = panels.get(i);String[] txtLines = panel.getText().split("(?<=\\。)");// 3.1 计算面板位置(Z字型布局)int cardWidth = PANEL_WIDTH - 2 * CARD_MARGIN;int offsetX = (i % 2 == 0) ? ZIGZAG_OFFSET : -ZIGZAG_OFFSET;int cardX = (PANEL_WIDTH - cardWidth) / 2 + offsetX;// 3.2 绘制阴影和卡片背景g.setColor(new Color(0, 0, 0, 28));g.fillRoundRect(cardX + 5, cursorY + 5, cardWidth, PANEL_HEIGHT, CARD_ROUND, CARD_ROUND);g.setColor(new Color(255, 255, 255, CARD_ALPHA));g.fillRoundRect(cardX, cursorY, cardWidth, PANEL_HEIGHT, CARD_ROUND, CARD_ROUND);// 3.3 动态加载并绘制图片(关键修改点)try {BufferedImage img = loadImageFromUrl(panel.getImageUrl());int imgX = cardX + (cardWidth - IMAGE_SIZE) / 2;int imgY = cursorY + 30;g.drawImage(img, imgX, imgY, IMAGE_SIZE, IMAGE_SIZE, null);} catch (IOException e) {// 图片加载失败时绘制占位符g.setColor(Color.LIGHT_GRAY);g.fillRect(cardX + (cardWidth - IMAGE_SIZE)/2, cursorY + 30, IMAGE_SIZE, IMAGE_SIZE);g.setColor(Color.RED);g.drawString("图片加载失败", cardX + 20, cursorY + 60);}// 3.4 绘制文字g.setColor(Color.BLACK);int textX = cardX + TEXT_PADDING;int textY = cursorY + 30 + IMAGE_SIZE + 30;int textMaxWidth = cardWidth - 2 * TEXT_PADDING;drawWrappedText(g, txtLines, textX, textY, textMaxWidth, lineHeight);// 记录面板中心点(用于后续绘制连接线)centers.add(new Point(cardX + cardWidth/2, cursorY + PANEL_HEIGHT/2));cursorY += PANEL_HEIGHT + PANEL_VGAP;}// 4. 绘制面板间的连接线(保持不变)drawConnectingLines(g, centers);g.dispose();return scroll;}// 新增方法:从URL加载图片private static BufferedImage loadImageFromUrl(String imageUrl) throws IOException {Request request = new Request.Builder().url(imageUrl).build();try (Response response = httpClient.newCall(request).execute()) {if (!response.isSuccessful() || response.body() == null) {throw new IOException("HTTP " + response.code());}return ImageIO.read(response.body().byteStream());}}// 绘制连接线(保持不变)private static void drawConnectingLines(Graphics2D g, List<Point> centers) {g.setColor(new Color(90, 90, 90, 180));float[] dash = {10, 5};g.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10f, dash, 0));for (int i = 0; i < centers.size() - 1; i++) {Point p1 = centers.get(i);Point p2 = centers.get(i + 1);int ctrlY = (p1.y + p2.y)/2 + 60 * ((i%2 == 0) ? 1 : -1);CubicCurve2D curve = new CubicCurve2D.Float(p1.x, p1.y, p1.x, ctrlY, p2.x, ctrlY, p2.x, p2.y);g.draw(curve);}}// 文字换行处理(优化版)private static void drawWrappedText(Graphics2D g, String[] lines, int x, int y, int maxWidth, int lineHeight) {FontMetrics fm = g.getFontMetrics();for (String line : lines) {if (fm.stringWidth(line) <= maxWidth) {g.drawString(line, x, y);y += lineHeight;} else {// 处理长文本换行StringBuilder currentLine = new StringBuilder();for (char c : line.toCharArray()) {if (fm.stringWidth(currentLine.toString() + c) > maxWidth) {g.drawString(currentLine.toString(), x, y);y += lineHeight;currentLine.setLength(0);}currentLine.append(c);}if (currentLine.length() > 0) {g.drawString(currentLine.toString(), x, y);y += lineHeight;}}}}
}

修改后的 ScrollHorizontalRollComposer

InfoPanel 改为存储图片 URL 而非 BufferedImage,需要重构 ScrollHorizontalRollComposer

package com.museum.utils;import com.museum.pojo.InfoPanel;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.CubicCurve2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;public class ScrollHorizontalRollComposer {// 配置参数(保持不变)private static final int PANEL_WIDTH = 560;private static final int PANEL_HEIGHT = 400;private static final int PANEL_VGAP = 50;private static final int TOP_PADDING = 30;private static final int BOTTOM_PADDING = 30;private static final int CARD_MARGIN = 30;private static final int CARD_ROUND = 25;private static final int CARD_ALPHA = 190;private static final int ZIGZAG_OFFSET = 40;private static final int TEXT_PADDING = 40;private static final int FONT_SIZE = 22;private static final int IMAGE_SIZE = 180;// HTTP客户端(用于动态加载图片)private static final OkHttpClient httpClient = new OkHttpClient();public static BufferedImage compose(BufferedImage bg, List<InfoPanel> panels) {int panelCount = panels.size();int totalHeight = TOP_PADDING + BOTTOM_PADDING + panelCount * PANEL_HEIGHT + (panelCount - 1) * PANEL_VGAP;BufferedImage scroll = new BufferedImage(PANEL_WIDTH, totalHeight, BufferedImage.TYPE_INT_ARGB);Graphics2D g = scroll.createGraphics();// 1. 绘制背景(平铺)for (int y = 0; y < totalHeight; y += bg.getHeight()) {g.drawImage(bg, 0, y, PANEL_WIDTH, bg.getHeight(), null);}// 2. 设置字体和抗锯齿g.setFont(new Font("Serif", Font.PLAIN, FONT_SIZE));g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);FontMetrics fm = g.getFontMetrics();int lineHeight = fm.getHeight();// 3. 绘制每个面板int cursorY = TOP_PADDING;List<Point> centers = new ArrayList<>();for (int i = 0; i < panelCount; i++) {InfoPanel panel = panels.get(i);String[] txtLines = panel.getText().split("(?<=\\。)");// 3.1 计算面板位置(Z字型布局)int cardWidth = PANEL_WIDTH - 2 * CARD_MARGIN;int offsetX = (i % 2 == 0) ? ZIGZAG_OFFSET : -ZIGZAG_OFFSET;int cardX = (PANEL_WIDTH - cardWidth) / 2 + offsetX;// 3.2 绘制阴影和卡片背景g.setColor(new Color(0, 0, 0, 28));g.fillRoundRect(cardX + 5, cursorY + 5, cardWidth, PANEL_HEIGHT, CARD_ROUND, CARD_ROUND);g.setColor(new Color(255, 255, 255, CARD_ALPHA));g.fillRoundRect(cardX, cursorY, cardWidth, PANEL_HEIGHT, CARD_ROUND, CARD_ROUND);// 3.3 动态加载并绘制图片(关键修改点)try {BufferedImage img = loadImageFromUrl(panel.getImageUrl());int imgX = cardX + (cardWidth - IMAGE_SIZE) / 2;int imgY = cursorY + 30;g.drawImage(img, imgX, imgY, IMAGE_SIZE, IMAGE_SIZE, null);} catch (IOException e) {// 图片加载失败时绘制占位符g.setColor(Color.LIGHT_GRAY);g.fillRect(cardX + (cardWidth - IMAGE_SIZE)/2, cursorY + 30, IMAGE_SIZE, IMAGE_SIZE);g.setColor(Color.RED);g.drawString("图片加载失败", cardX + 20, cursorY + 60);}// 3.4 绘制文字g.setColor(Color.BLACK);int textX = cardX + TEXT_PADDING;int textY = cursorY + 30 + IMAGE_SIZE + 30;int textMaxWidth = cardWidth - 2 * TEXT_PADDING;drawWrappedText(g, txtLines, textX, textY, textMaxWidth, lineHeight);// 记录面板中心点(用于后续绘制连接线)centers.add(new Point(cardX + cardWidth/2, cursorY + PANEL_HEIGHT/2));cursorY += PANEL_HEIGHT + PANEL_VGAP;}// 4. 绘制面板间的连接线(保持不变)drawConnectingLines(g, centers);g.dispose();return scroll;}// 新增方法:从URL加载图片private static BufferedImage loadImageFromUrl(String imageUrl) throws IOException {Request request = new Request.Builder().url(imageUrl).build();try (Response response = httpClient.newCall(request).execute()) {if (!response.isSuccessful() || response.body() == null) {throw new IOException("HTTP " + response.code());}return ImageIO.read(response.body().byteStream());}}// 绘制连接线(保持不变)private static void drawConnectingLines(Graphics2D g, List<Point> centers) {g.setColor(new Color(90, 90, 90, 180));float[] dash = {10, 5};g.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10f, dash, 0));for (int i = 0; i < centers.size() - 1; i++) {Point p1 = centers.get(i);Point p2 = centers.get(i + 1);int ctrlY = (p1.y + p2.y)/2 + 60 * ((i%2 == 0) ? 1 : -1);CubicCurve2D curve = new CubicCurve2D.Float(p1.x, p1.y, p1.x, ctrlY, p2.x, ctrlY, p2.x, p2.y);g.draw(curve);}}// 文字换行处理(优化版)private static void drawWrappedText(Graphics2D g, String[] lines, int x, int y, int maxWidth, int lineHeight) {FontMetrics fm = g.getFontMetrics();for (String line : lines) {if (fm.stringWidth(line) <= maxWidth) {g.drawString(line, x, y);y += lineHeight;} else {// 处理长文本换行StringBuilder currentLine = new StringBuilder();for (char c : line.toCharArray()) {if (fm.stringWidth(currentLine.toString() + c) > maxWidth) {g.drawString(currentLine.toString(), x, y);y += lineHeight;currentLine.setLength(0);}currentLine.append(c);}if (currentLine.length() > 0) {g.drawString(currentLine.toString(), x, y);y += lineHeight;}}}}
}

关键修改说明

  1. 图片加载方式
    • 移除对 InfoPanel.getImage() 的依赖
    • 新增 loadImageFromUrl() 方法,通过 HTTP 动态加载图片
    • 添加图片加载失败时的降级处理(显示占位符)
  2. 性能优化
    • 使用静态 OkHttpClient 复用连接
    • 图片按需加载,避免预先下载所有图片
  3. 错误处理
    • 捕获 IOException 并显示错误提示
    • 保持画卷生成流程不被单张图片失败中断
  4. 兼容性
    • 完全适配修改后的 InfoPanel 结构(imageUrl + text
    • 保留原有布局和样式逻辑

移除冗余代码

删除 ImageCropper 和本地图片裁剪逻辑。

修改总结

文件原版本(本地文件)修改版本(URL处理)主要改动点
ScrollHorizontalRollComposer直接使用BufferedImagepanels.get(i).getImage()新增loadImageFromUrl()方法: java<br>BufferedImage img = loadImageFromUrl(panel.getImageUrl());<br> 支持HTTP下载图片,失败时显示占位符1. 通过URL动态加载图片 2. 使用OkHttpClient 3. 错误降级处理
ImageCropper仅支持文件路径输入: ImageIO.read(new File(path))支持两种输入方式: java<br>// 方式1:URL转临时文件<br>crop(downloadToTemp(url), w, h);<br><br>// 方式2:直接处理BufferedImage<br>crop(bufferedImage, w, h);<br>1. 增加日志 2. 支持内存图像处理 3. 优化缩放插值
ScrollService处理MultipartFile上传: java<br>multipartFile.transferTo(tempFile);<br>cropImageFile(tempFile...);<br>完全重构为URL处理: java<br>// 动态生成背景图<br>BufferedImage bg = generateNewBackground();<br><br>// 直接使用URL创建面板<br>panels.add(new InfoPanel(url, text));<br><br>// 自动上传结果到图床<br>uploadScrollToImageHost(finalRoll);<br>1. 移除文件上传逻辑 2. 新增DALL-E背景生成 3. 集成图床自动上传
InfoPanel模型存储BufferedImagejava<br>private BufferedImage image;<br>改为存储图片URL: java<br>private String imageUrl; // 存储URL<br>模型层解耦图像存储
ScrollFramer简单居中嵌入: java<br>g.drawImage(content, x, y, null);<br>智能缩放+裁剪: java<br>// 计算缩放比例<br>double scale = innerH / content.getHeight();<br><br>// 水平居中裁剪<br>if (cropX > 0) {<br> content.getSubimage(cropX, 0, w, h);<br>}<br>1. 自适应内容尺寸 2. 精确边框对齐

数据流

前端 后端 图床 [{content:"青铜器", imageUrl:"..."},...] 下载图片 合成画卷 上传结果 返回画卷URL 前端 后端 图床

图片格式兼容性问题

  • 使用的图片是 .webp 格式,但 Java 原生 ImageIO 不支持 WebP。
  • 错误日志中 BufferedImage.getWidth() failed 表明图片已下载但无法解析。

解决方案

引入 WebP 支持库

<dependency><groupId>com.twelvemonkeys.imageio</groupId><artifactId>imageio-webp</artifactId><version>3.9.4</version>
</dependency>

同时,上传的图床的照片格式尽量使jpg

成果展示

测试版

在这里插入图片描述

最终版

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/909935.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/909935.shtml
英文地址,请注明出处:http://en.pswp.cn/news/909935.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

攻克SQL审核“最后堡垒”!PawSQL首发T-SQL存储过程深度优化引擎

为什么存储过程审核那么难&#xff1f; 存储过程将数据操作逻辑固化在数据库层&#xff0c;一次编译、多次执行&#xff0c;既能大幅提升性能&#xff0c;也能通过权限隔离增强安全。然而&#xff0c;正因其逻辑复杂、分支众多&#xff0c;存储过程内部的 SQL 审核与优化常常成…

计算机网络零基础完全指南

目录 🌐 什么是计算机网络 生活中的类比 计算机网络的本质 网络的发展历程 🏠 网络IP详解(重点) 1. IP地址是什么? 生活例子:IP地址就像门牌号 IP地址的格式 IP地址的二进制表示 2. IP地址的分类详解 A类地址(大型网络) B类地址(中型网络) C类地址(小…

DL___线性神经网络

1&#xff09;回归&#xff08;regression&#xff09;是能为一个或多个自变量与因变量之间关系建模的一类方法。 在自然科学和社会科学领域&#xff0c;回归经常用来表示输入和输出之间的关系。 2&#xff09;一般回归是和预测有关&#xff0c;比如预测价格(房屋&#xff0c;…

WSL2安装与使用(USB、GPU、虚拟机、图形界面)

文章目录 前言WSL2安装&#xff08;手动安装&#xff09;WSL2基础使用VS Code与WSL2配合使用连接USB设备WSL2中使用GPU&#xff08;RTX5060Ti 16G&#xff09;与虚拟机兼容使用&#xff08;Virtual Box&#xff09;图形与桌面环境WSL消失&#xff08;灾难性故障&#xff09;问题…

uni-app项目实战笔记16--实现头部导航栏效果

先来看效果&#xff1a; 要求&#xff1a;顶部导航栏要始终固定在上方&#xff0c;不随页面上下拖动而消失。 代码实现&#xff1a; 1.定义一个自定义导航栏组件&#xff1a;custom-nav-bar.vue&#xff0c;并写入如下代码&#xff1a; <template><view class"…

web3.js 核心包及子模块

. 核心包 (web3) 功能:提供基础连接、工具函数和核心功能。 包含子模块: web3.eth - 以太坊区块链交互 web3.utils - 辅助工具函数 web3.shh - Whisper 协议(已废弃) web3.bzz - Swarm 去中心化存储(已废弃) web3.net - 网络相关功能 web3.contract - 智能合约交互 web3.…

训练检测之前的视频抽帧

接下来安装pytorch Previous PyTorch Versions 视频抽帧 import cv2def extract_frames(video_path, output_folder, frame_rate1):"""从视频中抽取帧。:param video_path: 视频文件的路径:param output_folder: 存储帧的文件夹路径:param frame_rate: 抽取的…

智能家居HA篇 二、配置Home Assistant并实现外部访问

智能家居HA篇 一、Win10 VM虚拟机安装 Home Assistant 手把手教学 二、通过Cpolar配置Home Assistant并实现外部访问 文章目录 智能家居HA篇前言一、内网穿透工具&#xff08;cpolar&#xff09;二、映射HA端口1.访问cpolar仪表2.创建账号并登录3.创建隧道 三、HA设置及公网访…

day09——Java基础项目(ATM系统)

文章目录 Java项目实战&#xff1a;手把手开发ATM银行系统&#xff08;附完整源码&#xff09;一、系统架构设计1. 三层架构模型2. 核心数据结构 二、核心功能实现1. 开户功能&#xff08;含唯一卡号生成&#xff09;2. 登录安全验证3. 存取款业务4. 安全转账实现 三、账户安全…

计算机网络:(五)信道复用技术,数字传输系统,宽带接入技术

计算机网络&#xff1a;&#xff08;五&#xff09;信道复用技术&#xff0c;数字传输系统&#xff0c;宽带接入技术 前言一、信道复用技术1. 为什么需要复用技术&#xff1f;2. 频分复用&#xff08;FDM&#xff09;3. 时分复用&#xff08;TDM&#xff09;4. 统计时分复用&am…

【期末总结】计算机网络

【期末总结】计算机网络 参考链接&#xff1a;计算机网络知识点全面总结&#xff08;有这一篇就够了&#xff01;&#xff01;&#xff01;&#xff09;-CSDN博客 一.概述 1.1 计算机网络的分类 按照网络的作用范围&#xff1a;广域网&#xff08;WAN&#xff09;、城域网&a…

React学习001-创建 React 应用

React学习001-创建 React 应用 1、安装node.js2、安装构建工具2.1 核心特性2.2 性能对比​​2.3 适用场景​​ 3、创建应用4、项目启动参考文章 1、安装node.js 这里建议安装nvm多版本管理node.js&#xff0c;想用哪个版本&#xff0c;一条命令即可~ 多版本管理node.js 2、安…

(cvpr2025) Adaptive Rectangular Convolution for Remote Sensing Pansharpening

论文&#xff1a;(cvpr2025) Adaptive Rectangular Convolution for Remote Sensing Pansharpening 代码&#xff1a;https://github.com/WangXueyang-uestc/ARConv.git 这个论文研究的是全色与多光谱图像的融合。作者认为现有的基于CNN的方法中&#xff0c;传统的卷积存在两个…

【图像处理入门】7. 特征描述子:从LBP到HOG的特征提取之道

摘要 特征描述子是图像处理中提取图像本质信息的关键工具。本文将深入讲解局部二值模式(LBP)与方向梯度直方图(HOG)两种经典特征描述子的原理、实现方法及应用场景。结合OpenCV代码示例,展示如何利用LBP提取纹理特征、使用HOG进行目标检测,帮助读者掌握从图像中提取有效…

AI 应用开发的‘核心枢纽’:Dify、Coze、n8n、FastGPT、MaxKB、RAGFlow 等六大平台全面对决

在人工智能与自动化流程日益普及的当下&#xff0c;各类平台如雨后春笋般涌现&#xff0c;成为构建智能应用与自动化工作流的 “核心枢纽”。其中&#xff0c;Dify、Coze、n8n、FastGPT、MaxKB、RAGFlow 备受瞩目&#xff0c;它们各自具备独特的功能与优势&#xff0c;适用于不…

RV1126+OPENCV对视频流单独进行视频膨胀/腐蚀操作

一.RV1126OPENCV对视频流进行视频膨胀操作的大体流程图 思路&#xff1a;初始化VI与VENC模块&#xff0c;之后开启两个线程&#xff0c;一个线程从VI模块获取视频流数据&#xff0c;用Opencv的Mat将其转成Mat矩阵之后进行用dilate膨胀&#xff0c;将膨胀之后的视频数据用send函…

Cordova + Vue 移动端视频播放组件(支持 HLS + 原生播放器兜底)

在混合 App 中&#xff0c;移动端使用 标签播放视频经常踩坑&#xff0c;尤其是格式兼容、跨域限制、WebView 差异等问题。 本文介绍一个通用的 Cordova 视频播放组件&#xff1a;优先 HTML5 播放&#xff0c;播放失败自动提示用户使用系统播放器&#xff0c;并支持原生插件兜底…

【Linux】掌握vim编译器使用——详细教程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、Vim的三种核心模式解析 二、高效编辑核心命令大全 1. 光标快速导航 2. 文本编辑四连击 3. 高效搜索替换 三、Vim神技&#xff1a;批量注释与多文件编辑 1. 批量…

Oracle迁移瀚高,如何做表等对象与文件名一对一的文件脚本(APP)

文章目录 环境文档用途详细信息 环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5 文档用途 Oracle迁移到瀚高后&#xff0c;需要整理一张表对应一个与表同名的脚本&#xff0c;一个函数对应一个与函数同名的脚本 详细信息 一、整理…