├── 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 | ![](https://raw.githubusercontent.com/skyevvv/garbage-classification/master/function.png)
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 | --------------------------------------------------------------------------------