'''
视频抽帧工具,所有视频所在目录以及抽帧图片保存路径
单个视频抽帧操作步骤:
选择文件路径->选择保存路径->拖动跳帧间隔->点击抽取帧
批量视频抽帧操作步骤:
选择文件夹路径->选择保存路径->拖动跳帧间隔->点击抽取帧
'''
import sys
import cv2
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton, QLabel, QFileDialog, QSlider, QHBoxLayout, QProgressBar
from PyQt5.QtCore import Qt
import os
from shortuuid import uuid
IMG_FORMATS = 'bmp', 'dng', 'jpeg', 'jpg', 'mpo', 'png', 'tif', 'tiff', 'webp', 'pfm'
VID_FORMATS = 'asf', 'avi', 'gif', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mpg', 'ts', 'wmv'
def is_img(file_path: str):
return file_path.split('.')[-1].lower() in IMG_FORMATS
def is_video(file_path: str):
return file_path.split('.')[-1].lower() in VID_FORMATS
class VideoFrameExtractor(QMainWindow):
def __init__(self):
super().__init__()
self.videoCapture = None
self.frame_counter = 0
self.save_path = ""
self.central_widget = QWidget(self)
self.setCentralWidget(self.central_widget)
self.layout = QVBoxLayout()
self.central_widget.setLayout(self.layout)
self.progress_bar = QProgressBar()
self.layout.addWidget(self.progress_bar)
self.video_label = QLabel(self)
self.layout.addWidget(self.video_label)
file_select_layout = QHBoxLayout()
self.file_button = QPushButton("选择文件路径", self)
self.file_button.clicked.connect(self.openFile)
file_select_layout.addWidget(self.file_button)
self.dir_button = QPushButton("选择文件夹路径", self)
self.dir_button.clicked.connect(self.openFileDir)
file_select_layout.addWidget(self.dir_button)
self.layout.addLayout(file_select_layout)
save_start_layout = QHBoxLayout()
self.save_button = QPushButton("选择保存路径", self)
self.save_button.clicked.connect(self.openSavePath)
save_start_layout.addWidget(self.save_button)
self.extract_button = QPushButton("抽取帧", self)
self.extract_button.clicked.connect(self.extractFrames)
self.extract_button.setEnabled(False)
save_start_layout.addWidget(self.extract_button)
self.layout.addLayout(save_start_layout)
param_start_layout = QHBoxLayout()
self.frame_label = QLabel("跳帧间隔: 1", self)
param_start_layout.addWidget(self.frame_label)
self.frame_slider = QSlider(Qt.Horizontal, self)
self.frame_slider.setRange(1, 100)
self.frame_slider.setValue(1)
self.frame_slider.valueChanged.connect(self.update_skip_param)
param_start_layout.addWidget(self.frame_slider)
self.layout.addLayout(param_start_layout)
self.videos = []
self.setGeometry(100, 100, 800, 100)
self.setWindowTitle("视频抽帧应用")
self.show()
def update_skip_param(self):
self.skip_param = self.frame_slider.value()
self.frame_label.setText(f"跳帧间隔: {self.skip_param}")
def openFileDir(self):
options = QFileDialog.Options()
options |= QFileDialog.ShowDirsOnly
file_dir = QFileDialog.getExistingDirectory(
self, "选择视频文件夹", "", options=options)
if file_dir:
self.extract_button.setEnabled(True)
all_files = os.listdir(file_dir)
self.videos = [os.path.join(file_dir, file)
for file in all_files if is_video(file)]
def openFile(self):
options = QFileDialog.Options()
options |= QFileDialog.ReadOnly
file_path, _ = QFileDialog.getOpenFileName(
self, "选择视频文件", "", "视频文件 (*.mp4 *.avi *.mkv)", options=options)
if file_path:
self.extract_button.setEnabled(True)
self.videos = [file_path]
def openSavePath(self):
options = QFileDialog.Options()
options |= QFileDialog.ReadOnly
directory = QFileDialog.getExistingDirectory(
self, "选择保存路径", options=options)
if directory:
self.save_path = directory
def disable_elems(self):
self.extract_button.setEnabled(False)
self.file_button.setEnabled(False)
self.dir_button.setEnabled(False)
self.save_button.setEnabled(False)
self.frame_slider.setEnabled(False)
def enable_elems(self):
self.extract_button.setEnabled(True)
self.file_button.setEnabled(True)
self.dir_button.setEnabled(True)
self.save_button.setEnabled(True)
self.frame_slider.setEnabled(True)
def handle(self):
pass
def extractFrames(self):
self.disable_elems()
frame_skip = self.frame_slider.value()
max_value = len(self.videos)
self.progress_bar.setValue(0)
for idx, video_path in enumerate(self.videos):
cap = cv2.VideoCapture(video_path)
prefix = str(uuid())
save_name = os.path.join(os.path.splitext(
os.path.basename(video_path))[0])
save_dir = os.path.join(self.save_path, save_name)
os.makedirs(save_dir, exist_ok=True)
frame_idx = -1
while True:
frame_idx += 1
cap.grab()
if frame_idx % frame_skip != 0:
continue
ret, frame = cap.retrieve()
if not ret:
break
cv2.imencode('.jpg', frame, [int(cv2.IMWRITE_JPEG_QUALITY), 100])[
1].tofile(f"{save_dir}/{prefix}_{str(frame_idx).zfill(6)}.jpg")
cap.release()
self.progress_bar.setValue(int((idx+1) / max_value * 100))
QApplication.processEvents()
self.progress_bar.repaint()
self.enable_elems()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = VideoFrameExtractor()
sys.exit(app.exec_())

- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
