基于Tensorflow.js的花卉识别编程实践
使用TensorFlow.js进行编程有许多优点,特别适合开发机器学习和深度学习的应用。TensorFlow.js可以直接在浏览器中运行,无需服务器或特殊环境配置。这使得开发者可以轻松地创建和部署基于Web的机器学习应用。TensorFlow.js提供了许多预训练模型,开发者可以直接使用这些模型进行各种任务,如图像分类、物体检测、自然语言处理等,减少了从头开始训练模型的时间和资源。
使用Tensorflow.js,所有的计算都在本地进行,数据不会被发送到服务器,有助于保护用户的隐私。由于数据不离开用户的设备,减少了数据泄露的风险。
下面以基于TensorFlow.js的花卉识别为例,讲述编程涉及的关键问题。
主要功能是:
(1)用户选择图片。
(2)选择花朵的2种方式:
- 双击鼠标:已鼠标位置为中心,固定大小的矩形框确定花朵区域。
- 拖动鼠标框选花朵区域。
(3)裁剪图片,获取花朵图片,识别花朵类别,显示识别结果。
深度学习的训练模型需要保存为SavedModel的模型,再转换为Tensorflow.js格式模型(1个json文件+几个bin文件)。
下面对关键的代码进行说明。
index.html中需要加载Tensorflow.js库
<!-- Import TensorFlow.js library -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
花卉识别的主要功能均在ai_flower.js实现,下面介绍其中的关键函数。
加载模型和标签
loadModel()
:异步加载TensorFlow模型。加载成功后更新页面提示。
async function loadModel() {try {// 加载Savedmodel转换模型model = await tf.loadGraphModel(modelUrl);document.getElementById('result').innerText = "Model loaded successfully.";// console.log("Model input:", model.input)// console.log(model.summary())} catch (error) {console.error("Error loading model:", error);document.getElementById('predict').disabled = true;document.getElementById('result').innerText = "Error loading model.";}
}
loadLabelMap(label_map_path, label_encn_path)
:异步加载类别标签和英文-中文对照表。
async function loadLabelMap(label_map_path, label_encn_path){try {// 获取标签类别名称const responseMap = await fetch(label_map_path);labelMap = await responseMap.json();const responseEncn = await fetch(label_encn_path);leabelEnCn = await responseEncn.json();} catch (error) {console.error("加载类别标签失败:", error);document.getElementById('result').innerText = "加载类别标签失败。";}
}
处理图片上传
handleImageUpload(event)
:处理图片选择事件,清空上次识别结果,加载新的图片并显示在页面上。同时创建临时图像对象获取原始图像尺寸,并更新覆盖画布的尺寸和位置。
function handleImageUpload(event) {// 清空上次识别结果document.getElementById('result').innerText = "";document.getElementById('promptText').innerText = "";// 清除之前的裁剪图片const croppedImgElement = document.getElementById('croppedImage');croppedImgElement.src = '';croppedImgElement.style.display = 'none';// 清除虚线框overlay.style.display = 'none';// 禁用预测按钮document.getElementById('predict').disabled = true; // 未确定矩形框isSelectRect = false;// 获取 imgElementconst imgElement = document.getElementById('image');imgElement.style.display = 'none';// 清除之前的虚线框const overlayCanvas = document.getElementById('overlayCanvas');overlayCanvas.style.display = 'none';// 加载图片const file = event.target.files[0];const reader = new FileReader();reader.onload = function(e) {const imgElement = document.getElementById('image');imgElement.src = e.target.result;imgElement.style.display = 'block';// 创建一个临时的 Image 对象来获取原始尺寸const tempImg = new Image();tempImg.onload = function() {// 保存原始图像数据和尺寸const canvas = document.createElement('canvas');canvas.width = tempImg.naturalWidth;canvas.height = tempImg.naturalHeight;const ctx = canvas.getContext('2d');ctx.drawImage(tempImg, 0, 0);imgElement.dataset.originalImage = canvas.toDataURL();imgElement.dataset.naturalWidth = tempImg.naturalWidth;imgElement.dataset.naturalHeight = tempImg.naturalHeight;// 更新 overlayCanvas 尺寸和位置const overlayCanvas = document.getElementById('overlayCanvas');overlayCanvas.width = tempImg.naturalWidth;overlayCanvas.height = tempImg.naturalHeight;overlayCanvas.style.display = 'block';// 确保 canvas 与 imgElement 对齐const imgRect = imgElement.getBoundingClientRect();overlayCanvas.style.position = 'absolute';overlayCanvas.style.left = `${imgRect.left}px`;overlayCanvas.style.top = `${imgRect.top}px`;// 添加提示文本document.getElementById('promptText').innerText = '请拖动鼠标,框选花朵。';};tempImg.src = e.target.result;};reader.readAsDataURL(file);
}
裁剪并显示图像
cropImage(cropStartX, cropStartY, cropEndX, cropEndY)
:根据用户拖动的矩形框或双击鼠标确定的矩形框,裁剪图像,并显示裁剪后的图像(方便调试)。裁剪区域相对于原始图像进行计算,以确保裁剪的准确性。
function cropImage(cropStartX, cropStartY, cropEndX, cropEndY) {const imgElement = document.getElementById('image');const originalImageData = imgElement.dataset.originalImage;const naturalWidth = parseInt(imgElement.dataset.naturalWidth, 10);const naturalHeight = parseInt(imgElement.dataset.naturalHeight, 10);// Get the image's bounding rectangleconst imgRect = imgElement.getBoundingClientRect();// Calculate the scaling factorsconst scaleX = naturalWidth / imgElement.width;const scaleY = naturalHeight / imgElement.height;// Convert displayed coordinates to original image coordinatesconst sx = cropStartX * scaleX;const sy = cropStartY * scaleY;const ex = cropEndX * scaleX;const ey = cropEndY * scaleY;const width = ex - sx;const height = ey - sy;// Ensure the crop area is within image boundsconst adjustedStartX = Math.max(0, Math.min(sx, naturalWidth - width));const adjustedStartY = Math.max(0, Math.min(sy, naturalHeight - height));const canvas = document.createElement('canvas');const ctx = canvas.getContext('2d');// Set canvas dimensionscanvas.width = cropEndX - cropStartX;canvas.height = cropEndY - cropStartY;// Create a temporary image object to load the original image dataconst tempImg = new Image();tempImg.onload = function() {// Draw the cropped area on the canvasctx.drawImage(tempImg, adjustedStartX, adjustedStartY, width, height, 0, 0, canvas.width, canvas.height);const croppedImgElement = document.getElementById('croppedImage');croppedImgElement.src = canvas.toDataURL();croppedImgElement.style.display = 'block';};tempImg.src = originalImageData;document.getElementById('predict').disabled = false; // 启用预测按钮document.getElementById('promptText').innerText = ''}
预测图像
predictImage()
:使用加载的模型对裁剪后的图像进行预测。获取图像元素并将其转换为TensorFlow张量。调整图像大小,归一化并添加批量维度后进行预测。解析预测结果并显示前k个预测结果。
async function predictImage() {if (!model) {document.getElementById('result').innerText = "模型尚未加载。";return;}if (!isSelectRect) {document.getElementById('result').innerText = "请拖动鼠标,框选花朵。";return;}// 获取图像元素const imgElement = document.getElementById('croppedImage');// 从图像元素创建张量const tensorImg = tf.browser.fromPixels(imgElement).toFloat();// 调整为模型需要的输入大小const resizedImg = tf.image.resizeBilinear(tensorImg, [224, 224]); // 归一化图像const normalizedImg = resizedImg.div(255.0);// 添加批量维度const batchedImg = normalizedImg.expandDims(0);// 进行预测let predictions;try {predictions = await model.execute(batchedImg);} catch (error) {console.error("模型预测失败:", error);document.getElementById('result').innerText = "模型预测失败。";return;}// 检查 predictions 是否有效if (!predictions || Array.isArray(predictions) && predictions.length === 0) {console.error("模型预测返回了无效的输出。");document.getElementById('result').innerText = "模型预测返回了无效的输出。";return;}// 获取第一个 Tensor 作为输出// 实际不是数组const outputTensor = Array.isArray(predictions) ? predictions[0] : predictions;if (!outputTensor) {console.error("输出 Tensor 未定义");document.getElementById('result').innerText = "输出 Tensor 未定义。";return;}// 将 Tensor 转换为数组let probabilities;try {probabilities = await outputTensor.data(); // 使用 .data() 而不是 .array()// console.log("probabilities:")// console.log(probabilities)} catch (error) {console.error("Tensor 转换为数组失败:", error);document.getElementById('result').innerText = "Tensor 转换为数组失败。";return;}// 获取 top-k 预测const topKIndices = Array.from(probabilities).map((prob, index) => ({prob, index})).sort((a, b) => b.prob - a.prob).slice(0, topK).map(item => item.index);const topKProbabilities = topKIndices.map(index => probabilities[index]);// console.log(topKIndices)// console.log(topKProbabilities)// 显示 top-k 预测结果, label编号从1开始(index+1),不是从0开始let resultText = "";topKIndices.forEach((index, i) => {const className = labelMap[index + 1] || "Unknown"; // 从字典中获取类别名称const cnName = leabelEnCn[className];const probability = topKProbabilities[i];resultText += `${className}-${cnName}: ${probability.toFixed(4)}\n`; // 保留四位小数});document.getElementById('result').innerText = resultText;
}
下载完整源代码
相关文章:
基于Tensorflow.js的花卉识别编程实践
使用TensorFlow.js进行编程有许多优点,特别适合开发机器学习和深度学习的应用。TensorFlow.js可以直接在浏览器中运行,无需服务器或特殊环境配置。这使得开发者可以轻松地创建和部署基于Web的机器学习应用。TensorFlow.js提供了许多预训练模型࿰…...
繁简之争:为什么手机芯片都是 ARM
RISC 和 CISC 指令集 之前的文章《揭秘 CPU 是如何执行计算机指令的》中说到,如果从软件的角度来讲,CPU 就是一个执行各种计算机指令(Instruction Code)的逻辑机器。 计算机指令集是计算机指令的集合,包括各种类型的…...
《机器人SLAM导航核心技术与实战》第1季:第8章_激光SLAM系统
视频讲解 【第1季】8.第8章_激光SLAM系统-视频讲解【第1季】8.1.第8章_激光SLAM系统_Gmapping算法-视频讲解【第1季】8.2.第8章_激光SLAM系统_Cartographer算法-视频讲解【第1季】8.3.第8章_激光SLAM系统_LOAM算法-视频讲解 第1季:第8章_激光SLAM系统 先 导 课第…...
Qt之Gui
组件依赖关系 应用 #mermaid-svg-GADicZtZJRVVUeiF {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GADicZtZJRVVUeiF .error-icon{fill:#552222;}#mermaid-svg-GADicZtZJRVVUeiF .error-text{fill:#552222;stroke:#…...
Redis的回收策略(淘汰策略)
volatile-lru :从已设置过期时间的数据集( server.db[i].expires )中挑选最近最少使用的数据淘汰 volatile-ttl : 从已设置过期时间的数据集( server.db[i].expires ) 中挑选将要过期的数据淘汰 volatile…...
基于Springboot的个人博客系统
文章目录 介绍访问地址一、功能展示1.前台首页归档相册留言关于我登陆注册 2.后台管理系统登陆页面首页文章管理相册管理写博客访客统计 介绍 基于Java(Springboot)可以用做毕业设计的个人博客系统,包括网站前台和后台管理系统两部分。网站前…...
J030_TCP通信
一、需求描述 使用TCP协议进行通信 1.1 一发一收 1.1.1 Client package com.itheima.tcp1;import java.io.DataOutputStream; import java.io.OutputStream; import java.net.Socket;public class Client {public static void main(String[] args) throws Exception {//1、…...
单片机复习题
第1章 思考题及习题 一、填空 1. 除了单片机这一名称之外,单片机还可称为 或 。 2.单片机与普通微型计算机的不同之处在于其将 、 、和 三部分,通过内部 连接在一起,集成于一块芯片上。 …...
Java | Leetcode Java题解之第322题零钱兑换
题目: 题解: public class Solution {public int coinChange(int[] coins, int amount) {int max amount 1;int[] dp new int[amount 1];Arrays.fill(dp, max);dp[0] 0;for (int i 1; i < amount; i) {for (int j 0; j < coins.length; j)…...
【MYSQL】MYSQL逻辑架构
mysql逻辑架构分为3层 mysql逻辑架构分为3层 1). 连接层:主要完成一些类似连接处理,授权认证及相关的安全方案。 2). 服务层:在 MySQL据库系统处理底层数据之前的所有工作都是在这一层完成的,包括权限判断,SQL接口&…...
SQL Server数据库的清洁工:垃圾回收机制解析
SQL Server数据库的清洁工:垃圾回收机制解析 在SQL Server的复杂而精密的数据库管理系统中,垃圾回收机制扮演着至关重要的角色。它负责清理不再需要的数据,释放空间供新数据使用。本文将深入探讨SQL Server中数据库垃圾回收机制的工作原理&a…...
使用MailKit在.NET Core中收发邮件的完整示例
在.NET Core中处理邮件收发操作时,MailKit是一个非常强大的库。它支持SMTP、POP3、IMAP等多种协议,可以轻松实现发送、接收、以及管理邮件的功能。下面我们将通过一个详细的示例,展示如何在.NET Core项目中使用MailKit来收发邮件,…...
flask高频面试题
目录 高频面试题及答案1. 如何在Flask中处理数据库迁移?2. Flask如何处理文件上传?3. 如何在Flask中处理跨域请求(CORS)?4. 如何在Flask中实现用户认证?5. Flask如何处理会话?6. Flask如何处理表…...
【算法模板】图论:Tarjan算法求割边割点
概念 割边(Bridge 或 Cut Edge) 定义: 在一个无向连通图中,如果删除某条边后,图不再连通(即任意两点之间不能相互到达),则称该边为割边。割边也被称为桥,因为它像桥梁…...
python——joblib进行缓存记忆化-对计算结果缓存
问题场景 在前端多选框需要选取多个数据进行后端计算。 传入后端是多个数据包的对应路径。 这些数据包需要按一定顺序运行,通过一个Bag(path).get_start_time() 可以获得一个float时间值进行排序,但由于数据包的特性,这一操作很占用性能和时…...
01 计算机系统基础-2
操作系统 进程管理 进程管理是操作系统的核心,但如果设计不当,就会出现死锁的问题。如果一个进程在等待一件不可能发生的事,则进程就死锁了。而如果一个或多个进程产生死锁,就会造成系统死锁。基于死锁产生机制及解决方案&#…...
2024死磕小红书,一定能赚到钱!
2024死磕小红书,一定能赚到钱!在文末领取小红书运营完全指南电子书 从2023年起,小红书这股热乎劲儿就像开了挂,突然间就成了人人想蹭的“显学”。大伙儿都想趁着平台红利期,分一杯羹。说来惭愧,我从2020年…...
关于JS中的AO对象
在JavaScript中,AO对象(Activation Object,激活对象)是与函数执行上下文相关的概念。每当一个函数被调用时,都会创建一个新的执行上下文,这个执行上下文包含一个AO对象,用于存储在函数执行期间创…...
49 序列解包的多种形式和用法
序列解包(Sequence Unpacking)是 Python 中非常重要和常用的一个功能,可以使用非常简洁的形式完成复杂的功能,提高了代码的可读性,减少了程序员的代码输入量。 x, y, z 1, 2, 3 # 多个变量同时赋值 v_tuple (False…...
2-55 基于matlab的 永磁同步电机滑膜观测器估算电机转速
基于matlab的 永磁同步电机滑膜观测器估算电机转速。精度比传统观测器精度高。分别输出电机转速估计值与实际值、电机转速估计误差、电机转子位置估计值与实际值、电机转子位置估计误差。程序已调通,可直接运行。 2-55滑膜观测器估算电机转速 - 小红书 (xiaohongsh…...
手机在网状态接口如何对接?(二)
一、什么是手机在网状态? 传入手机号码,查询该手机号的在网状态,返回内容有正常使用、停机、在网但不可用、不在网(销号/未启用/异常)、预销户等多种状态。 二、手机在网状态使用场景? 1.用户验证与联系…...
红黑树实现详解
实践意义 在各方面,红黑树要比AVL树性能更好,用途也更广泛 map&set底层都主要靠红黑树 概念 性质 插入时,抽象图 cur为新插入 插入时颜色更新逻辑图 板书...
计算机基础(Windows 10+Office 2016)教程 —— 第5章 文档编辑软件Word 2016(上)
第5章 文档编辑软件Word 2016 5.1 Word 2016入门5.1.1 Word 2016 简介5.1.2 Word 2016 的启动5.1.3 Word 2016 的窗口组成5.1.4 Word 2016 的视图方式5.1.5 Word 2016 的文档操作5.1.6 Word 2016 的退出 5.2 Word 2016的文本编辑5.2.1 输入文本5.2.3 插入与删除文本5.2.4 复制与…...
知识库、微调、AI Agent
Agent执行工作的过程是需要大模型来配合的,大模型充当一个大脑,给Agent下达指令。Agent当接收到这个指令的时候,然后去执行。 大模型参数的数量直接影响大模型的生成能力和推理能力,也直接影响了大模型的使用效果。参数越多&…...
目标检测——YOLOv10: Real-Time End-to-End Object Detection
YOLOv10是在YOLOv8的基础上,借鉴了RT-DETR的一些创新点改进出来的 标题:YOLOv10: Real-Time End-to-End Object Detection论文:https://arxiv.org/pdf/2405.14458源码:https://github.com/THU-MIG/yolov10 1. 论文介绍 在过去的几…...
算法训练,项目
一.木材加工 题解: 二分答案,左边0,右边可以为最长的木头,但我直接赋值了一个很大的值,进行二分,随后写个check;内部遍历木头截取为mid木块的个数,要是>k,满足要求,还…...
foreach循环和for循环在PHP中各有什么优势
在PHP中,foreach循环和for循环都是用来遍历数组的常用结构,但它们各有其优势和使用场景。 foreach循环的优势 简化代码:foreach循环提供了一种更简洁的方式来遍历数组,不需要手动控制索引或指针。易于阅读:对于简单的…...
html制作卡通图案代码,使用HTML和CSS3绘制基本卡通图案的示例分享
一、👨🎓网站题目 卡通网站的设计与制作。 二、✍️网站描述 - 卡通kitty猫主题的网页 一共八个页面 - kitty猫网页使用html css js制作 有banana图 - 页面可以相互跳转 包含表单 三级页面 - 网页可以使用vscode hbuilder dw等打开修改 - 里面的图片和…...
如何快速看完一个网页上的视频
如何快速看完一个视频 懂的都懂。 Edge浏览器 添加下面两个书签: javascript:document.querySelector("video").dispatchEvent(new Event("ended"))javascript:var vdocument.querySelector("video");if(v){v.mutedtrue;v.playba…...
Tensorflow训练视觉模型(CPU)
目录 零、模型下载 一、清理C盘 二、 配置环境 三、运行项目前提操作 (1)根据自己的项目设置路径。每次激活虚拟环境(tensorflow115)都得重设一次 (2)执行setup 这个项目的路径移动了位置也需要重设一…...
一种估计贝叶斯检索问题后验分布的神经网络方法
介绍 提出了一种基于神经网络的方法,即分位数回归神经网络 (QRNN),作为估计贝叶斯遥感检索后验分布的新方法。与传统的神经网络检索相比,QRNN 的优势在于它们不仅学会预测单个检索值,还学会预测相关的、特定案例的不确定性。在本…...
递归实现模拟汉诺塔
题目描述 用递归算法模拟汉诺塔 有三根杆子X,Y,Z X杆上有N个穿孔圆盘 , 盘的尺寸由下到上依次变小 要求按下列规则将所有圆盘移至Y杆: 每次只能移动一个圆盘 大盘不能叠在小盘上面 递归思想(为降低题目难度,公开递归思想): 将X杆上的n−1个圆盘都…...
@Component 注解高端玩法【策略模式】
优质博文:IT-BLOG-CN 在Spring框架中,Component注解本身并不支持直接通过注解参数来定义一个key值。不过,你可以通过自定义注解和Qualifier注解来实现类似的功能。 以下是一个示例,展示如何通过自定义注解和Qualifier来实现将不同…...
Java常用类和数据结构与算法
1. 其他常用类 1.1. Math类 java.lang.Math提供了一系列静态方法用于科学计算;其方法的参数和返回值一般为double型。如果需要更加强大的数学运算能力,可以使用apache commons下面的Math类库 public class TestMath {public static void main(String[…...
canal同步es数据到mysql
参考地址 碰到的bug 第一次按照启动canal.adapter的时候报错,经典空指针异常,代码如下: 2024-08-03 22:29:11.937 [pool-3-thread-1] ERROR c.a.otter.canal.adapter.launcher.loader.AdapterProcessor - java.lang.NullPointerException ja…...
【Python机器学习】支持向量机——在复杂数据上应用核函数
上图中,数据中存在某种可以识别的模式,其中一个问题就是:我们能否想线性情况一样,利用强大的工具来捕捉数据中的这种模式? 利用核函数将数据映射到高维空间 在上图中,数据点处于一个圆中,人类…...
机器学习 第9章-聚类
机器学习 第9章-聚类 9.1 聚类任务 在“无监督学习”(unsupervised learning)中,训练样本的标记信息是未知的,目标是通过对无标记训练样本的学习来揭示数据的内在性质及规律,为进一步的数据分析提供基础。此类学习任务中研究最多、应用最广…...
qcom ucsi probe
ucsi glink 注册一个ucsi 设备,和pmic glink进行通信,ucsi作为pmic glink的一个client。 lkml的patch https://lkml.org/lkml/2023/1/30/233 dtsi中一般会定义 qcom,ucsi-glink 信息,用于和驱动进行匹配 static const struct of_device_id …...
目标检测——X光安检数据集
1. OPIXray数据集(2020) 2. HIXray数据集(2021) 3. SIXray数据集(2019) 4. CLCXray数据集(2022) 5. PIDray数据集(2021) 6. GDXray数据集(20…...
C++客户端Qt开发——多线程编程(二)
多线程编程(二) ③线程池 Qt中线程池的使用 | 爱编程的大丙 1>线程池 我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行…...
python3 pyside6图形库学习笔记及实践(四)
目录 前言列表控件(QListWidget)创建列表增删插改查添加元素插入元素删除元素修改元素查找元素 常用信号和槽currentItemChangeditemChangedclear 列表排序列表的上下文菜单 图形视图框架简介框架核心图元类(QGraphicsItem)场景类(QGraphicsScene)视图类(QGraphicsView)交互机制…...
HCIE-Datacom题库__填空题
1.NETCONE是一种基于()的网络配置协议,它存在的目的在于用可编程的方式实现网络配置的自动化,从而简化并加速网络服务地部署。(英文缩写,全大写) XML 解析:NETCONF是基于可扩展标记语言XML(Ex…...
【java基础】徒手写Hello, World!程序
文章目录 前提:java环境变量配置使用vscode编写helloworld解析 前提:java环境变量配置 https://blog.csdn.net/xzzteach/article/details/140869188 使用vscode编写helloworld code .为什么用code看下图 报错了!!!&…...
【AI大模型】大模型应用开发学习线路
文章目录 壹. LLM基础一. 数学基础二. Python库的学习三. 深度学习基础四. 自然语言处理(NLP) 贰. LLM应用工程一. 部署和调用LLMLLM API之openAI提示工程构建应用程序其他 二. 构建向量存储三. (RAG)检索增强生成四. 高级 RAG五.…...
EasyAR_稠密空间图
EasyAR稠密空间图 1.稠密空间图 EasyAR稠密空间地图利用RGB相机图像对周围环境进行三维稠密重建,得到稠密的点云地图和网格地图。利用稠密空间地图让虚拟物体更好的融入真实环境之中,用以实现真实物体和虚拟物体正确遮挡、碰撞等AR应用。 2.在Unity中的…...
VS Code 和 Visual Studio 哪个更好
文章目录 VS Code 和 Visual Studio 哪个更好Visual Studio Code简介Visual Studio简介相同点差异点总结 VS Code 和 Visual Studio 哪个更好 Visual Studio Code简介 Visual Studio Code(简称 VS Code)是一款开源的、免费的、跨平台的、轻量级的代码编…...
(二)测试工具
16. 如何进行浏览器兼容性测试? 正确回答通过率:38.0%[ 详情 ] 推荐指数: ★★★★★ 试题难度: 高难 1、兼容性测试含义 兼容性测试是指要测试的软件在不同的硬件平台上、不同的应用软件之间、不同的操作系统中、不同的网络环境…...
设计模式实战:报表生成系统的设计与实现
问题描述 设计一个报表生成系统,支持不同类型的报表生成(如PDF报表、Excel报表),提供生成报表的模板方法,并能够选择不同的生成策略(如完整报表、简易报表)。系统需要确保报表生成过程的灵活性和可扩展性。 设计分析 建造者模式 建造者模式用于创建复杂对象的构建过…...
flutter开发windows应用程序(.exe)配置环境搭建 以及 visual studio配置flutter windows所需环境
flutter 开发 windows 应用程序(.exe)配置环境搭建 安装 android studio 开发工具 并配置相关所需环境 参考该文章:我还没写完你上百度上搜一下吧我帮你付钱了😂 控制台运行 flutter doctor 命令 查看当前 flutter 开发环境 报…...
驾驭PyCharm:破解环境配置的迷宫
驾驭PyCharm:破解环境配置的迷宫 PyCharm,作为Python开发者的首选IDE之一,以其强大的功能和用户友好的界面而广受好评。然而,即便是最强大的工具,环境配置问题也可能成为开发者的拦路虎。本文将带你深入探索PyCharm中…...