目錄
🧠 前言
🧾 我的需求
🔧 實現過程(按功能一步步來)
🚶?♂? 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 參數調整了實際迭代步數,以適配從已有圖像開始的優化過程。
正文開始:
🧾 我的需求
-
生成邏輯
-
輸入提示詞 → 點擊生成 → 得到一張圖像并保存,文件名按序遞增(如 image_001.png, image_002.png)
-
生成完成后暫停,等待下一次手動點擊“開始生成”。
-
-
交互界面
-
提供“清空提示詞”按鈕,方便輸入新提示詞。
-
歷史記錄列表顯示生成的圖像,格式為:
image_001.png 描述: A beautiful sunset
-
-
歷史管理
-
長提示詞能完整顯示,不被截斷。
-
每張圖片支持刪除功能,點擊圖片名稱后可刪除對應文件。
-
🔧 實現過程(按功能一步步來)
🚶?♂? 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 + 垃圾回收 |
? 最終效果展示
-
生成 + 保存
- 輸入提示詞,點擊“開始生成”,生成一張圖像并保存(如 image_001.png),完成后暫停。
- 清空提示詞后輸入新提示詞,點擊生成,保存為 image_002.png,文件名依次遞增。
-
歷史記錄顯示
-
歷史列表中,文件名和提示詞分兩行顯示,長提示詞自動換行,例如:
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
-
-
刪除功能完善
-
選中歷史中的圖片文件名,點“刪除”按鈕,會彈窗確認;
-
刪除后界面實時刷新,文件從磁盤也會消失;刪除后:
-
新生成的圖片也能正常刪了!刪除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+生成模型)。