【Python開源】深度解析:一款高效音頻封面批量刪除工具的設計與實現

🎵 【Python開源】深度解析:一款高效音頻封面批量刪除工具的設計與實現

請添加圖片描述

🌈 個人主頁:創客白澤 - CSDN博客
🔥 系列專欄:🐍《Python開源項目實戰》
💡 熱愛不止于代碼,熱情源自每一個靈感閃現的夜晚。愿以開源之火,點亮前行之路。
👍 如果覺得這篇文章有幫助,歡迎您一鍵三連,分享給更多人哦

請添加圖片描述在這里插入圖片描述

📖 概述

在數字音樂管理過程中,音頻文件內嵌的封面圖片往往會占用額外存儲空間,特別是當我們需要批量處理大量音頻文件時。本文介紹一款基于Python和PyQt5開發的跨平臺音頻封面刪除工具,它支持多種音頻格式(MP3、FLAC、M4A、OGG、WMA),提供三種不同的處理方式,并具備友好的圖形用戶界面。

本工具不僅能有效移除音頻文件中的封面數據,還能保持音頻質量無損,是音樂收藏家和數字資產管理者的實用工具。下面我們將從功能、實現原理、代碼解析等多個維度進行詳細介紹。

🛠? 功能特點

  1. 多格式支持

    • MP3 (ID3標簽)
    • FLAC (Vorbis注釋)
    • M4A/MP4 (iTunes元數據)
    • OGG (Vorbis/Opus)
    • WMA (ASF容器)
  2. 三種處理方式

    • Mutagen庫(推薦):Python專用音頻元數據處理庫
    • FFmpeg:專業音視頻處理工具
    • 二進制處理:最后手段的直接文件操作
  3. 智能文件管理

    • 拖放文件夾支持
    • 自動掃描子目錄
    • 可選輸出目錄設置
    • 文件類型過濾
  4. 可視化操作

    • 進度條顯示
    • 處理結果統計
    • 錯誤處理機制

🖼? 界面展示

在這里插入圖片描述

圖1:軟件主界面,包含目錄設置、文件列表和操作按鈕
在這里插入圖片描述
在這里插入圖片描述

圖2、圖3:文件處理進度顯示

🧰 使用說明

1. 準備工作

  • 安裝Python 3.7+
  • 安裝依賴庫:
    pip install PyQt5 mutagen
    
    • (可選) 如需使用FFmpeg方式,需提前安裝FFmpeg并加入系統PATH

2. 操作步驟

  1. 選擇輸入目錄:點擊"瀏覽"按鈕或直接拖放文件夾到輸入框
  2. 設置輸出目錄(可選):默認為輸入目錄下的"cleaned_audio"文件夾
  3. 選擇處理方式
    • Mutagen:推薦方式,處理速度快且穩定
    • FFmpeg:適合復雜音頻文件
    • 二進制:最后手段,兼容性較差
  4. 掃描文件:點擊"掃描文件"按鈕獲取目錄下所有支持的音頻文件
  5. 選擇處理范圍
    • “處理選中”:僅處理列表中選中的文件
    • “處理全部”:批量處理所有掃描到的文件
  6. 查看結果:處理完成后會顯示成功/失敗統計,處理后的文件保存在輸出目錄

3. 注意事項

  • 處理前建議備份原始文件
  • 某些音頻播放器可能需要重新掃描文件才能顯示更改
  • FLAC文件的封面刪除會同時移除所有內嵌圖片

💻 代碼深度解析

1. 核心技術棧

  • PyQt5:構建現代化GUI界面
  • Mutagen:音頻元數據處理核心庫
  • FFmpeg(可選):專業音視頻處理
  • 標準庫:os, sys, shutil等處理文件操作

2. 關鍵類說明

DraggableLineEdit (自定義拖放文本框)
class DraggableLineEdit(QLineEdit):def dragEnterEvent(self, event):if event.mimeData().hasUrls():event.acceptProposedAction()def dropEvent(self, event):for url in event.mimeData().urls():path = url.toLocalFile()if os.path.isdir(path):self.setText(path)break

實現文件夾拖放功能的核心代碼,增強了用戶體驗

AudioCoverRemover (主窗口類)
def process_with_mutagen(self, input_path, output_path, ext):# 先復制文件if input_path != output_path:shutil.copy2(input_path, output_path)# 根據格式使用不同的處理方法if ext == "mp3":audio = MP3(output_path, ID3=ID3)if audio.tags:audio.tags.delall("APIC")audio.save()elif ext == "flac":audio = FLAC(output_path)if audio.pictures:audio.clear_pictures()audio.save()...

不同音頻格式的封面刪除邏輯,展示了Mutagen庫的強大靈活性

3. 設計亮點

  1. 多方法兼容處理
    • 提供三種不同實現方式,確保最大兼容性
    • 自動選擇最適合當前文件的方法
  2. 現代化UI設計
    • 自定義樣式表美化界面
    • 響應式布局適應不同分辨率
    • 進度反饋增強用戶體驗
  3. 健壯的錯誤處理
    • 捕獲并記錄各種處理異常
    • 不影響整體批處理流程
  4. 跨平臺支持
    • 兼容Windows/macOS/Linux
    • 自動處理路徑分隔符差異

📥 源碼下載

import os
import sys
import subprocess
import shutil
from mutagen.mp3 import MP3
from mutagen.id3 import ID3, APIC
from mutagen.flac import FLAC
from mutagen.mp4 import MP4
from mutagen.oggopus import OggOpus
from mutagen.oggvorbis import OggVorbis
from mutagen.asf import ASF
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,QPushButton, QLabel, QLineEdit, QFileDialog,QListWidget, QWidget, QProgressBar, QMessageBox,QCheckBox, QGroupBox, QComboBox)
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QColor, QPalette, QIconclass DraggableLineEdit(QLineEdit):def __init__(self, parent=None):super().__init__(parent)self.setAcceptDrops(True)def dragEnterEvent(self, event):if event.mimeData().hasUrls():event.acceptProposedAction()def dropEvent(self, event):for url in event.mimeData().urls():path = url.toLocalFile()if os.path.isdir(path):self.setText(path)breakclass AudioCoverRemover(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("🎵 音頻封面刪除工具")self.setGeometry(100, 100, 547, 608)# 支持的音頻格式self.supported_formats = {'mp3': 'MP3音頻','flac': 'FLAC無損音頻','m4a': 'MP4/AAC音頻','ogg': 'OGG音頻','wma': 'WMA音頻'}# 初始化變量self.audio_files = []self.current_method = "mutagen"# 設置UI樣式self.setup_ui_style()# 初始化UIself.init_ui()# 設置窗口圖標self.setWindowIcon(QIcon(self.get_icon_path()))def get_icon_path(self):"""獲取圖標路徑(適配不同平臺)"""if getattr(sys, 'frozen', False):# 打包后的路徑base_path = sys._MEIPASSelse:# 開發時的路徑base_path = os.path.dirname(os.path.abspath(__file__))return os.path.join(base_path, 'icon.png')def setup_ui_style(self):"""設置現代化UI樣式"""palette = self.palette()palette.setColor(QPalette.Window, QColor(245, 245, 245))palette.setColor(QPalette.WindowText, QColor(60, 60, 60))palette.setColor(QPalette.Base, QColor(255, 255, 255))palette.setColor(QPalette.AlternateBase, QColor(240, 240, 240))palette.setColor(QPalette.ToolTipBase, QColor(255, 255, 220))palette.setColor(QPalette.ToolTipText, Qt.black)palette.setColor(QPalette.Text, Qt.black)palette.setColor(QPalette.Button, QColor(70, 160, 230))palette.setColor(QPalette.ButtonText, Qt.white)palette.setColor(QPalette.BrightText, Qt.red)palette.setColor(QPalette.Highlight, QColor(70, 160, 230))palette.setColor(QPalette.HighlightedText, Qt.white)self.setPalette(palette)self.setStyleSheet("""QGroupBox {border: 1px solid #dcdcdc;border-radius: 6px;margin-top: 12px;padding-top: 18px;font-weight: bold;color: #505050;}QGroupBox::title {subcontrol-origin: margin;left: 12px;padding: 0 5px;}QPushButton {background-color: #46a0f0;color: white;border: none;padding: 7px 14px;border-radius: 5px;min-width: 90px;font-size: 13px;}QPushButton:hover {background-color: #3a8cd0;}QPushButton:pressed {background-color: #2e78b0;}QPushButton:disabled {background-color: #cccccc;color: #888888;}QListWidget {border: 1px solid #dcdcdc;border-radius: 5px;background: white;font-size: 13px;}QProgressBar {border: 1px solid #dcdcdc;border-radius: 5px;text-align: center;height: 20px;font-size: 12px;}QProgressBar::chunk {background-color: #46a0f0;border-radius: 4px;}QComboBox {border: 1px solid #dcdcdc;border-radius: 4px;padding: 3px;min-width: 120px;}QLineEdit {border: 1px solid #dcdcdc;border-radius: 4px;padding: 5px;}""")def init_ui(self):main_widget = QWidget()self.setCentralWidget(main_widget)layout = QVBoxLayout()layout.setContentsMargins(12, 12, 12, 12)layout.setSpacing(10)# 頂部控制區域top_layout = QHBoxLayout()# 方法選擇method_layout = QHBoxLayout()method_layout.addWidget(QLabel("處理方法:"))self.method_combo = QComboBox()self.method_combo.addItems(["Mutagen (推薦)", "FFmpeg", "二進制處理"])method_layout.addWidget(self.method_combo)# 格式過濾format_layout = QHBoxLayout()format_layout.addWidget(QLabel("文件類型:"))self.format_combo = QComboBox()self.format_combo.addItems(["所有支持格式"] + list(self.supported_formats.values()))format_layout.addWidget(self.format_combo)top_layout.addLayout(method_layout)top_layout.addStretch()top_layout.addLayout(format_layout)# 目錄設置dir_group = QGroupBox("目錄設置")dir_layout = QVBoxLayout()dir_layout.setSpacing(10)# 輸入目錄(使用自定義的可拖放QLineEdit)input_layout = QHBoxLayout()input_layout.addWidget(QLabel("輸入目錄:"))self.input_path = DraggableLineEdit()self.input_path.setPlaceholderText("拖放文件夾到這里或點擊瀏覽...")self.browse_input_btn = QPushButton("瀏覽")self.browse_input_btn.clicked.connect(self.browse_input)input_layout.addWidget(self.input_path, stretch=1)input_layout.addWidget(self.browse_input_btn)# 輸出目錄output_layout = QHBoxLayout()output_layout.addWidget(QLabel("輸出目錄:"))self.output_path = DraggableLineEdit()self.output_path.setPlaceholderText("默認: 輸入目錄下的'cleaned_audio'文件夾")self.browse_output_btn = QPushButton("瀏覽")self.browse_output_btn.clicked.connect(self.browse_output)output_layout.addWidget(self.output_path, stretch=1)output_layout.addWidget(self.browse_output_btn)dir_layout.addLayout(input_layout)dir_layout.addLayout(output_layout)dir_group.setLayout(dir_layout)# 文件列表self.file_list = QListWidget()self.file_list.setSelectionMode(QListWidget.MultiSelection)self.file_list.setMinimumHeight(250)# 進度條self.progress = QProgressBar()self.progress.setVisible(False)# 操作按鈕btn_layout = QHBoxLayout()self.scan_btn = QPushButton("🔍 掃描文件")self.scan_btn.clicked.connect(self.scan_files)self.process_btn = QPushButton("? 處理選中")self.process_btn.clicked.connect(self.process_selected)self.process_btn.setEnabled(False)self.process_all_btn = QPushButton("🚀 處理全部")self.process_all_btn.clicked.connect(self.process_all)self.process_all_btn.setEnabled(False)btn_layout.addWidget(self.scan_btn)btn_layout.addWidget(self.process_btn)btn_layout.addWidget(self.process_all_btn)# 添加到主布局layout.addLayout(top_layout)layout.addWidget(dir_group)layout.addWidget(self.file_list)layout.addWidget(self.progress)layout.addLayout(btn_layout)main_widget.setLayout(layout)self.update_buttons()def browse_input(self):path = QFileDialog.getExistingDirectory(self, "選擇輸入目錄")if path:self.input_path.setText(path)def browse_output(self):path = QFileDialog.getExistingDirectory(self, "選擇輸出目錄")if path:self.output_path.setText(path)def scan_files(self):input_dir = self.input_path.text()if not os.path.isdir(input_dir):QMessageBox.warning(self, "錯誤", "請輸入有效的輸入目錄")returnself.audio_files = []self.file_list.clear()# 顯示掃描進度self.progress.setVisible(True)self.progress.setRange(0, 0)  # 不確定進度模式QApplication.processEvents()# 獲取選擇的格式selected_format = self.format_combo.currentText()if selected_format == "所有支持格式":extensions = list(self.supported_formats.keys())else:extensions = [k for k, v in self.supported_formats.items() if v == selected_format]for root, _, files in os.walk(input_dir):for file in files:ext = os.path.splitext(file)[1][1:].lower()if ext in extensions:self.audio_files.append(os.path.join(root, file))self.file_list.addItems([os.path.basename(f) for f in self.audio_files])self.progress.setVisible(False)self.update_buttons()QMessageBox.information(self, "完成", f"找到 {len(self.audio_files)} 個音頻文件")def process_selected(self):selected = self.file_list.selectedItems()if not selected:QMessageBox.warning(self, "警告", "請先選擇要處理的文件")returnindices = [self.file_list.row(item) for item in selected]self.process_files(indices)def process_all(self):if not self.audio_files:QMessageBox.warning(self, "警告", "沒有可處理的文件")returnreply = QMessageBox.question(self, "確認", f"確定要處理所有 {len(self.audio_files)} 個文件嗎?",QMessageBox.Yes | QMessageBox.No)if reply == QMessageBox.Yes:self.process_files(range(len(self.audio_files)))def process_files(self, indices):method = self.method_combo.currentText().split()[0].lower()input_dir = self.input_path.text()output_dir = self.output_path.text() or os.path.join(input_dir, "cleaned_audio")total = len(indices)success = 0failed = 0self.progress.setVisible(True)self.progress.setMaximum(total)self.progress.setValue(0)os.makedirs(output_dir, exist_ok=True)for i, idx in enumerate(indices, 1):input_path = self.audio_files[idx]filename = os.path.basename(input_path)output_path = os.path.join(output_dir, filename)try:ext = os.path.splitext(input_path)[1][1:].lower()if method == "ffmpeg":result = self.process_with_ffmpeg(input_path, output_path)elif method == "mutagen":result = self.process_with_mutagen(input_path, output_path, ext)else:  # 二進制處理result = self.process_binary(input_path, output_path, ext)if result:success += 1else:failed += 1except Exception as e:print(f"處理失敗 {input_path}: {str(e)}")failed += 1self.progress.setValue(i)QApplication.processEvents()self.progress.setVisible(False)QMessageBox.information(self, "完成",f"處理完成!\n成功: {success}\n失敗: {failed}\n輸出目錄: {output_dir}")def process_with_ffmpeg(self, input_path, output_path):"""使用FFmpeg處理"""try:cmd = ["ffmpeg","-i", input_path,"-map", "0:a","-c:a", "copy","-map_metadata", "-1",output_path,"-y"  # 覆蓋輸出文件]subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)return Trueexcept Exception as e:print(f"FFmpeg處理失敗: {str(e)}")return Falsedef process_with_mutagen(self, input_path, output_path, ext):"""使用Mutagen處理不同格式的音頻文件"""try:# 先復制文件if input_path != output_path:shutil.copy2(input_path, output_path)# 根據格式使用不同的處理方法if ext == "mp3":audio = MP3(output_path, ID3=ID3)if audio.tags:audio.tags.delall("APIC")audio.save()elif ext == "flac":audio = FLAC(output_path)if audio.pictures:audio.clear_pictures()audio.save()elif ext == "m4a":audio = MP4(output_path)if 'covr' in audio:del audio['covr']audio.save()elif ext == "ogg":try:audio = OggOpus(output_path)except:audio = OggVorbis(output_path)if 'metadata_block_picture' in audio:del audio['metadata_block_picture']audio.save()elif ext == "wma":audio = ASF(output_path)if hasattr(audio, 'tags') and 'WM/Picture' in audio.tags:del audio.tags['WM/Picture']audio.save()return Trueexcept Exception as e:print(f"Mutagen處理失敗: {str(e)}")return Falsedef process_binary(self, input_path, output_path, ext):"""二進制方式處理(最后手段)"""try:if ext == "mp3":# MP3文件的簡單二進制處理with open(input_path, "rb") as f:data = f.read()apic_pos = data.find(b"APIC")if apic_pos == -1:if input_path != output_path:shutil.copy2(input_path, output_path)return Truenew_data = data[:apic_pos] + data[apic_pos+4:]with open(output_path, "wb") as f:f.write(new_data)return Trueelse:# 其他格式直接復制(無法二進制處理)if input_path != output_path:shutil.copy2(input_path, output_path)return Falseexcept Exception as e:print(f"二進制處理失敗: {str(e)}")return Falsedef update_buttons(self):has_files = bool(self.audio_files)self.process_btn.setEnabled(has_files)self.process_all_btn.setEnabled(has_files)def main():app = QApplication(sys.argv)# 設置應用程序樣式app.setStyle('Fusion')window = AudioCoverRemover()window.show()sys.exit(app.exec_())if __name__ == "__main__":main()

🎯 性能優化建議

  1. 多線程處理
   # 可使用QThreadPool實現多線程處理from PyQt5.QtCore import QThreadPool, QRunnableclass Worker(QRunnable):def __init__(self, task_func):super().__init__()self.task_func = task_funcdef run(self):self.task_func()
  1. 緩存機制

    • 緩存已掃描文件列表
    • 實現增量處理功能
  2. 元數據分析

    • 添加封面大小統計功能
    • 支持預覽被刪除的封面

📝 總結

本文詳細介紹了一款功能完善的音頻封面刪除工具的開發過程。通過結合PyQt5的GUI能力和Mutagen的音頻處理能力,我們實現了一個用戶友好且功能強大的應用程序。關鍵收獲包括:

  1. 音頻處理知識:深入理解了不同音頻格式的元數據存儲方式
  2. GUI開發技巧:掌握了現代化Qt界面設計方法
  3. 健壯性設計:學習了多種處理方法的兼容實現

該工具不僅具有實用價值,其開發過程也展示了Python在多媒體處理領域的強大能力。讀者可以根據實際需求進一步擴展功能,如添加音頻格式轉換、元數據編輯等特性。

擴展思考:如何將此工具集成到自動化音樂管理流水線中?能否結合機器學習自動識別并分類音樂封面?


附錄:完整代碼

文中的完整Python代碼已在前文展示,也可從GitHub倉庫獲取最新版本。建議在Python 3.7+環境中運行,并安裝所有依賴庫。

希望本文對您的音頻處理項目有所啟發!如有任何問題,歡迎在評論區討論。

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

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

相關文章

JAVA房屋租售管理系統房屋出租出售平臺房屋銷售房屋租賃房屋交易信息管理源碼

一、源碼描述 這是一套房屋租售管理源碼,基于SpringBootVue框架,后端采用JAVA開發,源碼功能完善,涵蓋了房屋租賃、房屋銷售、房屋交易等業務。 二、源碼截圖

一篇文章講清楚mysql的聚簇索引、非聚簇索引、輔助索引

聚簇索引與非聚簇索引最大的區別就是: 聚簇索引的索引和數據是存放在一起的,都是在葉子結點; 非聚簇索引的索引和數據是分開存儲的,葉子節點存放的是索引和指向數據文件的地址,通過葉子節點找到索引,再通…

使用ESPHome燒錄固件到ESP32-C3并接入HomeAssistant

文章目錄 一、安裝ESPHome二、配置ESP32-C3控制燈1.主配置文件esp32c3-luat.yaml2.基礎通用配置base.yaml3.密碼文件secret.yaml4.圍欄燈four_light.yaml5.彩燈rgb_light.yaml6.左右柱燈left_right_light.yaml 三、安裝固件四、HomeAssistant配置ESPHome1.直接訪問2.配置ESPHom…

什么是變量提升?

變量提升(Hoisting) 是 JavaScript 引擎在代碼執行前的一個特殊行為,它會將變量聲明和函數聲明自動移動到當前作用域的頂部。但需要注意的是,只有聲明會被提升,賦值操作不會提升。 ??核心概念?? 變量聲明提升&…

【萬字長文】深入淺出 LlamaIndex 和 LangChain:從RAG到智能體,輕松駕馭LLM應用開發

Langchain系列文章目錄 01-玩轉LangChain:從模型調用到Prompt模板與輸出解析的完整指南 02-玩轉 LangChain Memory 模塊:四種記憶類型詳解及應用場景全覆蓋 03-全面掌握 LangChain:從核心鏈條構建到動態任務分配的實戰指南 04-玩轉 LangChai…

2025 后端自學UNIAPP【項目實戰:旅游項目】3、API接口請求封裝,封裝后的簡單測試以及實際使用

一、創建請求封裝目錄 選中自己的項目,右鍵鼠標---->新建---->目錄---->名字自定義【我的是api】 二、創建兩個js封裝文件 選中封裝的目錄,右鍵鼠標---->新建---->js文件---->名字自定義【我的兩個js文件分別是my_http和my_api】 三…

autojs和冰狐智能輔助該怎么選擇?

最近打算做自動化腳本,在autojs和冰狐智能輔助中做選擇,不知道該怎么選。沒辦法只能花費大量時間仔細研究了autojs和冰狐智能輔助,綜合考慮功能需求、開發復雜度、編程經驗及項目規模等因素。以下是兩者的核心對比及選擇建議,僅供…

python24-匿名函數

課程:B站大學 記錄python學習,直到學會基本的爬蟲,使用python搭建接口自動化測試就算學會了,在進階webui自動化,app自動化 匿名函數 匿名函數實踐是檢驗真理的唯一標準 匿名函數 匿名函數是指沒有名字的函數&#xff…

Android 查看 Logcat (可純手機方式 無需電腦)

安裝 Logcat Reader Github Google Play 如果有電腦 使用其ADB方式可執行如下命令 后續無需安裝Termux # 使用 ADB 授予 android.permission.READ_LOGS 權限給 Logcat Reader adb shell "pm grant com.dp.logcatapp android.permission.READ_LOGS && am force-…

驅動開發硬核特訓 · Day 30(上篇):深入理解 I2C 總線驅動模型(以 at24 EEPROM 為例)

作者:嵌入式Jerry 視頻教程請關注 B 站:“嵌入式Jerry” 一、寫在前面 在上一階段我們已經深入理解了字符設備驅動與設備模型之間的結合方式、sysfs 的創建方式以及平臺驅動模型的實際運用。今天我們邁入總線驅動模型的世界,聚焦于 I2C 總線…

超詳細講解注意力機制、自注意力機制、多頭注意力機制、通道注意力機制、空間注意力機制

在如今的機器學習和深度學習領域,注意力機制絕對是一個熱度居高不下的話題。事實上,注意力機制并不是一個全新的概念,早在多年前就已經被提出并應用。比如在圖像分類任務中,SENet 和 ECA-Net 等模型中都運用了注意力機制&#xff…

Wireshark基本使用

本文會對Wireshark做簡單介紹,帶大家熟悉一下Wireshark的界面,以及如何使用過濾器。 接著會帶大家查看TCP五層模型下,帶大家回顧各層首部的格式。 最后會演示 Wireshark 如何抓取三次握手和四次揮手包的過程。 目錄 一.Wireshark簡介 二…

加速項目落地(Trae編輯器)

目錄 vscode安裝python支持 vscode常用插件 Trae編輯器 兩個界面合成 補充(QT開發的繁瑣) AI編程哪家強?Cursor、Trae深度對比,超詳細! - 知乎 Trae兼容vscode的插件,我們可以先在vscode里面裝好再一…

stable diffusion的attention-map:提取和可視化跨注意力圖

項目: wooyeolbaek/attention-map-diffusers: 🚀 Cross attention map tools for huggingface/diffusers 參考:【可視化必備技能(1)】SD / Flux 文生圖模型的 Attention Map 可視化-CSDN博客

多環串級PID

文章目錄 為什么要多環程序主函數內環外環 雙環PID調參內環Kp調法Ki調法 外環Kp 以一定速度到達指定位置封裝 為什么要多環 單環只能單一控制速度或者位置,如果想要同時控制多個量如速度,位置,角度,就需要多個PID 速度環一般PI…

基于Kubernetes的Apache Pulsar云原生架構解析與集群部署指南(上)

#作者:閆乾苓 文章目錄 概念和架構概述主要特點消息傳遞核心概念Pulsar 的消息模型Pulsar 的消息存儲與分發Pulsar 的高級特性架構BrokerBookKeeperZooKeeper 概念和架構 概述 Pulsar 是一個多租戶、高性能的服務器到服務器消息傳遞解決方案。Pulsar 最初由雅虎開…

電子電氣架構 --- 如何有助于提安全性并減少事故

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 鈍感力的“鈍”,不是木訥、遲鈍,而是直面困境的韌勁和耐力,是面對外界噪音的通透淡然。 生活中有兩種人,一種人格外在意別人的眼光;另一種人無論…

rest_framework學習之認證 權限

權限 DRF提供如下幾種常見權限: IsAuthenticated, 認證通過 IsAdminUser, 管理員權限 IsAuthenticatedOrReadOnly, 登錄用戶增刪改 非登錄用戶只能查詢 AllowAny,無需認證(默認) 在rest_framework的APIView基礎類中&#xf…

【使用switch結構輸出季節】2021-11-23

緣由用switch語句設計程序一年有12個月-編程語言-CSDN問答 void 使用switch結構輸出季節(int y) {//緣由https://ask.csdn.net/questions/7577096?spm1005.2025.3001.5141std::cout << y << "\t";switch (y){case 3: case 4: case 5:std::cout <<…

主備Smart Link + Monitor Link組網技術詳細配置

1.實驗拓撲 2.使用設備 eNSP模擬建議下行設備三臺使用S3700模擬&#xff08;全部使用S5700可能會出現流量丟失等異常問題。&#xff09; 3.實驗配置 [SW1]dis cu # sysname SW1 # vlan batch 100 110 # interface Ethernet0/0/1port link-type accessport default vlan 100 …