Stable Diffusion+Pyqt5: 实现图像生成与管理界面(带保存 + 历史记录 + 删除功能)——我的实验记录(结尾附系统效果图)

目录

🧠 前言

🧾 我的需求

🔧 实现过程(按功能一步步来)

🚶‍♂️ Step 1:基本图像生成界面

🗃️ Step 2:保存图片并显示历史记录

📏 Step 3:优化长提示词显示和添加删除功能

🧹 Step 4:解决新生成图片删除失败

⚠️ 遇到的问题总结

✅ 最终效果展示

🧩 总结 & 收获


🧠 前言

本科课设做过一个人脸面部表情识别系统,当时是用Pyqt5库实现的图形可视化界面,最近我尝试结合 PyQt5 和 Stable Diffusion 模型开发一个图像生成工具,目标是实现一个带有图形界面的系统,能够输入提示词生成图像、自动保存并按序命名查看历史记录并支持删除功能。在开发过程中,我逐步实现了功能,也遇到了一些问题,最终在优化下达到了预期效果。本文将记录我的需求、实现过程、遇到的问题以及最终成果。
这一篇是在先前部署Stable Diffusion V1.5的基础上,加入了图形界面显示优化,部署可看从零搭建这篇,搭建完即可链接本篇:
深度学习项目记录·Stable Diffusion从零搭建、复现笔记-CSDN博客
从全灰到清晰图像:我的 Stable Diffusion 多尺度优化学习记录-CSDN博客
这里初始图像生成迭代步数我设置为30步,img2img优化迭代步数设置为50步



可能不好理解为什么实际运行生成图片过程中是15步?其实大概知道是微调受strength参数影响,50*0.3=15,这里我查了仔细解释,如下:
在 img2img(图像到图像)模式中,实际执行的迭代步数通常会受到 strength 参数的影响,而不是直接等于 num_inference_steps。
 

第一,strength 参数的作用

  • strength(这里是0.3)控制从初始图像 init_image 到生成新图像的“变化程度”。
  • 它的取值范围是0到1:
    • strength=0:完全保留初始图像,不做任何改变。
    • strength=1:完全忽略初始图像,等同于从头生成(text-to-image)。
    • strength=0.3:表示保留70%的初始图像特征,只对30%的内容进行调整。
       

第二,为什么是15步而不是50步?

  • 在 img2img 模式中,初始图像 init_image 已经提供了大部分结构,模型不需要从完全随机的噪声开始生成。因此,strength=0.3 表示只需要对图像进行轻微调整,实际迭代步数被缩减为 50 * 0.3 = 15。
  • 这是一个优化机制,避免浪费计算资源。
     

 第三,总结

  • 第一行(30/30):对应 pipe(prompt, ..., num_inference_steps=30),完整的30步生成。
  • 第二行(15/15):对应 img2img_pipe(..., num_inference_steps=50, strength=0.3),实际步数被 strength=0.3 缩减为15步。
  • 原因:img2img 模式的 strength 参数调整了实际迭代步数,以适配从已有图像开始的优化过程。

正文开始:

🧾 我的需求

  1. 生成逻辑

    • 输入提示词 → 点击生成 → 得到一张图像并保存,文件名按序递增(如 image_001.png, image_002.png)

    • 生成完成后暂停,等待下一次手动点击“开始生成”。

  2. 交互界面

    • 提供“清空提示词”按钮,方便输入新提示词。

    • 历史记录列表显示生成的图像,格式为:

      image_001.png  
      描述: A beautiful sunset
      
  3. 历史管理

    • 长提示词能完整显示,不被截断。

    • 每张图片支持删除功能,点击图片名称后可删除对应文件。


🔧 实现过程(按功能一步步来)


🚶‍♂️ Step 1:基本图像生成界面

目标:实现一个简单的 PyQt5 界面,能够输入提示词并生成图像,仅显示在界面上,不保存。

主要通过导入PyQt5库实现:

实现要点

  • 使用 StableDiffusionPipeline 和 StableDiffusionImg2ImgPipeline 生成图像。

  • 创建 PyQt5 界面,包含 QLineEdit(输入提示词)、QPushButton(开始生成)、QLabel(显示图像)

  • 将生成过程放入 QThread,通过信号机制 pyqtSignal将生成的图像传回主线程显示。(生成图像是个耗时操作,用 QThread 异步处理)

    用到的 Python 原理和技术

  • 多线程(QThread):避免生成图像时阻塞主界面,使用 pyqtSignal 传递结果。
  • GUI 布局(QVBoxLayout, QHBoxLayout):组织界面元素。
  • 图像处理(PIL 到 QPixmap):将生成的 PIL 图像转换为 PyQt5 可显示的格式。
class ImageGenerationThread(QThread):finished = pyqtSignal(object)def run(self):image = self.pipe(self.prompt).images[0]self.finished.emit(image)def generate_image(self):self.thread = ImageGenerationThread(prompt, save_path)self.thread.finished.connect(self.show_image)self.thread.start()def show_image(self, image):image.save("temp.png")pixmap = QPixmap("temp.png")self.image_label.setPixmap(pixmap)

🗃️ Step 2:保存图片并显示历史记录

目标:在生成图像后自动保存,并用列表显示图片名称和提示词,支持点击查看。

实现要点

  • 添加保存路径和文件名生成逻辑(image_001.png 等)。
  • 使用 QListWidget 显示历史记录,每张图片占两行:文件名和描述。
  • 实现点击列表项显示对应图像的功能。
     

    用到的 Python 原理和技术

  • 文件操作(os.path, os.makedirs):创建目录并保存图像。
  • 列表控件(QListWidget):存储和显示图片名及描述,使用 addItem 添加条目。
  • 字典(dict):用 self.history_data 存储文件名和提示词的映射,便于查看时获取描述。
  • 事件处理(itemClicked):绑定点击事件,加载并显示选中的图像。
def generate_image(self):filename = f"image_{self.count:03d}.png"save_path = os.path.join(self.save_dir, filename)self.thread = ImageGenerationThread(prompt, save_path)self.thread.finished.connect(self.on_generation_finished)def on_generation_finished(self, image, save_path, prompt):pixmap = QPixmap(save_path)self.image_label.setPixmap(pixmap)filename = os.path.basename(save_path)self.history_list.addItem(filename)self.history_list.addItem(f"描述: {prompt}")self.history_data[filename] = promptself.count += 1

📏 Step 3:优化长提示词显示和添加删除功能

目标:解决长提示词截断问题,并为每张图片添加删除按钮。

实现要点

  • 设置 QListWidget 宽度、开启自动换行;

  • 新增“删除图像”按钮,绑定删除逻辑;

  • 删除时移除对应列表项,弹确认框防误删。
     

    用到的 Python 原理和技术

  • 控件属性调整(setMaximumWidth, setWordWrap):增加宽度并启用自动换行。
  • 文件删除(os.remove):删除磁盘上的图片文件。
  • 列表操作(takeItem):从 QListWidget 中移除条目。
  • 对话框(QMessageBox):提供删除确认提示。
self.history_list.setMaximumWidth(400)
self.history_list.setWordWrap(True)def delete_selected_image(self):filename = self.history_list.selectedItems()[0].text()image_path = os.path.join(self.save_dir, filename)os.remove(image_path)row = self.history_list.row(selected_item)self.history_list.takeItem(row + 1)  # 描述self.history_list.takeItem(row)      # 文件名

🧹 Step 4:解决新生成图片删除失败

现象:已有图片可以删,但刚生成的删不了,会报错 [WinError 2]

原因:生成线程或图像显示时还占着这个文件的资源,没释放。

解决办法

  • 生成完成后手动释放图像对象;

  • 删除前清空当前显示的图像;

  • 加一波 gc.collect() 触发垃圾回收。
     

    用到的 Python 原理和技术

  • 垃圾回收(gc.collect):强制释放内存中的图像对象。
  • 资源管理(del, clear):显式删除图像对象并清除 QLabel 显示。
  • 异常处理(try-except):捕获删除时的错误并显示。
def on_generation_finished(self, image, save_path, prompt):# 显示图像 & 加入历史列表 ...del imagegc.collect()self.image_label.clear()def delete_selected_image(self):if self.image_label.pixmap():self.image_label.clear()os.remove(image_path)

⚠️ 遇到的问题总结

问题描述解决方法
❌ 无法保存图像最初没写保存逻辑手动加保存路径 + 文件名管理
❌ 提示词显示不完整长提示词在历史列表中只显示一行增加宽度 + 开启 setWordWrap(True)
❌ 新图片删不掉报错 [WinError 2] 文件被占用显式释放图像资源 + 清空 QLabel + 垃圾回收

最终效果展示

  1. 生成 + 保存

    • 输入提示词,点击“开始生成”,生成一张图像并保存(如 image_001.png),完成后暂停。
    • 清空提示词后输入新提示词,点击生成,保存为 image_002.png,文件名依次递增。
  2. 历史记录显示

    • 历史列表中,文件名和提示词分两行显示,长提示词自动换行,例如:

      image_001.png  
      描述: A beautiful sunset over the mountains with a long description that wraps
      image_002.png  
      描述: A cute kitten playing with a ball
      
  3. 删除功能完善

    • 选中历史中的图片文件名,点“删除”按钮,会弹窗确认;

    • 删除后界面实时刷新,文件从磁盘也会消失;删除后:

    • 新生成的图片也能正常删了!删除001.png

      下方显示已删除图像+图像名
      生成测试:This guy is wearing a short-sleeve T-shirt with pure color patterns. The T-shirt is with cotton fabric and its neckline is v-shape. The pants this guy wears is of long length. The pants are with cotton fabric and solid color patterns
      这位男士穿着一件纯色图案的短袖 T 恤。T恤为棉质面料,领口呈 V 形。这位男士穿着一条长裤。裤子为纯棉面料,纯色图案。
      效果一般,测试2:A little girl is sitting in front of a large painted rainbow .

      测试3:White dog playing with a red ball on the shore near the water .



      关闭系统界面,图像已保存到本地


🧩 总结 & 收获

这个小项目让我逐步实践了以下内容:

  • PyQt5 界面搭建;

  • 多线程 + 信号机制避免卡顿;

  • 图像处理、自动保存、文件管理;

  • 资源释放与垃圾回收(真香);

  • 控件交互与用户体验优化。

最关键的是,从一开始的功能设想到实际落地、再到解决 bug 完善体验,完整走了一遍闭环,非常适合练手或当项目展示。


如果你也想做个图像生成小工具类似系统,可以直接参考我这套逻辑(Pyqt5+生成模型)。

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

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

相关文章

量子计算未来的潜力和挑战

据麦肯锡预测,到 2035 年或 2040 年,量子计算市场规模可能增长至约 800 亿美元。目前,许多量子比特技术正竞相成为首台通用、无差错量子计算机的基础,但仍面临诸多挑战。 我们将探讨量子计算的未来前景、潜力,以及它对…

ArcGIS 给大面内小面字段赋值

文章目录 引言:地理数据处理中的自动化赋值为何重要?实现思路模型实现关键点效果实现步骤1、准备数据2、执行3、完成4、效果引言:地理数据处理中的自动化赋值为何重要? 在地理信息系统(GIS)的日常工作中,空间数据的属性字段赋值是高频且关键的操作,例如在土地利用规划…

如何打通虚拟化-容器环境并保障流量安全?SmartX VCCI 方案升级!

为了提升资源利用率、交付效率和业务灵活性,不少企业用户都在推进从传统架构向云原生架构的演进,并采用虚拟机与容器共存的混合模式支持多种业务系统。由于两个环境在业务交互层面形成高度耦合,企业需要具备简单、高效方案,实现虚…

stable diffusion 量化加速点

文章目录 一、导出为dynamic shape1)函数讲解(函数导出、输出检查)2)代码展示二、导出为static shape1)函数讲解(略)2)代码展示三、序列化为FP32测速1)测速2)代码四、序列化为FP16测速1)测速2)代码同上五、发现并解决解决CLIP FP16溢出,并测速1)如何找到溢出的算子…

7-openwrt-one通过web页面配置访客网络、无线中继等功能

前几个章节一直在介绍编译、分区之类的,都还没正常开始使用这个路由器的wifi。默认wifi是没有启动的,前面还是通过手动修改uci配置启动的,这个章节介绍下官方web页面的使用。特别是访客网络、无线中继 1、开启wifi,配置wifi基本信息 我们使用有线连接路由器,通过192.168.…

AcWing 6099. 座位

原题目链接 问题描述 有 n 头奶牛(n ≥ 5),编号为 1 ∼ n,按照某种顺序围着一张圆桌坐成一圈。 奶牛之间存在如下的朋友关系: 如果两头奶牛相邻,则它们是朋友;如果两头奶牛之间只隔着一头奶…

44、Spring Boot 详细讲义(一)

Spring Boot 详细讲义 目录 Spring Boot 简介Spring Boot 快速入门Spring Boot 核心功能Spring Boot 技术栈与集成Spring Boot 高级主题Spring Boot 项目实战Spring Boot 最佳实践总结 一、Spring Boot 简介 1. Spring Boot 概念和核心特点 1.1、什么是 Spring Boot&#…

配置mac mini M4 的一些软件

最近更换了 mac mini M4 ,想要重新下载配置软件 ,记录一下。 Homebrew是什么? homebrew是一款Mac OS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等功能。通过简单的指令可以实现包管理,而不用关心各种…

网络空间安全(54)CSRF

一、定义与原理 CSRF(Cross-Site Request Forgery),全称为跨站请求伪造,也被称为One Click Attack或Session Riding,缩写为CSRF或XSRF。它是一种网络安全漏洞,攻击者通过伪造用户的请求,利用用户…

分布式文件存储系统FastDFS

文章目录 1 分布式文件存储1_分布式文件存储的由来2_常见的分布式存储框架 2 FastDFS介绍3 FastDFS安装1_拉取镜像文件2_构建Tracker服务3_构建Storage服务4_测试图片上传 4 客户端操作1_Fastdfs-java-client2_文件上传3_文件下载4_获取文件信息5_问题 5 SpringBoot整合 1 分布…

安装了VM Tools,仍无法复制拖动-解决方案

今天在安装ubuntu时遇到了困扰许久的问题,安装了VM Tools,仍无法拖动主机文件到虚拟机,主要有两种原因并对应解决办法。 1.相关虚拟机设置选项卡中-客户机隔离-两个功能没有勾选 解决方案:勾选重启虚拟机即可 2.(这个…

Jmeter分布式测试启动

代理客户端配置 打开jmeter.properties文件,取消注释并设置端口(如server_port1099), 并添加server.rmi.ssl.disabletrue禁用SSL加密。 (Linux系统)修改jmeter-server文件中的RMI_HOST_DEF为代理机实际IP。…

火语言RPA--Oracle-导入数据表格

【组件功能】:导入特定的表格数据到包含同样字段的数据表 将表格对象数据通过数据库操作对象导入到指定数据库。 配置预览 配置说明 源表格 表格来源有“来自表格对象”和“来自表达式”2种,表达式支持DataTable类型变量。 对象 对应来自表格对象&…

Java的Selenium的特殊元素操作与定位之验证码

1.使用OCR技术识别验证 步骤: 截取整个网页的截图。 定位验证码图片元素。 根据验证码图片的位置和大小,从截图中裁剪出验证码图片。 使用OCR工具(如Tesseract)识别验证码图片中的文本。 2.手动处理验证码 步骤:…

OpenStack Yoga版安装笔记(十七)安全组笔记

一、安全组与iptables的关系 OpenStack的安全组(Security Group)默认是通过Linux的iptables实现的。以下是其主要实现原理和机制: 安全组与iptables的关系 OpenStack的安全组规则通过iptables的规则链实现。每条安全组规则会被转换为相应的i…

starrocks split函数和trino split函数差异性

在trino419和starrocks3.2.8中分别执行下面这两条sql,出来的结果是不一样的 select split(,,,)[1] as t1 select coalesce(split(,,&#

Spring Data JPA中的List底层:深入解析ArrayList的奥秘!!!

&#x1f31f; Spring Data JPA中的List底层&#xff1a;深入解析ArrayList的奥秘 &#x1f4a1; 你是否好奇过&#xff0c;为什么Spring Data JPA的查询方法返回的List<T>总是默认为ArrayList&#xff1f;本文将通过技术原理解析、验证实验和性能优化指南&#xff0c;为…

腾讯云智测试开发面经

1、投递时间线 2.20投递简历,3.11第一轮面试,3.30第二轮面试,4.4第三轮面试,4.10第四轮面试,4.11offer意向书 2、第一轮面试 第一轮面试技术面,面试官是导师,面试时长40多分钟 1)自我介绍 2)数组和列表的区别 3)了解哪些数据库 4)进程和线程的区别 5)了解哪…

【深度学习】【目标检测】【Ultralytics-YOLO系列】YOLOV3源码整体结构解析

【深度学习】【目标检测】【Ultralytics-YOLO系列】YOLOV3源码整体结构解析 文章目录 【深度学习】【目标检测】【Ultralytics-YOLO系列】YOLOV3源码整体结构解析前言代码结构整体data文件结构模型训练超参数配置文件解析数据集配置文件解析 models文件结构utils文件结构runs文…

Python常用排序算法

1. 冒泡排序 冒泡排序是一种简单的排序算法&#xff0c;它重复地遍历要排序的列表&#xff0c;比较相邻的元素&#xff0c;如果他们的顺序错误就交换他们。 def bubble_sort(arr):# 遍历所有数组元素for i in range(len(arr)):# 最后i个元素是已经排序好的for j in range(0, …