├── README.md
├── a.ui
├── dataset
├── cardboard.zip
├── glass.zip
├── meat.zip
├── paper.zip
├── plastic.zip
├── test.cpp
└── trash.zip
├── function.png
├── garbage.py
├── kid_ui.ui
├── test-1.py
├── test-4.py
├── 界面.jpg
└── 项目代码
├── classify.py
├── garbage_ui.py
├── kid_ui.ui
├── main_ui.py
└── ui.py
/README.md:
--------------------------------------------------------------------------------
1 | garbage-classification
2 | ======
3 | 一个智能垃圾分类系统
4 | ------
5 | #### 一、主要功能
6 | 对上传的垃圾图片进行识别,并返回分类结果(干垃圾、湿垃圾、有害垃圾、可回收垃圾)
7 | #### 二、实现原理
8 | 
9 | 该垃圾分类项目采用深度学习--图像处理模块,采用的是Sequential 序贯模型,序贯模型是函数式模型的简略版,为最简单的线性、从头到尾的结构顺序,不分叉。Sequential模型的基本组件包括① model.add,添加层;② model.compile,模型训练的BP模式设置;③ model.fit,模型训练参数设置 + 训练;④ 模型预测。
10 |
11 | #### 三、运行环境
12 | win10+cuda9.1+cudnn7+tensorflow-gpu-1.12.0、pytorch1.4.0+keras-2.2.4
13 |
14 | #### 四、关于库文件:
15 | 在运行项目的过程中,我们遇到了很多报错,很大一部分是缺少各种库文件,在实现这个项目前,需要配置好环境,并且文件的版本需要对应,否则也会出现各种各样的报错问题(比如tensorflow、pytorch、keras、cudnn之间的版本对应),需要安装tensorflow、pytorch、keras、numpy、torchvision、scipy等文件包
16 |
17 | #### 五、数据集:
18 | 我们在查找过程中发现网上大部分用的都是kaggle的垃圾数据集,一共六类,包括纸板(cardboard)、玻璃(glass)、金属(metal)、纸(paper)、塑料(plastic)、其他垃圾(trash) [数据集下载地址](https://www.kaggle.com/asdasdasasdas/garbage-classification)
19 |
20 | #### 六、模型训练基本步骤:
21 | ①对数据集进行训练,生成模型,记录最终准确率(训练模型因为机子配置原因耗时较长)
22 | ②运用生成好的模型对图像进行测试,查看图像识别结果及垃圾分类情况,记录结果及验证情况
23 | ③调整参数,对模型进行优化
24 |
25 | #### 七、代码运行:
26 | 运行 garbage_ui.py 文件在前端界面进行图片上传、识别即可。
27 |
28 |
--------------------------------------------------------------------------------
/a.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 904
10 | 745
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 255
20 | 255
21 | 255
22 |
23 |
24 |
25 |
26 |
27 |
28 | 219
29 | 240
30 | 194
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | 255
40 | 255
41 | 255
42 |
43 |
44 |
45 |
46 |
47 |
48 | 219
49 | 240
50 | 194
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | 219
60 | 240
61 | 194
62 |
63 |
64 |
65 |
66 |
67 |
68 | 219
69 | 240
70 | 194
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | Arial Narrow
80 | 10
81 |
82 |
83 |
84 | Dialog
85 |
86 |
87 | Qt::RightToLeft
88 |
89 |
90 | true
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | 150
99 | 30
100 | 601
101 | 111
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | 0
111 | 0
112 | 0
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | 0
122 | 0
123 | 0
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | 120
133 | 120
134 | 120
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | 15
144 | 75
145 | true
146 |
147 |
148 |
149 | UpArrowCursor
150 |
151 |
152 | <html><head/><body><p><span style=" font-size:28pt; color:#428663;">快来看看是什么垃圾~~~</span></p></body></html>
153 |
154 |
155 |
156 |
157 |
158 | 110
159 | 600
160 | 141
161 | 81
162 |
163 |
164 |
165 | <html><head/><body><p><span style=" font-size:16pt;">识别结果:</span></p></body></html>
166 |
167 |
168 |
169 |
170 |
171 | 120
172 | 200
173 | 551
174 | 401
175 |
176 |
177 |
178 |
179 |
180 |
181 | 250
182 | 620
183 | 421
184 | 41
185 |
186 |
187 |
188 |
189 |
190 |
191 | 220
192 | 140
193 | 441
194 | 41
195 |
196 |
197 |
198 |
199 |
200 |
201 | 160
202 | 220
203 | 72
204 | 15
205 |
206 |
207 |
208 | 待测垃圾
209 |
210 |
211 |
212 |
213 |
214 | 720
215 | 440
216 | 111
217 | 51
218 |
219 |
220 |
221 |
222 | 111
223 | 51
224 |
225 |
226 |
227 |
228 | Agency FB
229 | 11
230 | 50
231 | false
232 |
233 |
234 |
235 | Qt::RightToLeft
236 |
237 |
238 | Button { background-color: rgb(255, 255, 255); border-radius: 3px; color: rgb(255, 255, 255); } QPushButton:hover { background-color: rgb(85, 170, 127); }
239 |
240 |
241 | 开始识别
242 |
243 |
244 |
245 | 30
246 | 30
247 |
248 |
249 |
250 |
251 |
252 |
253 | 720
254 | 510
255 | 111
256 | 51
257 |
258 |
259 |
260 |
261 | Agency FB
262 | 11
263 | 50
264 | false
265 |
266 |
267 |
268 | Qt::RightToLeft
269 |
270 |
271 | Button { background-color: rgb(255, 255, 255); border-radius: 3px; color: rgb(255, 255, 255); } QPushButton:hover { background-color: rgb(85, 170, 127); }
272 |
273 |
274 | 上传图片
275 |
276 |
277 |
278 |
279 |
280 | 120
281 | 140
282 | 111
283 | 41
284 |
285 |
286 |
287 | <html><head/><body><p><span style=" font-size:12pt;">图片路径:</span></p></body></html>
288 |
289 |
290 |
291 |
292 |
293 | 570
294 | 680
295 | 121
296 | 16
297 |
298 |
299 |
300 |
301 | Bahnschrift
302 | 16
303 |
304 |
305 |
306 | <html><head/><body><p><span style=" font-size:9pt; color:#306148;">©2020 ev dj ch</span></p></body></html>
307 |
308 |
309 |
310 |
311 |
312 | 720
313 | 20
314 | 141
315 | 191
316 |
317 |
318 |
319 | <html><head/><body><p><img src=":/g/垃圾桶.jpg"/></p></body></html>
320 |
321 |
322 |
323 |
324 |
325 | 720
326 | 370
327 | 111
328 | 51
329 |
330 |
331 |
332 |
333 | 111
334 | 51
335 |
336 |
337 |
338 |
339 | Agency FB
340 | 11
341 | 50
342 | false
343 |
344 |
345 |
346 | Qt::RightToLeft
347 |
348 |
349 | Button { background-color: rgb(255, 255, 255); border-radius: 3px; color: rgb(255, 255, 255); } QPushButton:hover { background-color: rgb(85, 170, 127); }
350 |
351 |
352 | 垃圾科普
353 |
354 |
355 |
356 | 30
357 | 30
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
--------------------------------------------------------------------------------
/dataset/cardboard.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skyevvv/garbage-classification/8861c45568333b54be82de91457fa9470ab17ba3/dataset/cardboard.zip
--------------------------------------------------------------------------------
/dataset/glass.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skyevvv/garbage-classification/8861c45568333b54be82de91457fa9470ab17ba3/dataset/glass.zip
--------------------------------------------------------------------------------
/dataset/meat.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skyevvv/garbage-classification/8861c45568333b54be82de91457fa9470ab17ba3/dataset/meat.zip
--------------------------------------------------------------------------------
/dataset/paper.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skyevvv/garbage-classification/8861c45568333b54be82de91457fa9470ab17ba3/dataset/paper.zip
--------------------------------------------------------------------------------
/dataset/plastic.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skyevvv/garbage-classification/8861c45568333b54be82de91457fa9470ab17ba3/dataset/plastic.zip
--------------------------------------------------------------------------------
/dataset/test.cpp:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dataset/trash.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skyevvv/garbage-classification/8861c45568333b54be82de91457fa9470ab17ba3/dataset/trash.zip
--------------------------------------------------------------------------------
/function.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skyevvv/garbage-classification/8861c45568333b54be82de91457fa9470ab17ba3/function.png
--------------------------------------------------------------------------------
/garbage.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array, array_to_img
4 | from keras.layers import Conv2D, Flatten, MaxPooling2D, Dense
5 | from keras.models import Sequential
6 |
7 | import glob, os, random
8 | base_path = 'D:/AI/cnn-garbage/dataset-resized'
9 | #glob.glob获取指定目录下的所有图片
10 | img_list = glob.glob(os.path.join(base_path, '*/*.jpg'))
11 | print(len(img_list))#数据集一共2527个数据
12 | #随机展示六张图片
13 | for i, img_path in enumerate(random.sample(img_list, 6)):
14 | img = load_img(img_path)
15 | img = img_to_array(img, dtype=np.uint8)
16 |
17 | # plt.subplot(2, 3, i + 1)
18 |
19 | # plt.imshow(img.squeeze())
20 |
21 | #对数据进行分组
22 | train_datagen = ImageDataGenerator(
23 | rescale=1. / 225, shear_range=0.1, zoom_range=0.1,
24 | width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True,
25 | vertical_flip=True, validation_split=0.1)
26 |
27 | test_datagen = ImageDataGenerator(
28 | rescale=1. / 255, validation_split=0.1)
29 |
30 | train_generator = train_datagen.flow_from_directory(
31 | base_path, target_size=(300, 300), batch_size=16,
32 | class_mode='categorical', subset='training', seed=0)
33 |
34 | validation_generator = test_datagen.flow_from_directory(
35 | base_path, target_size=(300, 300), batch_size=16,
36 | class_mode='categorical', subset='validation', seed=0)
37 |
38 | labels = (train_generator.class_indices)
39 | labels = dict((v, k) for k, v in labels.items())
40 |
41 | print(labels)
42 | # 0: 'cardboard', 1: 'glass', 2: 'metal', 3: 'paper', 4: 'plastic', 5: 'trash'
43 |
44 | #模型的建立和训练
45 | #MaxPooling2D,epoch=50
46 | model = Sequential([
47 | Conv2D(filters=32, kernel_size=3, padding='same', activation='relu', input_shape=(300, 300, 3)),
48 | MaxPooling2D(pool_size=2),
49 |
50 | Conv2D(filters=
51 | , kernel_size=3, padding='same', activation='relu'),
52 | MaxPooling2D(pool_size=2),
53 |
54 | Conv2D(filters=32, kernel_size=3, padding='same', activation='relu'),
55 | MaxPooling2D(pool_size=2),
56 |
57 | Conv2D(filters=32, kernel_size=3, padding='same', activation='relu'),
58 | MaxPooling2D(pool_size=2),
59 |
60 | Flatten(),
61 | #Flatten层用来将输入“压平”,即把多维的输入一维化,
62 | # 常用在从卷积层到全连接层的过渡。Flatten不影响batch的大小。
63 | Dense(64, activation='relu'),
64 | #units=64是正整数,输出空间维度。
65 | #Dense 实现以下操作:output = activation(dot(input, kernel) + bias)
66 | # 其中 activation 是按逐个元素计算的激活函数,kernel 是由网络层创建的权值矩阵,
67 | # 以及 bias 是其创建的偏置向量 (只在 use_bias 为 True 时才有用)。
68 | #如果该层的输入的秩大于2,那么它首先被展平然后再计算与 kernel 的点乘。
69 | Dense(6, activation='softmax')
70 | #units=6,,是正整数,输出空间维度。
71 | ])
72 |
73 | model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])
74 | #categorical_crossentropy输出张量与目标张量之间的分类交叉熵-∑p(x)logq(x)。
75 | #p代表正确答案,q代表的是预测值。交叉熵值越小,两个概率分布越接近。
76 | history_fit = model.fit_generator(train_generator,
77 | epochs=100, #迭代总轮数
78 | steps_per_epoch=2276//32,#generator 产生的总步数(批次样本)
79 | validation_data=validation_generator,# 验证数据的生成器
80 | validation_steps=251//32)
81 |
82 | with open(base_path + "/history_fit.json", "w") as json_file:
83 | json_file.write(str(history_fit))
84 | acc = history_fit.history['acc']
85 | val_acc = history_fit.history['val_acc']
86 | loss = history_fit.history['loss']
87 |
88 | epochs = range(1, len(acc) + 1)
89 | plt.figure("acc")
90 | plt.plot(epochs, acc, 'r-', label='Training acc')
91 | plt.plot(epochs, val_acc, 'b', label='validation acc')
92 | plt.title('The comparision of train_acc and val_acc')
93 | plt.legend()
94 | plt.show()
95 |
96 | plt.figure("loss")
97 | plt.plot(epochs, loss, 'r-', label='loss')
98 | plt.title('The comparision of loss')
99 | plt.legend()
100 | plt.show()
101 |
102 | #结果展示
103 | #下面我们随机抽取validation中的16张图片,展示图片以及其标签,并且给予我们的预测。 我们发现预测的准确度还是蛮高的,对于大部分图片,都能识别出其类别。
104 | test_x, test_y = validation_generator.__getitem__(1)
105 |
106 | preds = model.predict(test_x)
107 |
108 | plt.figure(figsize=(16, 16))
109 | for i in range(16):
110 | plt.subplot(4, 4, i+1)
111 | plt.title('pred:%s / truth:%s' % (labels[np.argmax(preds[i])], labels[np.argmax(test_y[i])]))
112 | plt.imshow(test_x[i])
113 | plt.show()
114 | print(labels[np.argmax(preds[i])])
115 |
--------------------------------------------------------------------------------
/kid_ui.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 921
10 | 800
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 255
20 | 255
21 | 255
22 |
23 |
24 |
25 |
26 |
27 |
28 | 219
29 | 240
30 | 194
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | 255
40 | 255
41 | 255
42 |
43 |
44 |
45 |
46 |
47 |
48 | 219
49 | 240
50 | 194
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | 219
60 | 240
61 | 194
62 |
63 |
64 |
65 |
66 |
67 |
68 | 219
69 | 240
70 | 194
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | Dialog
79 |
80 |
81 |
82 |
83 | 280
84 | 30
85 | 361
86 | 111
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | 0
96 | 0
97 | 0
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | 0
107 | 0
108 | 0
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | 120
118 | 120
119 | 120
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | Arial
129 | 28
130 | 75
131 | true
132 |
133 |
134 |
135 | UpArrowCursor
136 |
137 |
138 | <html><head/><body><p><span style=" font-size:36pt; color:#428663;">垃圾小百科</span></p></body></html>
139 |
140 |
141 |
142 |
143 |
144 | 240
145 | 110
146 | 661
147 | 171
148 |
149 |
150 |
151 | <html><head/><body><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">有害垃圾是指:对人体健康或者自然环境造成直接或潜在危害的废弃物 </span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">有害垃圾主要包括:</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">废电池(充电电池、铅酸电池、镍镉电池、纽扣电池等)、废油漆、消毒剂、荧光灯管、含贡温度计、废药品及其包装物等</span></p></body></html>
152 |
153 |
154 |
155 |
156 |
157 | 230
158 | 260
159 | 631
160 | 181
161 |
162 |
163 |
164 | <html><head/><body><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">可回收物是指:适宜回收利用和资源化利用的生活废弃物</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">主要包括:</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">报纸、纸箱、书本、广告单、塑料瓶、塑料玩具、油桶、酒瓶、</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">玻璃杯、易拉罐、旧铁锅、旧衣服、包、旧玩偶、旧数码产品</span></p></body></html>
165 |
166 |
167 |
168 |
169 |
170 | 230
171 | 430
172 | 641
173 | 181
174 |
175 |
176 |
177 | <html><head/><body><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">厨余垃圾是指:居民日常生活及食品加工、饮食服务、单位供餐等活动中产生的垃圾。 </span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">厨余垃圾主要包括:</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">丢弃不用的菜叶、剩菜、剩饭、果皮、蛋壳、茶渣、骨头等。</span></p></body></html>
178 |
179 |
180 |
181 |
182 |
183 | 240
184 | 600
185 | 651
186 | 181
187 |
188 |
189 |
190 | <html><head/><body><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">干垃圾是指:除可回收物、有害垃圾、湿垃圾以外的其它生活废弃物 </span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">干垃圾主要包括:</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">餐盒、餐巾纸、湿纸巾、卫生间用纸、塑料袋、食品包装袋、污染</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">严重的纸、烟蒂、纸尿裤、一次性杯子、大骨头、贝壳、花盆等</span></p></body></html>
191 |
192 |
193 |
194 |
195 |
196 | 50
197 | 100
198 | 161
199 | 161
200 |
201 |
202 |
203 |
204 |
205 |
206 | 80
207 | 170
208 | 72
209 | 15
210 |
211 |
212 |
213 | <html><head/><body><p><img src=":/g/有害.jpg"/></p></body></html>
214 |
215 |
216 |
217 |
218 |
219 | 50
220 | 270
221 | 161
222 | 161
223 |
224 |
225 |
226 |
227 |
228 |
229 | 50
230 | 440
231 | 161
232 | 161
233 |
234 |
235 |
236 |
237 |
238 |
239 | 50
240 | 610
241 | 161
242 | 161
243 |
244 |
245 |
246 |
247 |
248 |
249 | 680
250 | 10
251 | 141
252 | 131
253 |
254 |
255 |
256 |
257 |
258 |
259 | 80
260 | 330
261 | 72
262 | 15
263 |
264 |
265 |
266 | <html><head/><body><p><img src=":/g/可回收.jpg"/></p></body></html>
267 |
268 |
269 |
270 |
271 |
272 | 90
273 | 510
274 | 72
275 | 15
276 |
277 |
278 |
279 | <html><head/><body><p><img src=":/g/厨余垃圾.jpg"/></p></body></html>
280 |
281 |
282 |
283 |
284 |
285 | 80
286 | 640
287 | 72
288 | 15
289 |
290 |
291 |
292 | <html><head/><body><p><img src=":/g/干垃圾.jpg"/></p></body></html>
293 |
294 |
295 |
296 |
297 |
298 | 710
299 | 40
300 | 72
301 | 15
302 |
303 |
304 |
305 | <html><head/><body><p><img src=":/g/你是垃圾吗.jpg"/></p></body></html>
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
--------------------------------------------------------------------------------
/test-1.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array, array_to_img
4 | from keras.layers import Conv2D, Flatten, MaxPooling2D, Dense
5 | from keras.models import Sequential
6 | from keras.callbacks import EarlyStopping
7 | from keras.applications.imagenet_utils import preprocess_input
8 | import glob, os, random
9 | from keras.models import model_from_json
10 | from tensorflow.keras.preprocessing import image
11 | from keras.optimizers import SGD
12 | root_path = 'D:/AI/cnn-garbage'
13 | labels = {0: 'cardboard', 1: 'glass', 2: 'metal', 3: 'paper', 4: 'plastic', 5: 'trash'}
14 |
15 | class Model:
16 | def __init__(self):
17 | self.model = None
18 |
19 | def build_model(self):
20 | self.model = Sequential()
21 |
22 | self.model.add(Conv2D(filters=32, kernel_size=3, padding='same', activation='relu', input_shape=(300, 300, 3)))
23 | self.model.add(MaxPooling2D(pool_size=2))
24 |
25 | self.model.add(Conv2D(filters=64, kernel_size=3, padding='same', activation='relu'))
26 | self.model.add(MaxPooling2D(pool_size=2))
27 |
28 |
29 | self.model.add(Conv2D(filters=32, kernel_size=3, padding='same', activation='relu'))
30 | self.model.add(MaxPooling2D(pool_size=2))
31 |
32 | self.model.add(Conv2D(filters=32, kernel_size=3, padding='same', activation='relu'))
33 | self.model.add(MaxPooling2D(pool_size=2))
34 |
35 | self.model.add(Flatten()) # 扁平化参数
36 | self.model.add(Dense(64, activation='relu'))
37 | self.model.add(Dense(6, activation='softmax'))
38 | self.model.summary()
39 |
40 | def train_model(self):
41 | self.model.compile(loss='categorical_crossentropy',
42 | optimizer='adam',#sgd 'adam'
43 | metrics=['accuracy'])
44 | # 自动扩充训练样本
45 |
46 | train_datagen = ImageDataGenerator(
47 | rescale=1. / 255, # 数据缩放,把像素点的值除以255,使之在0到1之间
48 | shear_range=0.1, # 错切变换角度
49 | zoom_range=0.1, # 随机缩放范围
50 | width_shift_range = 0.1,
51 | height_shift_range = 0.1,
52 | horizontal_flip=True,
53 | vertical_flip = True,
54 | validation_split = 0.1
55 | )
56 | # 归一化验证集
57 |
58 | val_datagen = ImageDataGenerator(
59 | rescale=1. / 255,validation_split=0.1)
60 |
61 | train_generator = train_datagen.flow_from_directory(
62 | root_path+'/dataset-resized',
63 | target_size=(300, 300),
64 | batch_size=16,
65 | class_mode='categorical',
66 | subset='training',
67 | seed=0)
68 | val_generator = val_datagen.flow_from_directory(
69 | root_path+'/dataset-resized',
70 | target_size=(300, 300),
71 | batch_size=16,
72 | class_mode='categorical',
73 | subset='validation',
74 | seed=0)
75 | # global labels
76 | # labels = (train_generator.class_indices)
77 | # labels = dict((v, k) for k, v in labels.items())
78 | try:
79 | history_fit = self.model.fit_generator(train_generator,
80 | epochs=100, # 迭代总轮数
81 | steps_per_epoch=2276//32, # generator 产生的总步数(批次样本)
82 | validation_data=val_generator, # 验证数据的生成器
83 | validation_steps=251//32)
84 | with open(root_path + "/history_fit.json", "w") as json_file:
85 | json_file.write(str(history_fit))
86 |
87 | acc = history_fit.history['acc']
88 | val_acc = history_fit.history['val_acc']
89 | loss = history_fit.history['loss']
90 | val_loss = history_fit.history['val_loss']
91 |
92 | epochs = range(1, len(acc) + 1)
93 | plt.figure("acc")
94 | plt.plot(epochs, acc, 'r-', label='Training acc')
95 | plt.plot(epochs, val_acc, 'b', label='validation acc')
96 | plt.title('The comparision of train_acc and val_acc')
97 | plt.legend()
98 | plt.show()
99 |
100 | plt.figure("loss")
101 | plt.plot(epochs, loss, 'r-', label='Training loss')
102 | plt.plot(epochs, val_loss, 'b', label='validation loss')
103 | plt.title('The comparision of train_loss and val_loss')
104 | plt.legend()
105 | plt.show()
106 | except StopIteration:
107 | pass
108 |
109 | def save_model(self):
110 | model_json=self.model.to_json()
111 | with open(root_path+'/model_json.json', "w") as json_file:
112 | json_file.write(model_json)
113 | self.model.save_weights(root_path+'/model_weight.h5')
114 | self.model.save(root_path+'/model.h5')
115 | print('model saved')
116 |
117 | def load_model(self):
118 | json_file = open(root_path + '/model_json.json') # 加载模型结构文件
119 | loaded_model_json = json_file.read()
120 | json_file.close()
121 | model = model_from_json(loaded_model_json) # 结构文件转化为模型
122 | # 加载权重
123 | model.load_weights(root_path + '/model_weight.h5') # h5文件保存模型的权重数据
124 | return model
125 |
126 | def generate_result(result):
127 | for i in range(6):
128 | if(result[0][i] == 1):
129 | return labels[i]
130 |
131 | if __name__=='__main__':
132 | model=Model()
133 | #model.build_model()
134 | print('model built')
135 | #model.train_model()
136 | print('model trained')
137 | # model.save_model()
138 | print('model saved')
139 | #已经有模型,直接加载,注释掉上面三个函数
140 | model1 = model.load_model()
141 | print('model loaded')
142 | img_path = "D:/AI/cnn-garbage/dataset-resized/cardboard/cardboard2.jpg"
143 | img = image.load_img(img_path, target_size=(300, 300))
144 | img = image.img_to_array(img)
145 | img = np.expand_dims(img, axis=0)
146 | result = model1.predict(img)
147 | print(generate_result(result))
148 |
--------------------------------------------------------------------------------
/test-4.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array, array_to_img
4 | from keras.layers import Conv2D, Flatten, MaxPooling2D, Dense, Dropout
5 | from keras.models import Sequential
6 | from keras.callbacks import EarlyStopping
7 | from keras.applications.imagenet_utils import preprocess_input
8 | import glob, os, random
9 | from keras.models import model_from_json
10 | from keras.preprocessing import image
11 | from keras.optimizers import SGD
12 | # labels = []
13 | root_path = '.'
14 | labels = {0: 'cardboard', 1: 'glass', 2: 'metal', 3: 'paper', 4: 'plastic', 5: 'trash'}
15 |
16 |
17 | class Model:
18 | def __init__(self):
19 | self.model = None
20 |
21 | def build_model(self):
22 | self.model = Sequential()
23 | #Sequential模型,顾名思义,就是多个网络层的线性堆叠,建立模型有两种方式:一是向layer添加list的方式,二是通过.add()方式一层层添加(一个add为一层)
24 |
25 | #二维卷积层,即对图像的空域卷积。该层对二维输入进行滑动窗卷积,当使用该层作为第一层时,应提供input_shape参数。例如input_shape = (128,128,3)代表128*128的彩色RGB图像
26 | #filters:卷积核的数目(即输出的维度)
27 | #kernel_size:单个整数或由两个整数构成的list / tuple,卷积核的宽度和长度。如为单个整数,则表示在各个空间维度的相同长度
28 | #padding:补0策略,为“valid”, “same” 。“valid”代表只进行有效的卷积,即对边界数据不处理。“same”代表保留边界处的卷积结果,通常会导致输出shape与输入shape相同,因为卷积核移动时在边缘会出现大小不够的情况
29 | #activation:激活函数,为预定义的激活函数名(参考激活函数),或逐元素(element-wise)的Theano函数
30 |
31 | self.model.add(Conv2D(filters=32, kernel_size=3, padding='same', activation='relu', input_shape=(300, 300, 3)))
32 |
33 | #空间池化(也叫亚采样或下采样)降低了每个特征映射的维度,但是保留了最重要的信息。空间池化可以有很多种形式:最大(Max),平均(Average),求和(Sum)等等。
34 | # 这里使用最大池化,我们定义了空间上的邻域(2x2的窗)并且从纠正特征映射中取出窗里最大的元素。池化可以是输入表征更小更易操作,减少网络中的参数与计算数量,防止过拟合,帮助我们获得不因尺寸二改变的等效图片表征
35 |
36 | self.model.add(MaxPooling2D(pool_size=2))
37 |
38 | self.model.add(Conv2D(filters=64, kernel_size=3, padding='same', activation='relu'))
39 | self.model.add(MaxPooling2D(pool_size=2))
40 |
41 | self.model.add(Conv2D(filters=32, kernel_size=3, padding='same', activation='relu'))
42 | self.model.add(MaxPooling2D(pool_size=2))
43 |
44 | self.model.add(Conv2D(filters=32, kernel_size=3, padding='same', activation='relu'))
45 | self.model.add(MaxPooling2D(pool_size=2))
46 |
47 | #Flatten层用来将输入“压平”,即把多维的输入一维化,常用在从卷积层到全连接层的过渡。Flatten不影响batch的大小
48 | self.model.add(Flatten()) # 扁平化参数
49 |
50 | #Dense层就是所谓的全连接神经网络层
51 | #units:该层有几个神经元 activation:该层使用的激活函数
52 | #定义了一个有64个节点,使用relu激活函数的神经层
53 | #ReLU是线性修正,是purelin的折线版。它的作用是如果计算出的值小于0,就让它等于0,否则保持原来的值不变。
54 | # 这是一种简单粗暴地强制某些数据为0的方法,使得网络可以自行引入稀疏性,同时大大地提高了训练速度
55 | self.model.add(Dense(64, activation='relu'))
56 | #Softmax 在机器学习和深度学习中有着非常广泛的应用,尤其在处理多分类。
57 | # 线性分类器模型最后输出层包含了6个输出值,经过Softmax处理后,数值转化为不同类别之间的相对概率,相对概率越高,预测为哪一类的可能性越大
58 | self.model.add(Dense(6, activation='softmax'))
59 | #输出模型各层的参数状况
60 | self.model.summary()
61 |
62 | def train_model(self):
63 | # 优化器, 主要有Adam、sgd、rmsprop等方式。
64 | # sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
65 | #在训练模型之前,我们需要通过compile来对学习过程进行配置。compile接收三个参数:
66 | #优化器optimizer:已预定义的优化器名
67 | #损失函数loss:最小化的目标函数,它可为预定义的损失函数
68 | #指标列表metrics:对分类问题,我们一般将该列表设置为metrics=['accuracy']。
69 | # 指标可以是一个预定义指标的名字,也可以是一个用户定制的函数。指标函数应该返回单个张量,或一个完成metric_name - > metric_value映射的字典。
70 |
71 | #categorical_crossentropy(交叉熵损失函数) 交叉熵是用来评估当前训练得到的概率分布与真实分布的差异情况
72 | #adam一种可以替代传统随机梯度下降(SGD)过程的一阶优化算法,它能基于训练数据迭代地更新神经网络权重
73 | self.model.compile(loss='categorical_crossentropy',
74 | optimizer='adam', # sgd 'adam'
75 | metrics=['accuracy'])
76 | # 自动扩充训练样本
77 | #ImageDataGenerator位于keras.preprocessing.image模块当中,可用于做数据增强,或者仅仅用于一个批次一个批次的读进图片数据
78 | train_datagen = ImageDataGenerator(
79 | rescale=1. / 255, # 数据缩放,把像素点的值除以255,使之在0到1之间
80 | shear_range=0.1, # 错切变换角度
81 | zoom_range=0.1, # 随机缩放范围
82 | width_shift_range=0.1,#浮点数,图片宽度的某个比例,数据提升时图片随机水平偏移的幅度
83 | height_shift_range=0.1,#浮点数,图片高度的某个比例,数据提升时图片随机竖直偏移的幅度。
84 | horizontal_flip=True,#布尔值,进行随机水平翻转。随机的对图片进行水平翻转,这个参数适用于水平翻转不影响图片语义的时候
85 | vertical_flip=True,#布尔值,进行随机竖直翻转。
86 | validation_split=0.1#多少数据用于验证集
87 | )
88 | # 生成验证集
89 | val_datagen = ImageDataGenerator(
90 | rescale=1. / 255, validation_split=0.1)
91 |
92 | # 读训练集图片
93 | train_generator = train_datagen.flow_from_directory(
94 | root_path + '/dataset-resized',#子文件路径
95 | # 整数元组 (height, width),默认:(300, 300)。 所有的图像将被调整到的尺寸。
96 | target_size=(300, 300),#输出的图片的尺寸
97 | # 一批数据的大小
98 | batch_size=16,
99 | # "categorical", "binary", "sparse", "input" 或 None 之一。
100 | # 默认:"categorical",返回one-hot 编码标签。
101 | class_mode='categorical',
102 | subset='training',
103 | seed=0)
104 |
105 | #读验证集图片
106 | val_generator = val_datagen.flow_from_directory(
107 | root_path + '/dataset-resized',
108 | target_size=(300, 300),
109 | batch_size=16,
110 | class_mode='categorical',
111 | subset='validation',
112 | seed=0)
113 |
114 | # 编译模型
115 | try:
116 | history_fit = self.model.fit_generator(train_generator,
117 | epochs=100, # 迭代总轮数
118 | steps_per_epoch=2276 // 32, # generator 产生的总步数/(批次样本)
119 | validation_data=val_generator, #验证数据的生成器
120 | validation_steps=251 // 32)# 当validation_data为生成器时,本参数指定验证集的生成器返回次数
121 |
122 | with open(root_path + "/model/history_fit.json", "w") as json_file:
123 | json_file.write(str(history_fit))
124 |
125 | acc = history_fit.history['acc']
126 | val_acc = history_fit.history['val_acc']
127 | loss = history_fit.history['loss']
128 | val_loss = history_fit.history['val_loss']
129 |
130 | epochs = range(1, len(acc) + 1)
131 | plt.figure("acc")
132 | plt.plot(epochs, acc, 'r-', label='Training acc')
133 | plt.plot(epochs, val_acc, 'b', label='validation acc')
134 | plt.title('The comparision of train_acc and val_acc')
135 | plt.legend()
136 | plt.show()
137 |
138 | plt.figure("loss")
139 | plt.plot(epochs, loss, 'r-', label='Training loss')
140 | plt.plot(epochs, val_loss, 'b', label='validation loss')
141 | plt.title('The comparision of train_loss and val_loss')
142 | plt.legend()
143 | plt.show()
144 | except StopIteration:
145 | pass
146 |
147 | def save_model(self):
148 | model_json = self.model.to_json()
149 | with open(root_path + '/model/model_json.json', "w") as json_file:
150 | json_file.write(model_json)
151 | self.model.save_weights(root_path + '/model/model_weight.h5')
152 | self.model.save(root_path + '/model/model.h5')
153 | print('model saved')
154 |
155 | def load_model(self):
156 | json_file = open(root_path + '/model/model_json.json') # 加载模型结构文件
157 | loaded_model_json = json_file.read()
158 | json_file.close()
159 | model = model_from_json(loaded_model_json) # 结构文件转化为模型
160 | # 加载权重
161 | model.load_weights(root_path + '/model/model_weight.h5') # h5文件保存模型的权重数据
162 | return model
163 |
164 |
165 | def generate_result(result):
166 | for i in range(6):
167 | if (result[0][i] == 1):
168 | return labels[i]
169 |
170 |
171 | if __name__ == '__main__':
172 | model = Model()
173 | model.build_model()
174 | print('model built')
175 | model.train_model()
176 | print('model trained')
177 | model.save_model()
178 | print('model saved')
179 | # 已经有模型,直接加载,注释掉上面三个函数
180 | model1 = model.load_model()
181 | print('model loaded')
182 | img_path = "./dataset-resized/glass/glass2.jpg"
183 | # 把图片转换成为numpy数组
184 | img = image.load_img(img_path, target_size=(300, 300))
185 | img = image.img_to_array(img)
186 | img = np.expand_dims(img, axis=0)
187 | result = model1.predict(img)
188 | print(generate_result(result))
--------------------------------------------------------------------------------
/界面.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skyevvv/garbage-classification/8861c45568333b54be82de91457fa9470ab17ba3/界面.jpg
--------------------------------------------------------------------------------
/项目代码/classify.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array, array_to_img
4 | from keras.layers import Conv2D, Flatten, MaxPooling2D, Dense, Dropout
5 | from keras.models import Sequential
6 | from keras.callbacks import EarlyStopping
7 | from keras.applications.imagenet_utils import preprocess_input
8 | import glob, os, random
9 | from keras.models import model_from_json
10 | from keras.preprocessing import image
11 | from keras.optimizers import SGD
12 | # labels = []
13 | root_path = '.'
14 | labels = {0: 'cardboard', 1: 'glass', 2: 'metal', 3: 'paper', 4: 'plastic', 5: 'trash'}
15 |
16 |
17 | class Model:
18 | def __init__(self):
19 | self.model = None
20 |
21 | def build_model(self):
22 | self.model = Sequential()
23 |
24 | self.model.add(Conv2D(filters=32, kernel_size=3, padding='same', activation='relu', input_shape=(300, 300, 3)))
25 | self.model.add(MaxPooling2D(pool_size=2))
26 |
27 | self.model.add(Conv2D(filters=64, kernel_size=3, padding='same', activation='relu'))
28 | self.model.add(MaxPooling2D(pool_size=2))
29 |
30 | self.model.add(Conv2D(filters=32, kernel_size=3, padding='same', activation='relu'))
31 | self.model.add(MaxPooling2D(pool_size=2))
32 |
33 | self.model.add(Conv2D(filters=32, kernel_size=3, padding='same', activation='relu'))
34 | self.model.add(MaxPooling2D(pool_size=2))
35 |
36 | self.model.add(Flatten()) # 扁平化参数
37 | self.model.add(Dense(64, activation='relu'))
38 | self.model.add(Dense(6, activation='softmax'))
39 | self.model.summary()
40 |
41 | def train_model(self):
42 | # 优化器, 主要有Adam、sgd、rmsprop等方式。
43 | # sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
44 | self.model.compile(loss='categorical_crossentropy',
45 | optimizer='adam', # sgd 'adam'
46 | metrics=['accuracy'])
47 | # 自动扩充训练样本
48 |
49 | train_datagen = ImageDataGenerator(
50 | rescale=1. / 255, # 数据缩放,把像素点的值除以255,使之在0到1之间
51 | shear_range=0.1, # 错切变换角度
52 | zoom_range=0.1, # 随机缩放范围
53 | width_shift_range=0.1,
54 | height_shift_range=0.1,
55 | horizontal_flip=True,
56 | vertical_flip=True,
57 | validation_split=0.1
58 | )
59 | # 生成验证集
60 |
61 | val_datagen = ImageDataGenerator(
62 | rescale=1. / 255, validation_split=0.1)
63 |
64 | # 以文件分类名划分label
65 | train_generator = train_datagen.flow_from_directory(
66 | root_path + '/dataset-resized',
67 | # 整数元组 (height, width),默认:(300, 300)。 所有的图像将被调整到的尺寸。
68 | target_size=(300, 300),
69 | # 一批数据的大小
70 | batch_size=16,
71 | # "categorical", "binary", "sparse", "input" 或 None 之一。
72 | # 默认:"categorical",返回one-hot 编码标签。
73 | class_mode='categorical',
74 | subset='training',
75 | seed=0)
76 | val_generator = val_datagen.flow_from_directory(
77 | root_path + '/dataset-resized',
78 | target_size=(300, 300),
79 | batch_size=16,
80 | class_mode='categorical',
81 | subset='validation',
82 | seed=0)
83 | # 编译模型
84 | try:
85 | history_fit = self.model.fit_generator(train_generator,
86 | epochs=100, # 迭代总轮数
87 | steps_per_epoch=2276 // 32, # generator 产生的总步数/(批次样本)
88 | validation_data=val_generator, #验证数据的生成器
89 | validation_steps=251 // 32)
90 | with open(root_path + "/model/history_fit.json", "w") as json_file:
91 | json_file.write(str(history_fit))
92 |
93 | acc = history_fit.history['acc']
94 | val_acc = history_fit.history['val_acc']
95 | loss = history_fit.history['loss']
96 | val_loss = history_fit.history['val_loss']
97 |
98 | epochs = range(1, len(acc) + 1)
99 | plt.figure("acc")
100 | plt.plot(epochs, acc, 'r-', label='Training acc')
101 | plt.plot(epochs, val_acc, 'b', label='validation acc')
102 | plt.title('The comparision of train_acc and val_acc')
103 | plt.legend()
104 | plt.show()
105 |
106 | plt.figure("loss")
107 | plt.plot(epochs, loss, 'r-', label='Training loss')
108 | plt.plot(epochs, val_loss, 'b', label='validation loss')
109 | plt.title('The comparision of train_loss and val_loss')
110 | plt.legend()
111 | plt.show()
112 | except StopIteration:
113 | pass
114 |
115 | def save_model(self):
116 | model_json = self.model.to_json()
117 | with open(root_path + '/model/model_json.json', "w") as json_file:
118 | json_file.write(model_json)
119 | self.model.save_weights(root_path + '/model/model_weight.h5')
120 | self.model.save(root_path + '/model/model.h5')
121 | print('model saved')
122 |
123 | def load_model(self):
124 | json_file = open(root_path + '/model/model_json.json') # 加载模型结构文件
125 | loaded_model_json = json_file.read()
126 | json_file.close()
127 | model = model_from_json(loaded_model_json) # 结构文件转化为模型
128 | # 加载权重
129 | model.load_weights(root_path + '/model/model_weight.h5') # h5文件保存模型的权重数据
130 | return model
131 |
132 |
133 | def generate_result(result):
134 | for i in range(6):
135 | if (result[0][i] == 1):
136 | return labels[i]
137 |
138 |
139 | if __name__ == '__main__':
140 | model = Model()
141 | model.build_model()
142 | print('model built')
143 | model.train_model()
144 | print('model trained')
145 | model.save_model()
146 | print('model saved')
147 | # 已经有模型,直接加载,注释掉上面三个函数
148 | model1 = model.load_model()
149 | print('model loaded')
150 | img_path = "./dataset-resized/glass/glass2.jpg"
151 | # 把图片转换成为numpy数组
152 | img = image.load_img(img_path, target_size=(300, 300))
153 | img = image.img_to_array(img)
154 | img = np.expand_dims(img, axis=0)
155 | result = model1.predict(img)
156 | print(generate_result(result))
--------------------------------------------------------------------------------
/项目代码/garbage_ui.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog, QMessageBox
3 | from main_ui import *
4 | from kid_ui import *
5 | from classify import *
6 | import numpy as np
7 | class Myshow(QtWidgets.QWidget, Ui_MainWindow):
8 | def __init__(self):
9 | super(Myshow, self).__init__()
10 | self.setupUi(self)
11 | self.pushButton_2.clicked.connect(self.ChoosePath)#上传图片
12 | self.pushButton.clicked.connect(self.Recognition)#开始识别
13 | self.test_path = './dataset-resized'
14 |
15 | def ChoosePath(self):
16 | file_name = QtWidgets.QFileDialog.getOpenFileName(self, "open file dialog", self.test_path, "图片(*.jpg)")
17 | print(file_name[0])
18 | self.test_path = file_name[0]
19 | self.lineEdit_2.setText(self.test_path)#显示路径
20 | self.label_4.setPixmap(QtGui.QPixmap(self.test_path)) #显示待测图片
21 |
22 | # 清空不相关内容
23 | self.lineEdit.clear()
24 |
25 | def Recognition(self):
26 | input=self.lineEdit_2.text()#存获取的地址
27 | garbage=''
28 | gt=''
29 | if (input==""):
30 | print(QMessageBox.warning(self, "警告", "请插入图片", QMessageBox.Yes, QMessageBox.Yes))
31 | return
32 | else:
33 | model = Model()
34 | model1 = model.load_model()
35 | print('model loaded')
36 | # img_path = "./dataset-resized/glass/glass39.jpg"
37 | img_path = input
38 | img = image.load_img(img_path, target_size=(300, 300))
39 | img = image.img_to_array(img)
40 | img = np.expand_dims(img, axis=0)
41 | result = model1.predict(img)
42 | print(generate_result(result))
43 | result0=generate_result(result)
44 | garbage = result0
45 | # 对结果进行分类
46 | if garbage=="trash":
47 | gt="干垃圾"
48 | elif garbage=="paper":
49 | gt="可回收垃圾"
50 | elif garbage=="cardboard":
51 | gt = "可回收垃圾"
52 | elif garbage=="glass":
53 | gt = "可回收垃圾"
54 | elif garbage=="metal":
55 | gt = "可回收垃圾"
56 | elif garbage=="plastic":
57 | gt = "可回收垃圾"
58 | self.lineEdit.setText(gt)
59 | print(QMessageBox.information(self, "提醒", "成功识别!该垃圾为:" + garbage, QMessageBox.Yes, QMessageBox.Yes))
60 | if __name__ == '__main__':
61 | app = QtWidgets.QApplication(sys.argv)
62 | # 实例化主窗口
63 | main = QMainWindow()
64 | main_ui = Ui_MainWindow()
65 | main_ui.setupUi(main)
66 | main = Myshow()
67 | main.setWindowTitle("嘎嘣脆垃圾识别系统")
68 | # 实例化子窗口
69 | child = QDialog()
70 | child.setWindowTitle("嘎嘣脆智能垃圾识别")
71 | child_ui = Ui_KidWindow()
72 | child_ui.setupUi(child)
73 |
74 | # 按钮绑定事件
75 | btn = main.pushButton_3
76 | btn.clicked.connect(child.show)
77 |
78 | #显示
79 | main.show()
80 | sys.exit(app.exec_())
81 |
--------------------------------------------------------------------------------
/项目代码/kid_ui.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 921
10 | 800
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 255
20 | 255
21 | 255
22 |
23 |
24 |
25 |
26 |
27 |
28 | 219
29 | 240
30 | 194
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | 255
40 | 255
41 | 255
42 |
43 |
44 |
45 |
46 |
47 |
48 | 219
49 | 240
50 | 194
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | 219
60 | 240
61 | 194
62 |
63 |
64 |
65 |
66 |
67 |
68 | 219
69 | 240
70 | 194
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | Dialog
79 |
80 |
81 |
82 |
83 | 280
84 | 30
85 | 361
86 | 111
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | 0
96 | 0
97 | 0
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | 0
107 | 0
108 | 0
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | 120
118 | 120
119 | 120
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | Arial
129 | 28
130 | 75
131 | true
132 |
133 |
134 |
135 | UpArrowCursor
136 |
137 |
138 | <html><head/><body><p><span style=" font-size:36pt; color:#428663;">垃圾小百科</span></p></body></html>
139 |
140 |
141 |
142 |
143 |
144 | 240
145 | 110
146 | 661
147 | 171
148 |
149 |
150 |
151 | <html><head/><body><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">有害垃圾是指:对人体健康或者自然环境造成直接或潜在危害的废弃物 </span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">有害垃圾主要包括:</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">废电池(充电电池、铅酸电池、镍镉电池、纽扣电池等)、废油漆、消毒剂、荧光灯管、含贡温度计、废药品及其包装物等</span></p></body></html>
152 |
153 |
154 |
155 |
156 |
157 | 230
158 | 260
159 | 631
160 | 181
161 |
162 |
163 |
164 | <html><head/><body><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">可回收物是指:适宜回收利用和资源化利用的生活废弃物</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">主要包括:</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">报纸、纸箱、书本、广告单、塑料瓶、塑料玩具、油桶、酒瓶、</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">玻璃杯、易拉罐、旧铁锅、旧衣服、包、旧玩偶、旧数码产品</span></p></body></html>
165 |
166 |
167 |
168 |
169 |
170 | 230
171 | 430
172 | 641
173 | 181
174 |
175 |
176 |
177 | <html><head/><body><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">厨余垃圾是指:居民日常生活及食品加工、饮食服务、单位供餐等活动中产生的垃圾。 </span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">厨余垃圾主要包括:</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">丢弃不用的菜叶、剩菜、剩饭、果皮、蛋壳、茶渣、骨头等。</span></p></body></html>
178 |
179 |
180 |
181 |
182 |
183 | 240
184 | 600
185 | 651
186 | 181
187 |
188 |
189 |
190 | <html><head/><body><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">干垃圾是指:除可回收物、有害垃圾、湿垃圾以外的其它生活废弃物 </span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">干垃圾主要包括:</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">餐盒、餐巾纸、湿纸巾、卫生间用纸、塑料袋、食品包装袋、污染</span></p><p><span style=" font-family:'微软雅黑,MS Sans Serif,sans-serif'; font-size:12pt; color:#000000; background-color:#ffedc4;">严重的纸、烟蒂、纸尿裤、一次性杯子、大骨头、贝壳、花盆等</span></p></body></html>
191 |
192 |
193 |
194 |
195 |
196 | 50
197 | 100
198 | 161
199 | 161
200 |
201 |
202 |
203 |
204 |
205 |
206 | 80
207 | 170
208 | 72
209 | 15
210 |
211 |
212 |
213 | <html><head/><body><p><img src=":/g/有害.jpg"/></p></body></html>
214 |
215 |
216 |
217 |
218 |
219 | 50
220 | 270
221 | 161
222 | 161
223 |
224 |
225 |
226 |
227 |
228 |
229 | 50
230 | 440
231 | 161
232 | 161
233 |
234 |
235 |
236 |
237 |
238 |
239 | 50
240 | 610
241 | 161
242 | 161
243 |
244 |
245 |
246 |
247 |
248 |
249 | 680
250 | 10
251 | 141
252 | 131
253 |
254 |
255 |
256 |
257 |
258 |
259 | 80
260 | 330
261 | 72
262 | 15
263 |
264 |
265 |
266 | <html><head/><body><p><img src=":/g/可回收.jpg"/></p></body></html>
267 |
268 |
269 |
270 |
271 |
272 | 90
273 | 510
274 | 72
275 | 15
276 |
277 |
278 |
279 | <html><head/><body><p><img src=":/g/厨余垃圾.jpg"/></p></body></html>
280 |
281 |
282 |
283 |
284 |
285 | 80
286 | 640
287 | 72
288 | 15
289 |
290 |
291 |
292 | <html><head/><body><p><img src=":/g/干垃圾.jpg"/></p></body></html>
293 |
294 |
295 |
296 |
297 |
298 | 710
299 | 40
300 | 72
301 | 15
302 |
303 |
304 |
305 | <html><head/><body><p><img src=":/g/你是垃圾吗.jpg"/></p></body></html>
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
--------------------------------------------------------------------------------
/项目代码/main_ui.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'a.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.9.2
6 | #
7 | # WARNING! All changes made in this file will be lost!
8 |
9 | from PyQt5 import QtCore, QtGui, QtWidgets
10 |
11 | class Ui_MainWindow(object):
12 | def setupUi(self, Dialog):
13 | Dialog.setObjectName("Dialog")
14 | Dialog.resize(904, 745)
15 | palette = QtGui.QPalette()
16 | brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
17 | brush.setStyle(QtCore.Qt.SolidPattern)
18 | palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush)
19 | brush = QtGui.QBrush(QtGui.QColor(219, 240, 194))
20 | brush.setStyle(QtCore.Qt.SolidPattern)
21 | palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Window, brush)
22 | brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
23 | brush.setStyle(QtCore.Qt.SolidPattern)
24 | palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush)
25 | brush = QtGui.QBrush(QtGui.QColor(219, 240, 194))
26 | brush.setStyle(QtCore.Qt.SolidPattern)
27 | palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Window, brush)
28 | brush = QtGui.QBrush(QtGui.QColor(219, 240, 194))
29 | brush.setStyle(QtCore.Qt.SolidPattern)
30 | palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush)
31 | brush = QtGui.QBrush(QtGui.QColor(219, 240, 194))
32 | brush.setStyle(QtCore.Qt.SolidPattern)
33 | palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Window, brush)
34 | Dialog.setPalette(palette)
35 | font = QtGui.QFont()
36 | font.setFamily("Arial Narrow")
37 | font.setPointSize(10)
38 | Dialog.setFont(font)
39 | Dialog.setLayoutDirection(QtCore.Qt.RightToLeft)
40 | Dialog.setAutoFillBackground(True)
41 | Dialog.setStyleSheet("")
42 | self.label = QtWidgets.QLabel(Dialog)
43 | self.label.setGeometry(QtCore.QRect(150, 30, 601, 111))
44 | palette = QtGui.QPalette()
45 | brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
46 | brush.setStyle(QtCore.Qt.SolidPattern)
47 | palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush)
48 | brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
49 | brush.setStyle(QtCore.Qt.SolidPattern)
50 | palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush)
51 | brush = QtGui.QBrush(QtGui.QColor(120, 120, 120))
52 | brush.setStyle(QtCore.Qt.SolidPattern)
53 | palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush)
54 | self.label.setPalette(palette)
55 | font = QtGui.QFont()
56 | font.setPointSize(15)
57 | font.setBold(True)
58 | font.setWeight(75)
59 | self.label.setFont(font)
60 | self.label.setCursor(QtGui.QCursor(QtCore.Qt.UpArrowCursor))
61 | self.label.setObjectName("label")
62 | self.label_2 = QtWidgets.QLabel(Dialog)
63 | self.label_2.setGeometry(QtCore.QRect(110, 600, 141, 81))
64 | self.label_2.setObjectName("label_2")
65 | self.graphicsView = QtWidgets.QGraphicsView(Dialog)
66 | self.graphicsView.setGeometry(QtCore.QRect(120, 200, 551, 401))
67 | self.graphicsView.setObjectName("graphicsView")
68 | self.label_4 = QtWidgets.QLabel(Dialog)
69 | self.label_4.setGeometry(QtCore.QRect(120, 200, 551, 401))
70 | self.label_4.setObjectName("label_4")
71 | self.label_4.setScaledContents(True) # label自适应图片大小
72 | self.lineEdit = QtWidgets.QLineEdit(Dialog)
73 | self.lineEdit.setGeometry(QtCore.QRect(250, 620, 421, 41))
74 | self.lineEdit.setObjectName("lineEdit")
75 | self.lineEdit_2 = QtWidgets.QLineEdit(Dialog)
76 | self.lineEdit_2.setGeometry(QtCore.QRect(220, 140, 441, 41))
77 | self.lineEdit_2.setObjectName("lineEdit_2")
78 | self.pushButton = QtWidgets.QPushButton(Dialog)
79 | self.pushButton.setGeometry(QtCore.QRect(720, 440, 111, 51))
80 | self.pushButton.setMinimumSize(QtCore.QSize(111, 51))
81 | font = QtGui.QFont()
82 | font.setFamily("Agency FB")
83 | font.setPointSize(11)
84 | font.setBold(False)
85 | font.setWeight(50)
86 | self.pushButton.setFont(font)
87 | self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft)
88 | self.pushButton.setStyleSheet("Button { background-color: rgb(255, 255, 255); border-radius: 3px; color: rgb(255, 255, 255); } QPushButton:hover { background-color: rgb(85, 170, 127); }")
89 | self.pushButton.setIconSize(QtCore.QSize(30, 30))
90 | self.pushButton.setObjectName("pushButton")
91 | self.pushButton_2 = QtWidgets.QPushButton(Dialog)
92 | self.pushButton_2.setGeometry(QtCore.QRect(720, 510, 111, 51))
93 | font = QtGui.QFont()
94 | font.setFamily("Agency FB")
95 | font.setPointSize(11)
96 | font.setBold(False)
97 | font.setWeight(50)
98 | self.pushButton_2.setFont(font)
99 | self.pushButton_2.setLayoutDirection(QtCore.Qt.RightToLeft)
100 | self.pushButton_2.setStyleSheet("Button { background-color: rgb(255, 255, 255); border-radius: 3px; color: rgb(255, 255, 255); } QPushButton:hover { background-color: rgb(85, 170, 127); }")
101 | self.pushButton_2.setObjectName("pushButton_2")
102 | self.label_3 = QtWidgets.QLabel(Dialog)
103 | self.label_3.setGeometry(QtCore.QRect(120, 140, 111, 41))
104 | self.label_3.setObjectName("label_3")
105 | self.label_5 = QtWidgets.QLabel(Dialog)
106 | self.label_5.setGeometry(QtCore.QRect(570, 680, 121, 16))
107 | font = QtGui.QFont()
108 | font.setFamily("Bahnschrift")
109 | font.setPointSize(16)
110 | self.label_5.setFont(font)
111 | self.label_5.setObjectName("label_5")
112 | self.label_6 = QtWidgets.QLabel(Dialog)
113 | self.label_6.setGeometry(QtCore.QRect(720, 20, 141, 191))
114 | self.label_6.setObjectName("label_6")
115 | self.pushButton_3 = QtWidgets.QPushButton(Dialog)
116 | self.pushButton_3.setGeometry(QtCore.QRect(720, 370, 111, 51))
117 | self.pushButton_3.setMinimumSize(QtCore.QSize(111, 51))
118 | font = QtGui.QFont()
119 | font.setFamily("Agency FB")
120 | font.setPointSize(11)
121 | font.setBold(False)
122 | font.setWeight(50)
123 | self.pushButton_3.setFont(font)
124 | self.pushButton_3.setLayoutDirection(QtCore.Qt.RightToLeft)
125 | self.pushButton_3.setStyleSheet("Button { background-color: rgb(255, 255, 255); border-radius: 3px; color: rgb(255, 255, 255); } QPushButton:hover { background-color: rgb(85, 170, 127); }")
126 | self.pushButton_3.setIconSize(QtCore.QSize(30, 30))
127 | self.pushButton_3.setObjectName("pushButton_3")
128 |
129 | self.retranslateUi(Dialog)
130 | QtCore.QMetaObject.connectSlotsByName(Dialog)
131 |
132 | def retranslateUi(self, Dialog):
133 | _translate = QtCore.QCoreApplication.translate
134 | Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
135 | self.label.setText(_translate("Dialog", "
快来看看是什么垃圾~~~
"))
136 | self.label_2.setText(_translate("Dialog", "垃圾类型:
"))
137 | self.label_4.setText(_translate("Dialog", "待测垃圾"))
138 | self.pushButton.setText(_translate("Dialog", "开始识别"))
139 | self.pushButton_2.setText(_translate("Dialog", "上传图片"))
140 | self.label_3.setText(_translate("Dialog", "图片路径:
"))
141 | self.label_5.setText(_translate("Dialog", "©2020 ev dj ch
"))
142 | self.label_6.setText(_translate("Dialog", "
"))
143 | self.pushButton_3.setText(_translate("Dialog", "垃圾科普"))
--------------------------------------------------------------------------------
/项目代码/ui.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------