SLIC超像素视频转换

1     问题描述

超像素视频生成小软件:完成一段小程序,读入一段原始视频,要求调用SLIC算法生成超像素视频。该软件要求有友好的用户界面,有功能按钮及视频显示等细节,要求能在界面上设置SLIC的输入参数。

2    超像素介绍

2.1概念

超像素概念是2003年Xiaofeng Ren提出和发展起来的图像分割技术,它是指具有相似纹理、颜色、亮度等特征的相邻像素构成的有一定视觉意义的不规则像素块。它利用像素之间特征的相似性将像素分组,用少量的超像素代替大量的像素来表达图片特征,很大程度上降低了图像后处理的复杂度,所以通常作为分割算法的预处理步骤。该技术已经被广泛的应用到图像分割、姿势估计、目标跟踪、目标识别等多个计算机视觉任务等。

2.2特点

  • 计算效率比较高

虽然计算实际的超像素组可能很昂贵,但它允许我们将图像本身的复杂性从几十万像素降低到几百像素。这些超级像素中的每一个都将包含某种感知的、理想的语义值。

  • 含有更多的感知信息

单个的像素网格其实并没有太大的感知意义,而超像素组中的像素具有共性,例如相似的颜色或纹理分布。下图展示了这种情况,单单将图中箭头所指的图像像素网格进行放大后并不能得到什么感知信息。

  • 过度分割可以减少像素损失

大多数的超像素算法都会使图像失真。这意味着图像中的大部分重要边界都被找到了;但是它的代价是生成许多无关紧要的边界。虽然这听起来像是使用超级像素的问题,但实际上它会带来优点,这种过度扩张的最终结果是-从像素网格到超级像素映射的像素损失非常小。

3     SLIC算法介绍

3.1算法原理

简单线性迭代聚类SILC(simple linear iterative clustering)是一种图像分割算法。默认情况下,该算法的唯一参数是k,约等于超像素尺寸的期望数量。对于CIELAB彩色空间的图像,在相隔S像素上采样得到初始聚类中心。为了产生大致相同尺寸的超像素,格点的距离是S=N/k−−−−√S=N/k。中心需要被移到3x3领域内的最低梯度处,这样做是为了避免超像素中心在边缘和噪声点上。

  接下来为每一个像素ii设置最近的聚类中心,该聚类中心的搜索区域要覆盖该像素的位置。这是本算法加速的关键,因为通过限制搜索区域的大小减小了距离计算的数量,并且相对于传统的k-means聚类算法有显著的速度优势,因为后者的每个像素都必须和所有的聚类中心进行比较。一个超像素的预期空间范围是约为SxS的区域,这里对于相似像素的搜索是在超像素中心的2Sx2S区域完成。

  一旦每个像素被关联到最近的聚类中心后,就通过求聚类中心所有像素的均值来执行聚类中心的更新。使用L2L2范数计算前一个聚类中心和当前聚类中心的残差。assignment和update步骤被重复迭代直到误差收敛,但是我们发现对于大多数图像10次迭代就够了。

3.2算法步骤

  • 步骤1-初始化种子点(聚类中心)按照设定的超像素组个数,在图像内均匀的分配一些种子点。假设图片总共有含有N 个像素点,预分割为 K 个相同尺寸的超像素,那么每个超像素的大小为N/ K ,则相邻种子点的距离或者步长近似可以表示为S=sqrt(N/K)。
  • 步骤2-在种子点的n*n邻域内重新选择种子点(一般取n=3)。首先计算该邻域内所有像素点的梯度值;然后将种子点移到该邻域内梯度最小的地方。这样做的目的是为了避免种子点落在梯度较大的轮廓边界上,以免影响后续聚类效果。
  • 步骤3-在每个种子点周围的邻域内为每个像素点分配类标签(即属于哪个聚类中心)。和标准的k-means在整张图中搜索不同,SLIC的搜索范围限制为2S_2S,可以加速算法收敛,具体的区别如下图所示。需要注意的是,期望的超像素尺寸为S_S,但是搜索的范围是2S*2S。

图3-1 标准K-means和SLIC搜索范围区别

  • 步骤4-距离度量。具体包括颜色和空间距离。对于每个搜索到的像素点,分别计算它到该种子点之间的距离。具体的距离公式如下所示:

图3-2 距离公式

3.3算法调用

slic(image,
n_segments=100,
compactness=10,
max_iter=10,
sigma=0,
spacing=None,
multichannel=True,
convert2lab=None,
enforce_connectivity=True,
min_size_factor=0.5,
max_size_factor=3,
slic_zero=False)

image 2D, 3D or 4D 数组

n_segments超像素近似个数

max_iter最大k均值迭代次数

sigma高斯平滑核宽

本实验中支持调整的参数有两个:n_segments, sigma

4     程序说明

4.1主程序流程图

图4-1 主程序流程图(不含UI界面事件)

4.2主要函数说明

表4-1 主要函数说明

函数名 功能 输入 输出
Read_video(path,file_name) 把视频转化为很多图片,并且提取音频文件 视频路径、文件名 帧率、尺寸
Super_pixel(path,numSegments,numSigma,outputDir,outputFilename) 把单张图片转为超像素图像 图片路径、超像素个数、平滑度、输出路径、输出文件名
Picture_conversion(inputDir,outputDir,numSegments,numSigma) 调用Super_pixel批量把图片转化成超像素图像 源目录、输出目录、超像素个数、平滑度
Create_video(videoDir,fps,height,width) 把批量的超像素图像合成为视频并进行压缩,加入音频 视频路径、帧率、尺寸

5     功能测试

图形界面有3个可修改的参数和4个按钮,如图5-1

图5-1 原视频

点击开始转换按钮,经过一段漫长的等待时间,显示转换成功

图5-2 转换成功

注意到第二个播放按钮变为可用,点击播放转换后的视频

图5-3 播放转换后的视频

不同参数情况下的转换结果:

图5-4 n_segments=100,sigma=0

图5-5 n_segments=100,sigma=5

图5-6 n_segments=100,sigma=10

图5-7 n_segments=500,sigma=5

图5-4 n_segments=1000,sigma=5

6     实验总结

本次实验有一定的挑战性,一方面是还没用PyQt5写过界面,另一方面,平时做的都是单张图像处理,还没做过视频处理,有些不知所措。查阅资料后发现没有能够直接对视频对象进行转换的,所以就确定了基本思路:把每一帧图像单独拆分出来进行超像素转换,再组合成新的视频,然后把原视频的音频信息再加入到新视频中。碰到的难点主要在单张图像的超像素转换和存储、视频的压缩以及在图形化界面中的视频播放。经过一番折腾也算是基本完成了任务需求,也积累了不少相关的开发经验,受益良多,很有意义。

7     源码

test.ui

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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SuperpixelVideo</class>
<widget class="QMainWindow" name="SuperpixelVideo">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1120</width>
<height>817</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QLabel" name="n_segments">
<property name="geometry">
<rect>
<x>80</x>
<y>40</y>
<width>151</width>
<height>51</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>75</weight>
<bold>true</bold>
<underline>false</underline>
</font>
</property>
<property name="text">
<string>超像素个数</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
</widget>
<widget class="QLabel" name="sigma">
<property name="geometry">
<rect>
<x>140</x>
<y>90</y>
<width>81</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>75</weight>
<bold>true</bold>
<underline>false</underline>
</font>
</property>
<property name="text">
<string>平滑度</string>
</property>
</widget>
<widget class="QPushButton" name="cofirmButton">
<property name="geometry">
<rect>
<x>830</x>
<y>30</y>
<width>221</width>
<height>91</height>
</rect>
</property>
<property name="palette">
<palette>
<active>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>85</red>
<green>170</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>85</red>
<green>170</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>85</red>
<green>170</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="font">
<font>
<pointsize>28</pointsize>
<weight>75</weight>
<italic>false</italic>
<bold>true</bold>
<strikeout>false</strikeout>
</font>
</property>
<property name="text">
<string>开始转换</string>
</property>
</widget>
<widget class="QLabel" name="videoPath">
<property name="geometry">
<rect>
<x>110</x>
<y>0</y>
<width>121</width>
<height>51</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>75</weight>
<bold>true</bold>
<underline>false</underline>
</font>
</property>
<property name="text">
<string>视频路径</string>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit01">
<property name="geometry">
<rect>
<x>240</x>
<y>10</y>
<width>351</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit02">
<property name="geometry">
<rect>
<x>240</x>
<y>50</y>
<width>81</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit03">
<property name="geometry">
<rect>
<x>240</x>
<y>90</y>
<width>81</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
</widget>
<widget class="QPushButton" name="button1">
<property name="geometry">
<rect>
<x>220</x>
<y>710</y>
<width>111</width>
<height>51</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>播放</string>
</property>
</widget>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>130</y>
<width>511</width>
<height>561</height>
</rect>
</property>
<layout class="QVBoxLayout" name="videoplayer1"/>
</widget>
<widget class="QWidget" name="verticalLayoutWidget_2">
<property name="geometry">
<rect>
<x>580</x>
<y>130</y>
<width>521</width>
<height>561</height>
</rect>
</property>
<layout class="QVBoxLayout" name="videoplayer2"/>
</widget>
<widget class="QPushButton" name="button2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>780</x>
<y>710</y>
<width>111</width>
<height>51</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>播放</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>620</x>
<y>60</y>
<width>211</width>
<height>41</height>
</rect>
</property>
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>340</x>
<y>100</y>
<width>72</width>
<height>15</height>
</rect>
</property>
<property name="text">
<string>0为不平滑</string>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>340</x>
<y>60</y>
<width>72</width>
<height>15</height>
</rect>
</property>
<property name="text">
<string>近似</string>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>610</x>
<y>20</y>
<width>91</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>单左斜杠'/'</string>
</property>
</widget>
<widget class="QPushButton" name="button3">
<property name="geometry">
<rect>
<x>500</x>
<y>710</y>
<width>111</width>
<height>51</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>停止</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1120</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

Ui_test.py

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
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'e:testtest.ui'
#
# Created by: PyQt5 UI code generator 5.13.0
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_SuperpixelVideo(object):
def setupUi(self, SuperpixelVideo):
SuperpixelVideo.setObjectName("SuperpixelVideo")
SuperpixelVideo.resize(1120, 817)
font = QtGui.QFont()
font.setPointSize(9)
SuperpixelVideo.setFont(font)
self.centralwidget = QtWidgets.QWidget(SuperpixelVideo)
self.centralwidget.setObjectName("centralwidget")
self.n_segments = QtWidgets.QLabel(self.centralwidget)
self.n_segments.setGeometry(QtCore.QRect(80, 40, 151, 51))
self.n_segments.setMinimumSize(QtCore.QSize(16, 16))
font = QtGui.QFont()
font.setPointSize(16)
font.setBold(True)
font.setUnderline(False)
font.setWeight(75)
self.n_segments.setFont(font)
self.n_segments.setTextFormat(QtCore.Qt.AutoText)
self.n_segments.setObjectName("n_segments")
self.sigma = QtWidgets.QLabel(self.centralwidget)
self.sigma.setGeometry(QtCore.QRect(140, 90, 81, 31))
font = QtGui.QFont()
font.setPointSize(16)
font.setBold(True)
font.setUnderline(False)
font.setWeight(75)
self.sigma.setFont(font)
self.sigma.setObjectName("sigma")
self.cofirmButton = QtWidgets.QPushButton(self.centralwidget)
self.cofirmButton.setGeometry(QtCore.QRect(830, 30, 221, 91))
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(85, 170, 255))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button, brush)
brush = QtGui.QBrush(QtGui.QColor(85, 170, 255))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Button, brush)
brush = QtGui.QBrush(QtGui.QColor(85, 170, 255))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Button, brush)
self.cofirmButton.setPalette(palette)
font = QtGui.QFont()
font.setPointSize(28)
font.setBold(True)
font.setItalic(False)
font.setWeight(75)
font.setStrikeOut(False)
self.cofirmButton.setFont(font)
self.cofirmButton.setObjectName("cofirmButton")
self.videoPath = QtWidgets.QLabel(self.centralwidget)
self.videoPath.setGeometry(QtCore.QRect(110, 0, 121, 51))
font = QtGui.QFont()
font.setPointSize(16)
font.setBold(True)
font.setUnderline(False)
font.setWeight(75)
self.videoPath.setFont(font)
self.videoPath.setObjectName("videoPath")
self.lineEdit01 = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit01.setGeometry(QtCore.QRect(240, 10, 351, 31))
font = QtGui.QFont()
font.setPointSize(14)
self.lineEdit01.setFont(font)
self.lineEdit01.setObjectName("lineEdit01")
self.lineEdit02 = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit02.setGeometry(QtCore.QRect(240, 50, 81, 31))
font = QtGui.QFont()
font.setPointSize(14)
self.lineEdit02.setFont(font)
self.lineEdit02.setObjectName("lineEdit02")
self.lineEdit03 = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit03.setGeometry(QtCore.QRect(240, 90, 81, 31))
font = QtGui.QFont()
font.setPointSize(14)
self.lineEdit03.setFont(font)
self.lineEdit03.setObjectName("lineEdit03")
self.button1 = QtWidgets.QPushButton(self.centralwidget)
self.button1.setGeometry(QtCore.QRect(220, 710, 111, 51))
font = QtGui.QFont()
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.button1.setFont(font)
self.button1.setObjectName("button1")
self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(20, 130, 511, 561))
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.videoplayer1 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.videoplayer1.setContentsMargins(0, 0, 0, 0)
self.videoplayer1.setObjectName("videoplayer1")
self.verticalLayoutWidget_2 = QtWidgets.QWidget(self.centralwidget)
self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(580, 130, 521, 561))
self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2")
self.videoplayer2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2)
self.videoplayer2.setContentsMargins(0, 0, 0, 0)
self.videoplayer2.setObjectName("videoplayer2")
self.button2 = QtWidgets.QPushButton(self.centralwidget)
self.button2.setEnabled(False)
self.button2.setGeometry(QtCore.QRect(780, 710, 111, 51))
font = QtGui.QFont()
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.button2.setFont(font)
self.button2.setObjectName("button2")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(620, 60, 211, 41))
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
self.label.setPalette(palette)
font = QtGui.QFont()
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.label.setFont(font)
self.label.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label.setText("")
self.label.setObjectName("label")
self.label_2 = QtWidgets.QLabel(self.centralwidget)
self.label_2.setGeometry(QtCore.QRect(340, 100, 72, 15))
self.label_2.setObjectName("label_2")
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setGeometry(QtCore.QRect(340, 60, 72, 15))
self.label_3.setObjectName("label_3")
self.label_4 = QtWidgets.QLabel(self.centralwidget)
self.label_4.setGeometry(QtCore.QRect(610, 20, 91, 16))
self.label_4.setObjectName("label_4")
self.button3 = QtWidgets.QPushButton(self.centralwidget)
self.button3.setGeometry(QtCore.QRect(500, 710, 111, 51))
font = QtGui.QFont()
font.setPointSize(16)
font.setBold(True)
font.setWeight(75)
self.button3.setFont(font)
self.button3.setObjectName("button3")
SuperpixelVideo.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(SuperpixelVideo)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1120, 26))
self.menubar.setObjectName("menubar")
SuperpixelVideo.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(SuperpixelVideo)
self.statusbar.setObjectName("statusbar")
SuperpixelVideo.setStatusBar(self.statusbar)

self.retranslateUi(SuperpixelVideo)
QtCore.QMetaObject.connectSlotsByName(SuperpixelVideo)

def retranslateUi(self, SuperpixelVideo):
_translate = QtCore.QCoreApplication.translate
SuperpixelVideo.setWindowTitle(_translate("SuperpixelVideo", "MainWindow"))
self.n_segments.setText(_translate("SuperpixelVideo", "超像素个数"))
self.sigma.setText(_translate("SuperpixelVideo", "平滑度"))
self.cofirmButton.setText(_translate("SuperpixelVideo", "开始转换"))
self.videoPath.setText(_translate("SuperpixelVideo", "视频路径"))
self.button1.setText(_translate("SuperpixelVideo", "播放"))
self.button2.setText(_translate("SuperpixelVideo", "播放"))
self.label_2.setText(_translate("SuperpixelVideo", "0为不平滑"))
self.label_3.setText(_translate("SuperpixelVideo", "近似"))
self.label_4.setText(_translate("SuperpixelVideo", "单左斜杠'/'"))
self.button3.setText(_translate("SuperpixelVideo", "停止"))

main.py

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
190
191
192
193
194
195
196
import glob
import os
import subprocess
import sys
import time
from subprocess import call

import cv2
import numpy as np
from ffmpy3 import FFmpeg
from moviepy.editor import *
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtMultimedia import *
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtWidgets import *
from skimage import io
from skimage.segmentation import mark_boundaries, slic
from skimage.util import img_as_float
from Ui_test import *

def Read_video(path,file_name):
video_path = path+file_name
sourceFileName=file_name
times=0
#提取视频的频率,每1帧提取一个
frameFrequency=1
outPutDirName=path+"input_pictures/"
if not os.path.exists(outPutDirName):
#如果文件目录不存在则创建目录
os.makedirs(outPutDirName)
video = cv2.VideoCapture(video_path)
video_temp = VideoFileClip(video_path)
audio=video_temp.audio
audio.write_audiofile(path+'audio.mp3')
fps = video.get(cv2.CAP_PROP_FPS)
while True:
times+=1
res, image = video.read()
if times==1:
size=image.shape
if not res:
break
if times%frameFrequency==0:
cv2.imwrite(outPutDirName + str(times)+'.jpg', image)
print("reading "+outPutDirName + str(times)+'.jpg')
print('done')
video.release()
return int(fps),size[0],size[1]

def Super_pixel(path,numSegments,numSigma,outputDir,outputFilename):
image = img_as_float(io.imread(path))
b,g,r = cv2.split(image)
image = cv2.merge([r,g,b])
segments = slic(image, n_segments = numSegments, sigma = numSigma)
image_out=mark_boundaries(image, segments)
if not os.path.exists(outputDir):
#如果文件目录不存在则创建目录
os.makedirs(outputDir)
cv2.imwrite(outputDir+outputFilename, image_out*255)
print("writing "+outputDir+outputFilename)

def Picture_conversion(inputDir,outputDir,numSegments,numSigma):
for root, dirs, files in os.walk(inputDir):
for file in files:
imgPath=root+'/'+file
Super_pixel(imgPath,numSegments,numSigma,outputDir,file)
print("done")

def Create_video(videoDir,fps,height,width):
print("waiting...")
path=videoDir+"output_pictures/"
filelist = os.listdir(path)
size = (width,height)
os.chdir(videoDir) #跳转到目标目录
#生成avi视频
if os.path.exists('video.avi'):
os.remove('video.avi')
video = cv2.VideoWriter("video.avi", cv2.VideoWriter_fourcc('I', '4', '2', '0'), fps, size)
num=0
for item in filelist:#统计有多少帧
if item.endswith('.jpg'):
num+=1
window.ui.label.setText("生成超像素视频...")
for i in range(num):
frame = cv2.imread(path+str(i+1)+'.jpg')
video.write(frame)#添加到视频中
video.release()
cv2.destroyAllWindows()
inputVideo = videoDir+'video'
window.ui.label.setText("压缩视频...")
command = "ffmpeg -i %s.avi %s.mp4" % (inputVideo, inputVideo)
if os.path.exists('video.mp4'):
os.remove('video.mp4')
if os.path.exists('output.mp4'):
os.remove('output.mp4')
call(command.split())
window.ui.label.setText("加入音频...")
subprocess.call('ffmpeg -i video.mp4 -i audio.mp3 -strict -2 -f mp4 output.mp4', shell=True)
os.remove("video.avi")
os.remove("audio.mp3")
os.remove("video.mp4")
window.ui.label.setText("转换成功!")

def confirm():
window.ui.button2.setEnabled(False)
videoPath=window.ui.lineEdit01.text()
if videoPath=='':
videoPath='E:/test/input.mp4'
index=videoPath.rfind('/')
videoDir=videoPath[:index+1]
videoName=videoPath[index+1:]
strSegments=window.ui.lineEdit02.text()
if strSegments=='':
strSegments='100'
numSegments=int(strSegments)
strSigma=window.ui.lineEdit03.text()
if strSigma=='':
strSigma='5'
numSigma=int(strSigma)
inputDir=videoDir+"input_pictures/"
outputDir=videoDir+"output_pictures/"
window.ui.label.setText("读取视频...")
window.ui.button1.setEnabled(False)
otherInfo=Read_video(videoDir,videoName)
window.ui.button1.setEnabled(True)
#otherInfo=(25, 1246, 720)
print('fps,height,width=')
print(otherInfo)
fps=otherInfo[0]
height=otherInfo[1]
width=otherInfo[2]
window.ui.label.setText("转换成超像素...")
#Picture_conversion(inputDir,outputDir,numSegments,numSigma)
Create_video(videoDir,fps,height,width)
window.ui.button2.setEnabled(True)

class mainWindow(QMainWindow):
def __init__(self):
super(QMainWindow, self).__init__()
self.ui = Ui_SuperpixelVideo()
self.ui.setupUi(self)

def play1():
path=window.ui.lineEdit01.text()
if path=='':
path='E:/test/input.mp4'
if not os.path.exists(path):
return
window.ui.videoplayer1.addWidget(videoWidget)
player.setMedia(QMediaContent(QUrl.fromLocalFile(path)))
player.play()

def play2():
path=window.ui.lineEdit01.text()
index=path.rfind('/')
videoDir=path[:index+1]
if not os.path.exists(videoDir+'output.mp4'):
return
window.ui.videoplayer2.addWidget(videoWidget)
player.setMedia(QMediaContent(QUrl.fromLocalFile(videoDir+"output.mp4")))
player.play()

def stop():
player.stop()

if __name__ == "__main__":
app = QApplication(sys.argv)
window = mainWindow()
window.show()

player = QMediaPlayer()
player.setVolume(100)
playlist = QMediaPlaylist()
playlist.setPlaybackMode(QMediaPlaylist.CurrentItemOnce)#只播放一次
player.setPlaylist(playlist)
videoWidget = QVideoWidget()
palette = QPalette()
palette.setBrush(QPalette.Background, Qt.black)
player.setVideoOutput(videoWidget)
videoWidget.show()
window.ui.videoplayer1.addWidget(videoWidget)

window.ui.lineEdit01.setText("E:/test/input.mp4")
window.ui.lineEdit02.setText("100")
window.ui.lineEdit03.setText("5")

window.ui.button1.setEnabled(True)
window.ui.button2.setEnabled(False)

window.ui.button1.clicked.connect(lambda:play1())
window.ui.button2.clicked.connect(lambda:play2())
window.ui.button3.clicked.connect(lambda:stop())
window.ui.cofirmButton.clicked.connect(lambda:confirm())
sys.exit(app.exec_())

SLIC超像素视频转换
https://xinhaojin.github.io/2020/04/30/超像素视频转换/
作者
xinhaojin
发布于
2020年4月30日
许可协议