基于深度學習的火災智能檢測系統設計與實現

在各類安全事故中,火災因其突發性強、破壞力大,一直是威脅人們生命財產安全的重大隱患。傳統的火災檢測方式多依賴煙霧傳感器、溫度傳感器等,存在響應滯后、易受環境干擾等問題。隨著深度學習技術的飛速發展,基于計算機視覺的火災檢測方法憑借其實時性強、檢測范圍廣等優勢,逐漸成為研究熱點。本文將簡單介紹一款基于深度學習的火災智能檢測系統的設計與實現過程。

一、系統整體設計

本火災智能檢測系統旨在通過深度學習技術實現對火災的精準、快速響應,并及時發出報警信息。系統主要由數據采集模塊、模型訓練模塊、火災檢測模塊、報警模塊和可視化界面模塊五部分組成,各模塊協同工作,形成一個完整的火災檢測閉環。

數據采集模塊負責獲取監測場景的圖像或視頻數據,支持攝像頭實時拍攝、視頻文件導入以及靜態圖片上傳三種方式,確保系統能適應不同的應用場景。模型訓練模塊是系統的核心,通過大量的火災圖像數據訓練深度學習模型,使其具備準確識別火焰的能力。火災檢測模塊利用訓練好的模型對采集到的數據進行分析處理,判斷是否存在火情。一旦檢測到火災,報警模塊立即觸發語音報警,連續播報三次 “發生火情”,同時可視化界面模塊會展示檢測結果,包括火情位置、檢測時間等信息,方便用戶及時了解情況并采取相應措施。

二、關鍵技術實現

(一)數據集構建

高質量的數據集是訓練出高性能深度學習模型的基礎。為了讓模型能夠準確識別不同場景、不同光照條件下的火焰,我們收集了大量的火災圖像數據,包括從公開數據集(如 FIRE-SMOKE Dataset、UCF101 中的火災相關視頻幀)中獲取的樣本,以及通過模擬火災場景拍攝的圖像。

在數據預處理階段,我們對收集到的圖像進行了一系列操作。首先進行圖像去噪,去除圖像中的噪聲干擾,提高圖像質量;然后進行尺寸統一,將所有圖像調整為固定的尺寸(如 224×224),以便輸入到模型中進行訓練;最后進行數據增強,通過旋轉、縮放、翻轉、亮度調整等方式擴充數據集,增加樣本的多樣性,避免模型過擬合。

(二)深度學習模型選擇與訓練

經過對比多種深度學習模型在火災檢測任務上的性能,我們最終選擇了YOLOv8?模型作為本系統的核心檢測模型。YOLOv8?模型具有檢測速度快、精度高的特點,能夠滿足實時檢測的需求。、

【從訓練集劃分驗證集】
import shutil
import osdef handle(src_detect_dir, dst_detect_dir, freq=5):src_detect_images_dir = os.path.join(src_detect_dir, "images")src_detect_labels_dir = os.path.join(src_detect_dir, "labels")dst_detect_images_dir = os.path.join(dst_detect_dir, "images")dst_detect_labels_dir = os.path.join(dst_detect_dir, "labels")if not os.path.exists(dst_detect_images_dir):os.makedirs(dst_detect_images_dir)if not os.path.exists(dst_detect_labels_dir):os.makedirs(dst_detect_labels_dir)i = 0filenames = os.listdir(src_detect_images_dir)print(len(filenames),filenames)for filename in filenames:if i % freq == 0:name = Nonenames = filename.split(".")if len(names) == 2:name = names[0]print("parse1:", len(names), "name=", name)else:if filename.endswith(".jpg"):name = filename[0:-4]print("parse2:", len(names), "name=", name)if name:src_image_path = os.path.join(src_detect_images_dir, name+".jpg")src_label_path = os.path.join(src_detect_labels_dir, name+".txt")dst_image_path = os.path.join(dst_detect_images_dir, name+".jpg")dst_label_path = os.path.join(dst_detect_labels_dir, name+".txt")try:shutil.copyfile(src_image_path, dst_image_path)shutil.copyfile(src_label_path, dst_label_path)print("--------%d---------" % i)print("src_image_path=", src_image_path)print("src_label_path=", src_label_path)os.remove(src_image_path)os.remove(src_label_path)except Exception as e:print("copy失敗:",e,src_image_path)try:os.remove(src_image_path)except: passtry:os.remove(src_label_path)except: passtry:os.remove(dst_image_path)except: passtry:os.remove(dst_label_path)except: passelse:print("filename=%s format error" % str(filename))i += 1if __name__ == '__main__':# 將訓練樣本按照指定頻率拆分一部分到測試樣本handle(src_detect_dir=r"C:\Users\dell\OneDrive\桌面\Project_YOLO\yolo_dataset\all_train\train",dst_detect_dir=r"C:\Users\dell\OneDrive\桌面\Project_YOLO\yolo_dataset\all_train\valid",freq=10 #表示每10張拆一張)

在模型訓練過程中,我們使用 PyTorch 框架搭建訓練環境。首先將數據集按照 8:2 的比例劃分為訓練集和驗證集,訓練集用于模型參數的學習,驗證集用于評估模型的性能并調整超參數。設置初始學習率為 0.001,采用 SGD 優化器,訓練輪次為 100 輪。在訓練過程中,通過監控損失函數的變化和驗證集上的準確率,適時調整學習率和其他超參數,以提高模型的性能。

【模型訓練】
from ultralytics import YOLO
# 1.加載訓練路徑
data_path = './signs.yaml'# 加載預訓練模型
# model = YOLO('./yolov8n.pt')  # 模型文件路徑
model = YOLO('./runs/detect/train/weights/last.pt')
# 配置模型的訓練參數
model.train(data=data_path, #指定訓練數據的配置文件路徑epochs=300, #訓練總次數    要300次以上imgsz=640,  #輸入模型的圖片尺寸batch=2,  #此時每一輪10圖片分成5次 每次讓你訓練2次 每兩張結束 拿驗證集驗證一下device='cpu', # 設備workers=0,  #模型訓練的線程patience=30,resume=True
)
print("訓練完成")

訓練完成后,對模型進行評估,主要評估指標包括準確率、召回率和 F1 值。經過多次訓練和優化,模型在測試集上的準確率達到了 95% 以上,能夠滿足實際應用的需求。

(三)可視化界面設計【PyQt6】

為了方便用戶操作和查看檢測結果,我們設計了一個簡潔直觀的可視化界面。界面主要包含以下幾個部分:

  • 數據輸入區域:用戶可以選擇攝像頭實時監測、導入視頻文件或上傳靜態圖片。
  • 檢測結果展示區域:實時顯示處理后的圖像或視頻,并用紅色邊界框標記出檢測到的火焰位置。
  • 信息提示區域:顯示火情發生的時間、位置等信息,以及系統的運行狀態。
  • 操作按鈕區域:提供開始檢測、停止檢測、保存檢測結果等功能按鈕。

需要在終端使用命令將ui文件轉為py文件才能繼續下面的操作。

(四)火災檢測與報警實現

火災檢測模塊通過調用訓練好的 YOLOv5 模型,對采集到的圖像或視頻幀進行處理。對于實時視頻流,系統每隔一定時間(如 300ms)截取一幀圖像進行檢測;對于視頻文件和靜態圖片,則直接進行檢測。

模型檢測到火焰后,會返回火焰在圖像中的位置坐標(以邊界框的形式表示)。系統根據這些坐標信息,判斷是否存在火情。當檢測到火情時,報警模塊立即啟動,通過調用語音合成接口,連續播報三次 “發生火情”。同時,系統會記錄火情發生的時間和位置信息,以便后續查詢和分析。

【AI分析火情】
import os
from dashscope import MultiModalConversation
def ai_analysis(self,local_path):# 將xxxx/test.jpg替換為你本地圖像的絕對路徑# local_path = r"C:\Users\dell\OneDrive\桌面\Project_YOLO\test\images\1.jpg"image_path = f"file://{local_path}"messages = [{'role':'user','content': [{'image': image_path},{'text': '請分析視頻內容,判斷是否為真實火災場景,''需考慮以下因素:1. 火焰形態 2. 煙霧特征 3. 環境背景 4. 潛在干擾源"''如果你認為極有可能是火災請輸出:發生火情;否則輸出:未檢測到火情'}]}]response = MultiModalConversation.call(# 若沒有配置環境變量,請用百煉API Key將下行替換為:api_key="sk-xxx",api_key='sk-ef17891e64364da887c6a3ff1a12d89d',model="qvq-max",  # 此處以qvq-max為例,可按需更換模型名稱。messages=messages,stream=True,)# 定義完整思考過程reasoning_content = ""# 定義完整回復answer_content = ""# 判斷是否結束思考過程并開始回復is_answering = Falseprint("=" * 20 + "思考過程" + "=" * 20)for chunk in response:# 如果思考過程與回復皆為空,則忽略message = chunk.output.choices[0].messagereasoning_content_chunk = message.get("reasoning_content", None)if (chunk.output.choices[0].message.content == [] andreasoning_content_chunk == ""):passelse:# 如果當前為思考過程if reasoning_content_chunk != None and chunk.output.choices[0].message.content == []:print(chunk.output.choices[0].message.reasoning_content, end="")reasoning_content += chunk.output.choices[0].message.reasoning_content# 如果當前為回復elif chunk.output.choices[0].message.content != []:if not is_answering:print("\n" + "=" * 20 + "完整回復" + "=" * 20)is_answering = Trueprint(chunk.output.choices[0].message.content[0]["text"], end="")answer_content += chunk.output.choices[0].message.content[0]["text"]# return f'{"="*5}思考過程{"="*5}\n{reasoning_content}\n{"="*5}分析結果{"="*5}\n{answer_content}'return answer_content# 需要打印完整思考過程與完整回復,請將以下代碼解除注釋后運行# print("=" * 20 + "完整思考過程" + "=" * 20 + "\n")# print(f"{reasoning_content}")# print("=" * 20 + "完整回復" + "=" * 20 + "\n")# print(f"{answer_content}")
【主文件】
# 保存為臨時文件
import os
import tempfile
import uuidfrom PySide6.QtTextToSpeech import QTextToSpeech
from analysis import ai_analysis
import sys
from pathlib import Path
import cv2
from PIL import Image
from PIL.ImageOps import exif_transposefrom PySide6 import QtCore
from PySide6.QtGui import QPixmap, QImage, Qt
from PySide6.QtWidgets import (QWidget, QApplication, QPushButton, QFileDialog,QMessageBox, QLabel, QLineEdit, QComboBox, QDoubleSpinBox, QTextEdit)
from fire_pre import Ui_Form
from ultralytics import YOLO# 封裝一個我的窗口
class Fire(QWidget,Ui_Form):def __init__(self):super().__init__()self.model_pt=''self.image_pth=''self.video_path = ''# 加載自己的窗口self.setupUi(self)# 找到ui界面的組件self.original_view:QLabel=self.original_viewself.tested_view:QLabel=self.tested_viewself.btn1:QPushButton=self.btn1self.btn2:QPushButton=self.btn2# 檢測圖像self.btn4_1:QPushButton=self.btn4_1self.btn4_4: QPushButton = self.btn4_4# 檢測視頻self.is_video_paused = False            # 視頻播放狀態self.is_video_detecting = False         # 視頻檢測狀態self.btn4_2: QPushButton = self.btn4_2  # 選擇視頻self.btn4_5: QPushButton = self.btn4_5  # 檢測視頻self.btn6_2:QPushButton=self.btn6_2     # 暫停檢測self.btn6_3:QPushButton=self.btn6_3     # 結束檢測# 攝像頭實時檢測self.btn4_3:QPushButton=self.btn4_3     # 打開攝像頭self.btn4_6:QPushButton=self.btn4_6     # 實時檢測self.btn6_4:QPushButton=self.btn6_4     # 關閉攝像頭,結束檢測# 查看任意檢測結果self.btn6_5:QPushButton=self.btn6_5# 檢測結果詳情顯示self.edit1:QLineEdit=self.edit1         # 檢測用時self.edit2: QLineEdit = self.edit2      # 檢測到的目標數量self.edit3: QLineEdit = self.edit3      # 目標位置xminself.edit4: QLineEdit = self.edit4      # xmaxself.edit5: QLineEdit = self.edit5      # yminself.edit6: QLineEdit = self.edit6      # ymax# 檢測目標self.cbb:QComboBox=self.cbbself.cbb.addItem('火')self.cbb.setCurrentText('火')# 置信度/閾值self.ds1:QDoubleSpinBox=self.ds1self.ds2:QDoubleSpinBox=self.ds2# 設置初始值和范圍self.ds1.setRange(0.0, 1.0)  # 置信度范圍0-1self.ds1.setValue(0.25)  # 默認值0.25self.ds2.setRange(0.0, 1.0)  # IOU范圍0-1self.ds2.setValue(0.7)  # 默認值0.7# ai分析self.textedit:QLabel=self.textedit# 新增火災分析定時器self.analysis_timer = QtCore.QTimer(self)self.analysis_timer.setInterval(30000)  # 30秒間隔self.analysis_timer.timeout.connect(self.analyze_fire_risk)# 實例化一個語音播報者 QTextToSpeechself.engine=Noneengines=QTextToSpeech.availableEngines()if engines:self.engine = engines[0]self.speaker = QTextToSpeech(self.engine, self)else:self.speaker = QTextToSpeech(self)# 檢查語音引擎是否成功初始化if len(self.speaker.availableVoices())==0:QMessageBox.warning(self, "語音功能", "未找到可用的語音引擎,語音播報功能將無法使用")else:# 設置默認語音if not self.engine:  # 如果沒有指定引擎,嘗試設置第一個可用語音voices = self.speaker.availableVoices()if voices:self.speaker.setVoice(voices[0])self.speaker.stateChanged.connect(self.on_speech_state_changed)# 播放次數計數器self.play_cont = 0# 是否已觸發self.alarm_triggered = False# 槽函數self.btn1.clicked.connect(self.model_weights)self.btn2.clicked.connect(self.model_init)self.btn4_1.clicked.connect(self.read_image)self.btn4_4.clicked.connect(self.test_image)self.btn4_2.clicked.connect(self.open_vidio)self.btn4_5.clicked.connect(self.start_video_detection)self.btn4_3.clicked.connect(self.open_camera)self.btn4_6.clicked.connect(self.start_continuous_detection)self.btn6_2.clicked.connect(self.toggle_video_play)  # 暫停/繼續視頻self.btn6_3.clicked.connect(self.stop_video_detection)  # 停止視頻檢測self.btn6_4.clicked.connect(self.close_camera)self.btn6_5.clicked.connect(self.view_history)# 文件框選擇模型權重def model_weights(self):# 彈出文件對話框  【絕對路徑】file_path,_=QFileDialog.getOpenFileName(self,'選擇文件','./','所有文件(*)')if file_path:QMessageBox.information(self,'模型權重',f'已選擇文件:\n{file_path}')self.model_pt=file_path# 模型初始化權重def model_init(self):file_path = r'C:\Users\dell\OneDrive\桌面\07 AI neural network\08 Project_YOLO\runs\detect\train\weights\best.pt'QMessageBox.information(self, '模型權重', f'已選擇文件:\n{file_path}')self.model_pt=file_path"""圖像檢測"""# 加載圖像def read_image(self):file_path, _ = QFileDialog.getOpenFileName(self, '選擇文件', './', '所有文件(*)')if not file_path:returntry:# 使用pathlib處理路徑image_path = Path(file_path)self.image_pth=image_path# 打開并預處理圖像with Image.open(image_path) as img:# 自動修正EXIF方向信息img = exif_transpose(img)# 轉換為RGB模式if img.mode != 'RGB':img = img.convert('RGB')# 轉換為QImageqt_image = QImage(img.tobytes(),img.width,img.height,img.width * 3,QImage.Format.Format_RGB888)# 創建QPixmap并縮放pixmap = QPixmap.fromImage(qt_image)scaled_pixmap = pixmap.scaled(self.original_view.size(),Qt.AspectRatioMode.KeepAspectRatio,Qt.TransformationMode.SmoothTransformation)# 顯示圖像self.original_view.setPixmap(scaled_pixmap)self.original_view.setAlignment(Qt.AlignmentFlag.AlignCenter)except Exception as e:QMessageBox.critical(self,"圖像加載失敗",f"無法加載圖像文件:\n{str(e)}")# 圖像檢測def test_image(self):conf=float(self.ds1.value())iou=float(self.ds2.value())model_rec=YOLO(self.model_pt)# 檢測照片 自動保存結果到runs/detect/exp目錄results=model_rec(self.image_pth,show=False,save=True,conf=conf,iou=iou)# 在tested_view上顯示出檢測后的圖像# 獲取保存路徑(YOLOv8默認保存路徑)save_dir = Path(results[0].save_dir)output_path = save_dir / self.image_pth.name# 驗證輸出文件是否存在if not output_path.exists():raise FileNotFoundError(f"未找到預測結果文件:{output_path}")# 加載處理后的圖像with Image.open(output_path) as img:# 自動修正EXIF方向信息img = exif_transpose(img)# 轉換為RGB模式if img.mode != 'RGB':img = img.convert('RGB')# 轉換為QImageqt_image = QImage(img.tobytes(),img.width,img.height,img.width * 3,QImage.Format.Format_RGB888)# 創建QPixmap并縮放pixmap = QPixmap.fromImage(qt_image)scaled_pixmap = pixmap.scaled(self.tested_view.size(),Qt.AspectRatioMode.KeepAspectRatio,Qt.TransformationMode.SmoothTransformation)# 顯示檢測結果self.tested_view.setPixmap(scaled_pixmap)self.tested_view.setAlignment(Qt.AlignmentFlag.AlignCenter)"""視頻檢測"""# 打開視頻def open_vidio(self):file_path, _ = QFileDialog.getOpenFileName(self, "選擇視頻文件", "./", "視頻文件 (*.mp4 *.avi *.mov *.mkv)")if not file_path:returntry:self.video_path = file_path  # 保存視頻路徑self.video_cap = cv2.VideoCapture(str(Path(file_path)))if not self.video_cap.isOpened():raise ValueError("無法打開視頻文件")QMessageBox.information(self, "視頻加載成功", f"已加載視頻:{Path(file_path).name}\n"f"分辨率:{int(self.video_cap.get(3))}x{int(self.video_cap.get(4))}")# 創建視頻定時器self.video_timer = QtCore.QTimer(self)self.video_timer.timeout.connect(self.update_video_frame)self.video_timer.start(30)  # 每30毫秒更新一次畫面self.is_video_paused = False  # 初始狀態為播放self.btn6_2.setText("暫停播放")  # 更新按鈕文本except Exception as e:QMessageBox.critical(self, "視頻加載失敗", f"無法加載視頻文件:\n{str(e)}")# 更新視頻幀(支持循環播放)def update_video_frame(self):if not hasattr(self, 'video_cap') or not self.video_cap.isOpened() or self.is_video_paused:returnret, frame = self.video_cap.read()if not ret:# 視頻播放完畢,重新開始播放self.video_cap.release()self.video_cap = cv2.VideoCapture(str(Path(self.video_path)))if self.video_cap.isOpened():ret, frame = self.video_cap.read()if ret:# 轉換顏色空間:BGR -> RGBrgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)# 轉換為QImageh, w, ch = rgb_frame.shapebytes_per_line = ch * wq_img = QImage(rgb_frame.data, w, h, bytes_per_line, QImage.Format_RGB888)# 縮放并顯示pixmap = QPixmap.fromImage(q_img)scaled_pixmap = pixmap.scaled(self.original_view.size(),Qt.AspectRatioMode.KeepAspectRatio,Qt.TransformationMode.SmoothTransformation)self.original_view.setPixmap(scaled_pixmap)else:QMessageBox.warning(self, "視頻錯誤", "無法重新打開視頻進行循環播放")self.video_timer.stop()return# 轉換顏色空間:BGR -> RGBrgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)# 轉換為QImageh, w, ch = rgb_frame.shapebytes_per_line = ch * wq_img = QImage(rgb_frame.data, w, h, bytes_per_line, QImage.Format_RGB888)# 縮放并顯示在original_view上pixmap = QPixmap.fromImage(q_img)scaled_pixmap = pixmap.scaled(self.original_view.size(),Qt.AspectRatioMode.KeepAspectRatio,Qt.TransformationMode.SmoothTransformation)self.original_view.setPixmap(scaled_pixmap)self.original_view.setAlignment(Qt.AlignmentFlag.AlignCenter)# 暫停/繼續視頻播放def toggle_video_play(self):if not hasattr(self, 'video_timer'):returnself.is_video_paused = not self.is_video_paused  # 切換播放狀態if self.is_video_paused:self.video_timer.stop()self.btn6_2.setText("繼續播放")else:self.video_timer.start(30)self.btn6_2.setText("暫停播放")# 開始視頻檢測def start_video_detection(self):if not hasattr(self, 'video_cap') or not self.video_cap.isOpened():QMessageBox.warning(self, '視頻', '請先選擇并加載視頻')returnif not self.model_pt:QMessageBox.warning(self, '模型', '請先選擇模型權重')returntry:# 加載模型if not hasattr(self, 'model'):self.model = YOLO(self.model_pt)# 創建視頻檢測定時器if not hasattr(self, 'video_detection_timer'):self.video_detection_timer = QtCore.QTimer(self)self.video_detection_timer.timeout.connect(self.detect_video_frame)if not self.video_detection_timer.isActive():self.is_video_detecting = Trueself.video_detection_timer.start(30)  # 每30ms檢測一次self.btn4_5.setText("停止檢測")self.btn4_5.clicked.disconnect()self.btn4_5.clicked.connect(self.stop_video_detection)except Exception as e:QMessageBox.critical(self, "檢測失敗", f"執行檢測時出錯:\n{str(e)}")# 停止視頻檢測def stop_video_detection(self):if hasattr(self, 'video_detection_timer') and self.video_detection_timer.isActive():self.is_video_detecting = Falseself.video_detection_timer.stop()self.btn4_5.setText("開始檢測")self.btn4_5.clicked.disconnect()self.btn4_5.clicked.connect(self.start_video_detection)# 檢測視頻幀def detect_video_frame(self):if not hasattr(self, 'video_cap') or not self.video_cap.isOpened() or self.is_video_paused:returnret, frame = self.video_cap.read()if not ret:# 視頻播放完畢,重新開始播放self.video_cap.release()self.video_cap = cv2.VideoCapture(str(Path(self.video_path)))if self.video_cap.isOpened():ret, frame = self.video_cap.read()if not ret:returnelse:self.stop_video_detection()return# 執行檢測results = self.model(frame, show=False, conf=0.5)# 渲染檢測結果annotated_frame = results[0].plot()# 轉換為RGB(確保顏色正確)rgb_annotated_frame = cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB)# 轉換為QImageh, w, ch = rgb_annotated_frame.shapebytes_per_line = ch * wq_img = QImage(rgb_annotated_frame.data, w, h, bytes_per_line, QImage.Format_RGB888)# 縮放并顯示在tested_view上pixmap = QPixmap.fromImage(q_img)scaled_pixmap = pixmap.scaled(self.tested_view.size(),Qt.AspectRatioMode.KeepAspectRatio,Qt.TransformationMode.SmoothTransformation)self.tested_view.setPixmap(scaled_pixmap)self.tested_view.setAlignment(Qt.AlignmentFlag.AlignCenter)# 顯示檢測信息self.edit1.setText(f"{results[0].speed['inference']:.2f}ms")  # 檢測用時self.edit2.setText(str(len(results[0].boxes)))  # 檢測到的目標數量if len(results[0].boxes) > 0:# 顯示第一個檢測框的位置box = results[0].boxes[0]xmin, ymin, xmax, ymax = box.xyxy[0].cpu().numpy().astype(int)self.edit3.setText(str(xmin))self.edit4.setText(str(xmax))self.edit5.setText(str(ymin))self.edit6.setText(str(ymax))"""攝像頭實時檢測"""# 打開攝像頭def open_camera(self):self.cap=cv2.VideoCapture(0)if not self.cap.isOpened():QMessageBox.warning(self,'攝像頭','打開攝像頭失敗,請檢查攝像頭')return# 創建定時器,用于定時捕獲畫面self.timer = QtCore.QTimer(self)self.timer.timeout.connect(self.update_camera_frame)self.timer.start(30)  # 每30毫秒更新一次畫面# 啟動火災定時器self.analysis_timer.start()  # 啟動分析定時器# 更新畫面def update_camera_frame(self):if not hasattr(self, 'cap') or not self.cap.isOpened():returnret,frame=self.cap.read()if not ret:QMessageBox.warning(self, '攝像頭', '攝像頭捕獲畫面失敗,請檢查攝像頭')self.cap.release()# 顯示圖像# 正確轉換顏色空間:BGR -> RGBrgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)# 將OpenCV幀轉換為QImageh, w, ch = rgb_frame.shapebytes_per_line = ch * wq_img = QImage(rgb_frame.data, w, h, bytes_per_line, QImage.Format_RGB888)# 縮放并顯示在original_view上pixmap = QPixmap.fromImage(q_img)scaled_pixmap = pixmap.scaled(self.original_view.size(),Qt.AspectRatioMode.KeepAspectRatio,Qt.TransformationMode.SmoothTransformation)self.original_view.setPixmap(scaled_pixmap)self.original_view.setAlignment(Qt.AlignmentFlag.AlignCenter)# 新增關閉攝像頭的方法def close_camera(self):if hasattr(self, 'cap') and self.cap.isOpened():self.cap.release()if hasattr(self, 'timer'):self.timer.stop()# 關閉火災定時器self.analysis_timer.stop()# 實時檢測攝像頭內容 顯示在tested_view()上def test_camara(self):if not hasattr(self, 'cap') or not self.cap.isOpened():QMessageBox.warning(self, '攝像頭', '請先打開攝像頭')returnif not self.model_pt:QMessageBox.warning(self, '模型', '請先選擇模型權重')returntry:# 加載模型if not hasattr(self, 'model'):self.model = YOLO(self.model_pt)# 讀取當前幀ret, frame = self.cap.read()if not ret:QMessageBox.warning(self, '攝像頭', '無法獲取當前幀')return# 執行檢測results = self.model(frame, show=False, conf=0.5)# 渲染檢測結果annotated_frame = results[0].plot()# 轉換為RGB(確保顏色正確)rgb_annotated_frame = cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB)# 轉換為QImageh, w, ch = rgb_annotated_frame.shapebytes_per_line = ch * wq_img = QImage(rgb_annotated_frame.data, w, h, bytes_per_line, QImage.Format_RGB888)# 縮放并顯示在tested_view上pixmap = QPixmap.fromImage(q_img)scaled_pixmap = pixmap.scaled(self.tested_view.size(),Qt.AspectRatioMode.KeepAspectRatio,Qt.TransformationMode.SmoothTransformation)self.tested_view.setPixmap(scaled_pixmap)self.tested_view.setAlignment(Qt.AlignmentFlag.AlignCenter)# 顯示檢測信息self.edit1.setText(f"{results[0].speed['inference']:.2f}ms")  # 檢測用時self.edit2.setText(str(len(results[0].boxes)))  # 檢測到的目標數量if len(results[0].boxes) > 0:# 顯示第一個檢測框的位置box = results[0].boxes[0]xmin, ymin, xmax, ymax = box.xyxy[0].cpu().numpy().astype(int)self.edit3.setText(str(xmin))self.edit4.setText(str(xmax))self.edit5.setText(str(ymin))self.edit6.setText(str(ymax))except Exception as e:QMessageBox.critical(self, "檢測失敗", f"執行檢測時出錯:\n{str(e)}")def start_continuous_detection(self):if not hasattr(self, 'detection_timer'):self.detection_timer = QtCore.QTimer(self)self.detection_timer.timeout.connect(self.test_camara)if not self.detection_timer.isActive():self.detection_timer.start(30)  # 每30ms檢測一次(約30FPS)self.btn4_6.setText("停止檢測")self.btn4_6.clicked.disconnect()self.btn4_6.clicked.connect(self.stop_continuous_detection)def stop_continuous_detection(self):if hasattr(self, 'detection_timer') and self.detection_timer.isActive():self.detection_timer.stop()self.btn4_6.setText("開始檢測")self.btn4_6.clicked.disconnect()self.btn4_6.clicked.connect(self.start_continuous_detection)# 火災分析def analyze_fire_risk(self):if not hasattr(self, 'cap') or not self.cap.isOpened():return# 獲取當前幀ret, frame = self.cap.read()if not ret:returntemp_dir = tempfile.gettempdir()temp_path = os.path.join(temp_dir, f'fire_analysis_{uuid.uuid4().hex}.jpg')cv2.imwrite(temp_path, frame)try:# 調用AI分析函數result = ai_analysis(self,temp_path)# 更新UI(保持QLabel不變)self.textedit.setText(result)# 改進的火情檢測邏輯if result=='發生火情':# 觸發 播放 初始化播放次數self.alarm_triggered = Trueself.play_cont = 0# 啟動語音模塊self.speaker.say(self.textedit.text())except Exception as e:QMessageBox.critical(self, "分析錯誤", f"火災分析失敗:{str(e)}")finally:if os.path.exists(temp_path):os.remove(temp_path)# 語音狀態變化處理def on_speech_state_changed(self, state):print(f"當前語音狀態:{state}")  # 添加調試輸出if state == QTextToSpeech.State.Ready and self.alarm_triggered:self.play_cont += 1if self.play_cont < 3:self.speaker.say(self.textedit.text())else:# 重置觸發狀態self.alarm_triggered = Falseself.play_cont = 0# 查看所有歷史預測結果def view_history(self):file_path,_=QFileDialog.getOpenFileName(self,'選擇文件','./','所有文件(*)')if not file_path:returntry:# 使用pathlib處理路徑image_path = Path(file_path)self.image_pth=image_path# 打開并預處理圖像with Image.open(image_path) as img:# 自動修正EXIF方向信息img = exif_transpose(img)# 轉換為RGB模式if img.mode != 'RGB':img = img.convert('RGB')# 轉換為QImageqt_image = QImage(img.tobytes(),img.width,img.height,img.width * 3,QImage.Format.Format_RGB888)# 創建QPixmap并縮放pixmap = QPixmap.fromImage(qt_image)scaled_pixmap = pixmap.scaled(self.tested_view.size(),Qt.AspectRatioMode.KeepAspectRatio,Qt.TransformationMode.SmoothTransformation)# 顯示圖像self.tested_view.setPixmap(scaled_pixmap)self.tested_view.setAlignment(Qt.AlignmentFlag.AlignCenter)except Exception as e:QMessageBox.critical(self,"圖像加載失敗",f"無法加載圖像文件:\n{str(e)}")if __name__ == '__main__':app=QApplication(sys.argv)fire_win=Fire()fire_win.show()sys.exit(app.exec())

三、系統測試【視頻演示】

簡單火災檢測系統演示

四、部署

一、準備工作

  1. 確認依賴完整性
    你的?requirements.txt?需包含項目所有依賴(如?torchopencv-pythonPyQt5?等),可通過?pip list --format=freeze > requirements.txt?重新生成(確保覆蓋全)。

  2. 處理資源文件

    • 若?icons.qrc?等 Qt 資源文件未編譯,先確保?icons_rc.py?已正確生成(若用?pyrcc5?編譯過則無需處理)。
    • 確認?models?文件夾、fire_pre.ui?等資源路徑在代碼中是相對路徑或可動態加載,避免打包后路徑失效。

二、使用 PyInstaller 打包

1. 安裝 PyInstaller
pip install pyinstaller
2. 編寫.spec 文件(關鍵配置,可選但推薦)

進入項目根目錄(YOLO?文件夾),執行:

pyi-makespec --onefile --windowed --name your_app_name ui.py
  • --onefile:打包成單個 EXE 文件(簡潔,適合分發)。
  • --windowed:隱藏命令行黑框(若為 GUI 程序,如 PyQt 項目,必選)。
  • --name:指定最終 EXE 名稱(替換?your_app_name)。
  • ui.py:假設?ui.py?是程序入口(若入口是其他文件,替換為實際入口腳本,比如?fire_pre.py?等,需確保入口邏輯完整)。

執行后會生成?your_app_name.spec?文件,打開編輯關鍵配置:

# your_app_name.spec
block_cipher = Nonea = Analysis(['ui.py'],  # 入口腳本,替換為實際入口pathex=['C:\\Users\\dell\\OneDrive\\桌面\\YOLO'],  # 項目根路徑binaries=[],datas=[# 打包額外資源:models 文件夾、ui 文件、圖標等('models', 'models'),  ('fire_pre.ui', '.'),  ('icons_rc.py', '.'),  # 若有其他資源(如 requirements.txt 想一起打包也可加)],hiddenimports=[# 手動補充 PyInstaller 未自動檢測到的依賴'torch', 'torchvision', 'cv2',  # 示例,根據實際依賴補充'PyQt5', 'PyQt5.QtWidgets'  # 若用 PyQt,需確保此類導入被識別],hookspath=[],hooksconfig={},runtime_hooks=[],excludes=[],win_no_prefer_redirects=False,win_private_assemblies=False,cipher=block_cipher,noarchive=False)pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)exe = EXE(pyz,a.scripts,a.binaries,a.zipfiles,a.datas,[],name='your_app_name',  # EXE 名稱debug=False,bootloader_ignore_signals=False,strip=False,upx=True,  # 壓縮 EXE,可選upx_exclude=[],runtime_tmpdir=None,console=False,  # 同 --windowed,隱藏命令行disable_windowed_traceback=False,argv_emulation=False,target_arch=None,codesign_identity=None,entitlements_file=None )

關鍵配置說明

  • datas:列出需打包的非代碼資源(文件夾、UI 文件等),格式?(原路徑, 打包后路徑)
  • hiddenimports:補充 PyInstaller 未自動發現的模塊(如 PyTorch、PyQt 的深層依賴),缺啥補啥,否則運行時會報?ModuleNotFoundError
3. 執行打包

在項目根目錄執行:

pyinstaller your_app_name.spec

PyInstaller 會根據?.spec?配置,收集依賴、打包資源,最終在?dist?文件夾生成?your_app_name.exe

三、測試與排坑

  1. 運行測試
    把?dist?里的?your_app_name.exe?單獨復制出來,雙擊運行,測試功能是否正常(如 YOLO 檢測、UI 交互等)。

  2. 常見問題處理

    • 依賴缺失:運行報錯?ModuleNotFoundError,補充到?.spec?的?hiddenimports?或?requirements.txt
    • 資源路徑錯誤:代碼中用?os.path.join?動態拼接路徑,避免硬編碼(如?os.path.join(os.path.dirname(__file__), 'models'))。
    • PyTorch 體積大:若嫌 EXE 太大,可嘗試精簡模型(如用更小的 YOLO 權重),或用?upx?壓縮(spec?中?upx=True)。

四、最終分發

將?dist?中的?your_app_name.exe?單獨發給他人,若依賴復雜(如 PyTorch 等大庫),EXE 體積可能較大,屬于正常現象。

五、總結與展望

本文設計并實現了一款基于深度學習的火災智能檢測系統,該系統通過 YOLOv8?模型實現了對火焰的精準檢測,支持多種數據輸入方式,具備實時報警和可視化展示功能。測試結果表明,系統具有較高的檢測準確率和實時性,能夠滿足實際火災檢測的需求。

然而,系統仍存在一些不足之處。例如,在復雜環境下(如強光照、大霧天氣),模型的檢測性能可能會受到一定影響;對于一些與火焰外觀相似的物體(如紅色燈光),可能會出現誤檢的情況;嵌入的大模型進行火情分析受配置限制,響應時間比較長。

未來,可以從以下幾個方面對系統進行改進和優化:

  • 進一步擴充數據集,增加復雜環境下的火災樣本和易混淆樣本,提高模型的泛化能力。
  • 嘗試使用更先進的深度學習模型,進一步提高系統的檢測精度和速度。
  • 增加火災蔓延預測功能,根據火災的發展趨勢,提前預測火災的蔓延路徑和范圍,為火災撲救提供更有力的支持。
  • 加強系統的穩定性和可靠性,提高系統在惡劣環境下的工作能力。

總之,基于深度學習的火災智能檢測系統具有廣闊的應用前景。隨著技術的不斷發展和完善,相信該系統能夠在火災預防和撲救工作中發揮更大的作用,為人們的生命財產安全提供更有力的保障。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/89498.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/89498.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/89498.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HIVE實戰處理(二十四)留存用戶數

留存概念: 次X日活躍留存&#xff0c;次X日新增留存&#xff0c;也就是看今天的新增或活躍用戶在后續幾天的留存情況一、留存表的生成邏輯 因為用戶活躍日期和留存的日期無法對齊所以搞了2級分區&#xff08;dt,static_day&#xff09; 1&#xff09;首先獲得計算日D、根據要出…

W3C XHTML 活動:標準化的未來與交互式體驗

W3C XHTML 活動:標準化的未來與交互式體驗 概述 W3C(World Wide Web Consortium)是全球領先的互聯網技術標準制定組織。XHTML,作為W3C推薦的標準之一,是一種基于XML的標記語言,旨在提供一個更加結構化、兼容性和可擴展性更高的網頁內容表示方式。本文將圍繞W3C的XHTML活…

Java-數構鏈表

1.鏈表 1.1鏈表的概念和結構 鏈表是一種物理存儲結構上非連續存儲結構&#xff0c;數據元素的邏輯順序是通過鏈表中引用鏈接次序實現的。 這里大多討論無頭單向非循環鏈表。這種結構&#xff0c;結構簡單&#xff0c;一般與其他數據結構結合&#xff0c;作為其他數據結構的子…

Windows系統軟件游戲丟失找不到mgmtapi.dll修復解決方法

在使用電腦系統時經常會出現丟失找不到某些文件的情況&#xff0c;由于很多常用軟件都是采用 Microsoft Visual Studio 編寫的&#xff0c;所以這類軟件的運行需要依賴微軟Visual C運行庫&#xff0c;比如像 QQ、迅雷、Adobe 軟件等等&#xff0c;如果沒有安裝VC運行庫或者安裝…

初識C++——開啟新旅途

從今天開始主包也是掉入C這個深坑&#xff0c;上完課也是跟沒上一樣&#xff0c;所以寫好博客復習還是很重要的&#xff0c;話不多說&#xff0c;進入正題~~1、命名空間(1)namespace的價值與作用在C/C中&#xff0c;變量、函數和后面要學到的類都是大量存在的&#xff0c;這些變…

vue2 面試題及詳細答案150道(141 - 150)

《前后端面試題》專欄集合了前后端各個知識模塊的面試題&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

第十三章 Go包管理

文章目錄使用logurs處理程序日志logrus 常用配置使用viper處理程序配置使用logurs處理程序日志 下載包&#xff0c;在終端執行命令 go get github.com/sirupsen/logrus官方示例 package mainimport (log "github.com/sirupsen/logrus" )func main() {log.WithFiel…

EP01:【Python 第一彈】基礎入門知識

一、基礎入門知識 1.1 代碼規范 1.1.1 語句分隔符 ; 換行 1.1.2 格式化 對 Windows 和 Linux 操作系統&#xff0c;快捷鍵是Ctrl Alt L對 macOS 操作系統&#xff0c;快捷鍵是Cmd Option L 1.1.3 注釋 單行注釋 # 這是一行注釋多行注釋 """ 這 是 …

實用的文件和文件夾批量重命名工具

在日常工作中&#xff0c;文件和文件夾的命名管理常常讓人頭疼。尤其是面對大量文件時&#xff0c;手動重命名不僅耗時&#xff0c;還容易出錯。今天&#xff0c;我要給大家推薦一款超級實用的工具——OncePower 文件批量重命名&#xff0c;它不僅能批量重命名文件和文件夾&…

【Git】報錯:git config --global http.sslBackend “openssl“

問題解決 報錯&#xff1a;git config --global http.sslBackend “openssl”解決方法&#xff1a; git config --global http.sslBackend "openssl"之后再 push 即可正常提交。 &#x1f50d; 原因分析 ??系統環境不支持 OpenSSL 后端?? Git 在某些平臺&#xf…

Redisson RLocalCachedMap 核心參詳解

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家&#xff0c;歷代文學網&#xff08;PC端可以訪問&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移動端可微信小程序搜索“歷代文學”&#xff09;總架構師&#xff0c;15年工作經驗&#xff0c;精通Java編…

AI輔助編程時代的高效規范開發指南:工具、原則與提效策略

引言&#xff1a;AI輔助編程的時代背景與核心挑戰 人工智能在編程領域的應用雖可追溯至20世紀50年代&#xff0c;但近十年實現了革命性突破&#xff0c;推動其從早期的代碼補全工具演進為能理解上下文、生成完整函數乃至項目架構的智能系統。關鍵發展里程碑包括&#xff1a;20…

百度網盤TV版1.21.0 |支持倍速播放,大屏云看片

百度網盤TV版是專為智能電視設計的應用程序&#xff0c;讓用戶可以直接在大屏幕上觀看保存在云端的視頻資源。此應用提供了與手機端幾乎相同的功能&#xff0c;包括倍速播放功能&#xff0c;使得用戶可以更方便地享受高清視頻內容。無需繁瑣的操作步驟&#xff0c;即可實現云端…

C++控制臺貪吃蛇開發(二):讓靈蛇舞動起來!

資料合集下載鏈接: ??https://pan.quark.cn/s/472bbdfcd014? 本文將深入講解蛇移動的機制,并帶你一步步實現以下功能: 理解蛇移動的核心算法:為什么蛇的移動是“倒著”更新的? 用代碼表示方向:如何使用??dx??和??dy??變量優雅地控制方向。 編寫核心??move…

Elasticsearch+Logstash+Filebeat+Kibana部署

目錄 軟件說明&#xff1a; 架構拓撲 集群模式&#xff1a; 單機模式 環境準備 部署&#xff1a; kibana es logstash filebeat es 檢查能否啟動 logstash 命令設置 es 修改es配置文件 啟用es kibana 修改kibana配置文件&#xff08;方便查看索引&#xff09…

GLM(General Language Model,通用語言模型)

&#x1f9e0; 一、GLM是什么&#xff1f;一句話概括 GLM&#xff08;General Language Model&#xff0c;通用語言模型&#xff09;是一個“大腦”&#xff0c;它通過閱讀海量書籍、網頁、對話記錄學會了人類的語言規則&#xff0c;不僅能“聽懂”你說的話&#xff0c;還能“…

【科研繪圖系列】R語言繪制顯著性標記的熱圖

文章目錄 介紹 加載R包 數據下載 導入數據 數據預處理 畫圖 系統信息 參考 介紹 【科研繪圖系列】R語言繪制顯著性標記的熱圖 加載R包 library(ggplot2) library(patchwork)rm(list = ls()) options(stringsAsFactors = F)

若依部署項目到服務器

目錄 一、環境配置 redis nginx&#xff08;宿主機|dokcer&#xff09; 1.宿主機 2.docker 二、打包jar包 0.查看后端配置 1.打包后端 2.打包前端 三、啟動 1.后端 2.前端 四、以上部署常見命令/錯誤 一、環境配置 之前的課都配過&#xff0c;先看看自己配了沒 看看…

零基礎學習性能測試-linux服務器監控:CPU監控

目錄學習內容與快速應用路徑第一階段&#xff1a;理解 CPU 核心概念 (0.5天)第二階段&#xff1a;掌握核心監控命令與指標 (1-2天)第三階段&#xff1a;識別 CPU 問題與瓶頸 (核心技能)第四階段&#xff1a;整合到性能測試工作流程 (快速應用落地)快速應用到工作中的關鍵策略零…

智能Agent場景實戰指南 Day 15:游戲NPC Agent互動設計

【智能Agent場景實戰指南 Day 15】游戲NPC Agent互動設計 文章內容 開篇 歡迎來到"智能Agent場景實戰指南"系列的第15天&#xff01;今天我們將深入探討游戲開發中一個極具挑戰性和創新性的領域——游戲NPC Agent互動設計。在當今游戲產業中&#xff0c;玩家對游戲…