【Python开源】深度解析:一款高效音频封面批量删除工具的设计与实现
🎵 【Python开源】深度解析:一款高效音频封面批量删除工具的设计与实现
🌈 个人主页:创客白泽 - CSDN博客
🔥 系列专栏:🐍《Python开源项目实战》
💡 热爱不止于代码,热情源自每一个灵感闪现的夜晚。愿以开源之火,点亮前行之路。
👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦
📖 概述
在数字音乐管理过程中,音频文件内嵌的封面图片往往会占用额外存储空间,特别是当我们需要批量处理大量音频文件时。本文介绍一款基于Python和PyQt5开发的跨平台音频封面删除工具,它支持多种音频格式(MP3、FLAC、M4A、OGG、WMA),提供三种不同的处理方式,并具备友好的图形用户界面。
本工具不仅能有效移除音频文件中的封面数据,还能保持音频质量无损,是音乐收藏家和数字资产管理者的实用工具。下面我们将从功能、实现原理、代码解析等多个维度进行详细介绍。
🛠️ 功能特点
-
多格式支持:
- MP3 (ID3标签)
- FLAC (Vorbis注释)
- M4A/MP4 (iTunes元数据)
- OGG (Vorbis/Opus)
- WMA (ASF容器)
-
三种处理方式:
- Mutagen库(推荐):Python专用音频元数据处理库
- FFmpeg:专业音视频处理工具
- 二进制处理:最后手段的直接文件操作
-
智能文件管理:
- 拖放文件夹支持
- 自动扫描子目录
- 可选输出目录设置
- 文件类型过滤
-
可视化操作:
- 进度条显示
- 处理结果统计
- 错误处理机制
🖼️ 界面展示
图1:软件主界面,包含目录设置、文件列表和操作按钮
图2、图3:文件处理进度显示
🧰 使用说明
1. 准备工作
- 安装Python 3.7+
- 安装依赖库:
pip install PyQt5 mutagen
- (可选) 如需使用FFmpeg方式,需提前安装FFmpeg并加入系统PATH
2. 操作步骤
- 选择输入目录:点击"浏览"按钮或直接拖放文件夹到输入框
- 设置输出目录(可选):默认为输入目录下的"cleaned_audio"文件夹
- 选择处理方式:
- Mutagen:推荐方式,处理速度快且稳定
- FFmpeg:适合复杂音频文件
- 二进制:最后手段,兼容性较差
- 扫描文件:点击"扫描文件"按钮获取目录下所有支持的音频文件
- 选择处理范围:
- “处理选中”:仅处理列表中选中的文件
- “处理全部”:批量处理所有扫描到的文件
- 查看结果:处理完成后会显示成功/失败统计,处理后的文件保存在输出目录
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. 设计亮点
- 多方法兼容处理:
- 提供三种不同实现方式,确保最大兼容性
- 自动选择最适合当前文件的方法
- 现代化UI设计:
- 自定义样式表美化界面
- 响应式布局适应不同分辨率
- 进度反馈增强用户体验
- 健壮的错误处理:
- 捕获并记录各种处理异常
- 不影响整体批处理流程
- 跨平台支持:
- 兼容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()
🎯 性能优化建议
- 多线程处理:
# 可使用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()
-
缓存机制:
- 缓存已扫描文件列表
- 实现增量处理功能
-
元数据分析:
- 添加封面大小统计功能
- 支持预览被删除的封面
📝 总结
本文详细介绍了一款功能完善的音频封面删除工具的开发过程。通过结合PyQt5的GUI能力和Mutagen的音频处理能力,我们实现了一个用户友好且功能强大的应用程序。关键收获包括:
- 音频处理知识:深入理解了不同音频格式的元数据存储方式
- GUI开发技巧:掌握了现代化Qt界面设计方法
- 健壮性设计:学习了多种处理方法的兼容实现
该工具不仅具有实用价值,其开发过程也展示了Python在多媒体处理领域的强大能力。读者可以根据实际需求进一步扩展功能,如添加音频格式转换、元数据编辑等特性。
扩展思考:如何将此工具集成到自动化音乐管理流水线中?能否结合机器学习自动识别并分类音乐封面?
附录:完整代码
文中的完整Python代码已在前文展示,也可从GitHub仓库获取最新版本。建议在Python 3.7+环境中运行,并安装所有依赖库。
希望本文对您的音频处理项目有所启发!如有任何问题,欢迎在评论区讨论。
相关文章:

【Python开源】深度解析:一款高效音频封面批量删除工具的设计与实现
🎵 【Python开源】深度解析:一款高效音频封面批量删除工具的设计与实现 🌈 个人主页:创客白泽 - CSDN博客 🔥 系列专栏:🐍《Python开源项目实战》 💡 热爱不止于代码,热情…...

OpenStack Yoga版安装笔记(26)实例元数据笔记
一、实例元数据概述 1.1 元数据 (官方文档:Metadata — nova 25.2.2.dev5 documentation) Nova 通过一种叫做元数据(metadata)的机制向其启动的实例提供配置信息。这些机制通常通过诸如 cloud-init 这样的初始化软件…...

【Linux】swap交换分区管理
目录 一、Swap 交换分区的功能 二、swap 交换分区的典型大小的设置 2.1 查看交换分区的大小 2.1.1 free 2.1.2 cat /proc/swaps 或 swapon -s 2.1.3 top 三、使用交换分区的整体流程 3.1 案例一 3.2 案例二 一、Swap 交换分区的功能 计算机运行一个程序首先会将外存&am…...
Spring IoC (Inversion of Control) 控制反转是什么?
我们分析一下 IoC (Inversion of Control) 控制反转的核心思想。 核心思想: IoC 是一种设计原则(Design Principle),它描述了一种软件设计模式,其中组件(对象)的创建、依赖关系的管理和生命周…...
互联网大厂Java求职面试:核心技术点深度解析
互联网大厂Java求职面试:核心技术点深度解析 在互联网大厂的Java岗位面试中,技术总监级别的面试官通常会从实际业务场景出发,层层深入地考察候选人的技术能力。本文通过一个严肃专业的技术总监与搞笑但有技术潜力的程序员郑薪苦之间的互动对…...

VirtualBox 创建虚拟机并安装 Ubuntu 系统详细指南
VirtualBox 创建虚拟机并安装 Ubuntu 系统详细指南 一、准备工作1. 下载 Ubuntu 镜像2. 安装 VirtualBox二、创建虚拟机1. 新建虚拟机2. 分配内存3. 创建虚拟硬盘三、配置虚拟机1. 加载 Ubuntu 镜像2. 调整处理器核心数(可选)3. 启用 3D 加速(图形优化)四、安装 Ubuntu 系统…...
【算法基础】递归算法 - JAVA
一、递归基础 1.1 什么是递归算法 递归算法是一种通过函数调用自身来解决问题的方法。简单来说,就是"自己调用自己"。递归将复杂问题分解为同类的更简单子问题,直到达到易于直接解决的基本情况。 1.2 递归的核心要素 递归算法由两个关键部…...

触想CX-3588工控主板应用于移动AI数字人,赋能新型智能交互
一、行业发展背景 随着AI智能、自主导航和透明屏显示等技术的不断进步,以及用户对“拟人化”、“沉浸式”交互体验的期待,一种新型交互终端——“移动AI数字人”正在加速实现规模化商用。 各大展厅展馆、零售导购、教学政务甚至家庭场景中,移…...

【深入浅出MySQL】之数据类型介绍
【深入浅出MySQL】之数据类型介绍 MySQL中常见的数据类型一览为什么需要如此多的数据类型数值类型BIT(M)类型INT类型TINYINT类型BIGINT类型浮点数类型float类型DECIMAL(M,D)类型区别总结 字符串类型CHAR类型VARCHAR(M)类型 日期和时间类型enum和set类型 …...

Vue3响应式:effect作用域
# Vue3响应式: effect作用域 什么是Vue3响应式? 是一款流行的JavaScript框架,它提供了响应式和组件化的视图组织方式。在Vue3中,响应式是一种让数据变化自动反映在视图上的机制。当数据发生变化时,与之相关的视图会自动更新。 作用…...

25.5.4数据结构|哈夫曼树 学习笔记
知识点前言 一、搞清楚概念 ●权:___________ ●带权路径长度:__________ WPL所有的叶子结点的权值*路径长度之和 ●前缀编码:____________ 二、构造哈夫曼树 n个带权值的结点,构造哈夫曼树算法: 1、转化成n棵树组成的…...

RabbitMQ 深度解析:从核心组件到复杂应用场景
一.RabbitMQ简单介绍 消息队列作为分布式系统中不可或缺的组件,承担着解耦系统组件、保障数据可靠传输、提高系统吞吐量等重要职责。在众多消息队列产品中,RabbitMQ 凭借其可靠性和丰富的特性,在企业级应用中获得了广泛应用。 二.RabbitMQ …...
Python高级技巧及案例分析:提升编程能力的实践指南
目录 Python高级技巧及案例分析:提升编程能力的实践指南1. Python高级特性概述2. 函数式编程技巧2.1 高阶函数2.2 函数柯里化2.3 不可变数据结构3. 元编程与反射3.1 动态属性访问3.2 类装饰器3.3 元类应用4. 并发与异步编程4.1 多线程与线程池4.2 协程与asyncio4.3 多进程处理…...

【Linux笔记】系统的延迟任务、定时任务极其相关命令(at、crontab极其黑白名单等)
一、延时任务 1、概念 延时任务(Delayed Jobs)通常指在指定时间或特定条件满足后执行的任务。常见的实现方式包括 at 和 batch 命令,以及结合 cron 的调度功能。 2、命令 延时任务的命令最常用的是at命令,第二大节会详细介绍。…...
转换算子和行动算子的区别
转换算子和行动算子主要是在分布式计算框架(如 Apache Spark)里常用的概念,它们在功能、执行机制、返回结果等方面存在明显区别,以下为你详细介绍: 定义与功能 返回结果 如何在使用转换算子和行动算子时避免出现内存溢…...

使用阿里AI的API接口实现图片内容提取功能
参考链接地址:如何使用Qwen-VL模型_大模型服务平台百炼(Model Studio)-阿里云帮助中心 在windows下,使用python语言测试,版本:Python 3.8.9 一. 使用QVQ模型解决图片数学难题 import os import base64 import requests# base 64 …...
告别散乱的 @ExceptionHandler:实现统一、可维护的 Spring Boot 错误处理
Spring Boot 的异常处理机制一直都烂得可以。即便到了 2025 年,有了这么多进步和新版本,开发者们发现自己还是在跟 ControllerAdvice、分散各处的 ExceptionHandler 方法以及五花八门的响应结构较劲。这真的是一团糟。 无论你是在构建 REST API、微服务…...
MariaDB 与 MySQL 的关系:从同源到分道扬镳
MariaDB 与 MySQL 的关系:从同源到分道扬镳 1. 起源:MySQL 的辉煌与危机 MySQL 是最流行的开源关系型数据库之一,由瑞典公司 MySQL AB 开发,并于 1995 年 首次发布。由于其高性能、易用性和开源特性,MySQL 迅速成为 L…...

从零开始搭建你的个人博客:使用 GitHub Pages 免费部署静态网站
🌐 从零开始搭建你的个人博客:使用 GitHub Pages 免费部署静态网站 在互联网时代,拥有一个属于自己的网站不仅是一种展示方式,更是一种技术能力的体现。今天我们将一步步学习如何通过 GitHub Pages 搭建一个免费的个人博客或简历…...

C#串口通信
在C#中使用串口通信比较方便,.Net 提供了现成的类, SerialPort类。 本文不对原理啥的进行介绍,只介绍SerialPort类的使用。 SerialProt类内部是调用了CreateFile,WriteFile等WinAPI函数来实现串口通信。 在后期的Windows编程系…...
Qt 显示QRegExp 和 QtXml 不存在问题
QRegExp 和 QtXml 问题 在Qt6 中 已被弃用; 1)QRegExp 已被弃用,改用 QRegularExpression Qt5 → Qt6 重大变更:QRegExp 被移到了 Qt5Compat 模块,默认不在 Qt6 核心模块中。 错误类型解决方法QRegExp 找不到改用 Q…...
【训练】Qwen2.5VL 多机多卡 Grounding Box定位
之前的相关文章: 【深度学习】LLaMA-Factory微调sft Qwen2-VL进行印章识别 https://www.dong-blog.fun/post/1661 使用LLaMA-Factory微调sft Qwen2-VL-7B-Instruct https://www.dong-blog.fun/post/1762 构建最新的LLaMA-Factory镜像 https://www.dong-blog.f…...

服务器配置llama-factory问题解决
在配置运行llama-factory,环境问题后显示环境问题。这边给大家附上连接,我们的是liunx环境但是还是一样的。大家也记得先配置虚拟环境。 LLaMA-Factory部署以及微调大模型_llamafactory微调大模型-CSDN博客 之后大家看看遇到的问题是不是我这样。 AI搜索…...

Spring Boot + Vue 实现在线视频教育平台
一、项目技术选型 前端技术: HTML CSS JavaScript Vue.js 前端框架 后端技术: Spring Boot 轻量级后端框架 MyBatis 持久层框架 数据库: MySQL 5.x / 8.0 开发环境: IDE:Eclipse / IntelliJ IDEA JDK&…...

使用Jmeter进行核心API压力测试
最近公司有发布会,需要对全链路比较核心的API的进行压测,今天正好分享下压测软件Jmeter的使用。 一、什么是Jmeter? JMeter 是 Apache 旗下的基于 Java 的开源性能测试工具。最初被设计用于 Web 应用测试,现已扩展到可测试多种不同的应用程…...

JavaScript中数组和对象不同遍历方法的顺序规则
在JavaScript中,不同遍历方法的顺序规则和适用场景存在显著差异。以下是主要方法的遍历顺序总结: 一、数组遍历方法 for循环 • 严格按数组索引顺序遍历(0 → length-1) • 支持break和continue中断循环 • 性能最优,…...
【机器学习-线性回归-5】多元线性回归:概念、原理与实现详解
线性回归是机器学习中最基础且广泛应用的算法之一,而多元线性回归则是其重要扩展。本文将全面介绍多元线性回归的核心概念、数学原理及多种实现方式,帮助读者深入理解这一强大的预测工具。 1. 多元线性回归概述 1.1 什么是多元线性回归 多元线性回归(…...
【软件设计师:数据结构】1.数据结构基础(一)
一 线性表 1.线性表定义 线性表是n个元素的有限序列,通常记为(a1,a2,…,an)。 特点: 存在惟一的表头和表尾。除了表头外,表中的每一个元素均只有惟一的直接前驱。除了表尾外,表中的每一个元素均只有惟一的直接后继。2.线性表的存储结构 (1)顺序存储 是用一组地址连续…...
简单面试提问
Nosql非关系型数据库: Mongodb:开源、json形式储存、c编写 Redis:key-value形式储存,储存在内存,c编写 关系型数据库: sqlite;:轻量型、0配置、磁盘存储、支持多种语言 mysql:开源…...
探秘数据中台:五大核心平台的功能全景解析
数据中台作为企业数据资产的 “智慧中枢”,通过整合数据处理全流程的核心功能,实现数据价值的深度挖掘与高效应用。以下从五大核心平台出发,全面拆解数据中台的功能架构与应用价值。 一、数据可视化平台:让数据 “开口说话” 1.…...