一.项目描述

数据集来源于kaggle猫狗大战数据集。训练集有25000张,猫狗各占一半。测试集12500张。希望计算机可以从这些训练集图片中学习到猫狗的特征,从而使得计算机可以正确的对未曾见过的猫狗图片进行分类。这就是图像分类问题,计算机视觉研究领域之一,计算机通过学习图像本身的特征将不同类别的图像区分开来。

二.评价指标

二分类评价指标

binary_crossentropy:交叉熵

pythoncnn图像分类 python图像分类代码_python

ŷ i是样本标签,yi样本输出。只有yi和ŷ i是相等时,loss才为0,否则loss就是为一个正数。而且,概率相差越大,loss就越大。这个度量概率距离的方式称为交叉熵。

二分类模型的最后一层的激活函数 是:sigmoid

pythoncnn图像分类 python图像分类代码_cv_02

二分类模型最后输出的是0到1的数。
应该使用numpy的四舍五入求取类别,并转换为整数
pred_y=int(np.round(predict_y))

多分类评价指标
categorical_crossentropy:分类交叉熵函数

pythoncnn图像分类 python图像分类代码_cv_03


ŷ i是样本标签,yi样本输出

多分类模型的最后一层的激活函数 是:softmax

pythoncnn图像分类 python图像分类代码_pythoncnn图像分类_04

softmax先把输出指数化,再归一化,得到各类概率。

假设一个问题是3分类,一个训练样本进来得到的softmax是[0.5,0.2,0.3]

假设这个正确样本类别为第一个类别。

则该样本分类交叉熵为:

pythoncnn图像分类 python图像分类代码_pythoncnn图像分类_05

多分类模型输出的是各个类别的概率,如2个样本的预测输出为:ypred=[[0.5,0.2,0.3],[0.4,0.1,0.5]]
应该使用numpy求取最大值索引
pred=np.argmax(ypred,axis=1)
得到[0,2]

三.算例实现

数据集

电脑垃圾啦,无法将全部图片都用上,跑不动。

于是猫和狗都选取原始数据集的一半左右。

猫的图片如下:

pythoncnn图像分类 python图像分类代码_python_06


狗的图片如下:

pythoncnn图像分类 python图像分类代码_python_07

各种各样的猫和狗,不容易找到统一的图片预处理方式,如处理成二值图像(但猫狗颜色不一样,二值处理有的把背景提取出来啦,毕竟二值处理需要把前景色处理成白色,为了达到此要求,有的图片得做反二值化处理)
就对图片不做任何处理吧,防止图片失真。

1数据集读取函数
注意:cv2.imread(name) 图片路径不能含有中文。
#个人喜好用OpenCV

需要对图片统一大小: cv2.resize(img, (100, 100))

import os
import numpy as np
import cv2

def ReFileName(dirPath):
    """
    :param dirPath: 文件夹路径
    :return:
    """

    # 对目录下的文件进行遍历
    x=[]
    for file in os.listdir(dirPath):
        # 判断是否是文件
        if os.path.isfile(os.path.join(dirPath, file)) == True:
           c= os.path.basename(file)
           name = dirPath + '\\' + c
           img = cv2.imread(name)
           img = cv2.resize(img, (100, 100))  # 使尺寸大小一样
           x.append(img)
    return x

2数据集读取并处理
分别读取猫狗数据集,之前我将猫和狗放在两个文件夹啦。
对数据除以255,cnn模型对数值小的数处理得比较好。
转换数据格式,图像格式为np.uint8, 转换成float型,计算机可以计算。
标签处理,猫处理成1,狗处理成0

dirPathcat = r"catdog\cat"#文件路径
cat=ReFileName(dirPathcat)#调用函数
cat=np.array(cat)/255
cat=cat.astype(np.float64)
print('输入cat.shape',cat.shape)

biaoqiancat=[1 for i in range(len(cat))]#标签处理
biaoqiancat=np.array(biaoqiancat)#标签

dirPathdog = r"catdog\dog"#文件路径
dog=ReFileName(dirPathdog)#调用函数
dog=np.array(dog)/255#数据
dog=dog.astype(np.float64)
biaoqiandog=[0 for i in range(len(dog))]#标签处理
biaoqiandog=np.array(biaoqiandog)#标签

3.数据打乱
首先拼接数据集,然后打乱数据集特征和标签。
如果是多分类问题得在打乱数据前对y进行one-hot化,如:
y=keras.utils.to_categorical(y,4)#四分类

x=np.concatenate((cat,dog),axis=0)
y=np.concatenate((biaoqiancat,biaoqiandog),axis=0)

index = [i for i in range(len(y))] # test_data为测试数据
np.random.seed(1)
np.random.shuffle(index) # 打乱索引
train_data = x[index]

train_label = y[index]

4.定义模型
电脑不行,就用个普通模型跑吧。

如果是多分类问题
最后两行为

model.add(Dense(2, activation='softmax'))  # 输出层
 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

本文模型

from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D
import keras
import time

def define_model():
    model = Sequential()

    model.add(Conv2D(filters=16,
                     kernel_size=(5, 5),
                     padding='same',
                     input_shape=(100, 100, 3),
                     activation='relu'))  # 卷积层1

    model.add(MaxPooling2D(pool_size=(2, 2)))  # 池化层2
    model.add(Dropout(0.25))

    model.add(Flatten())  # 平坦层
    model.add(Dense(10,activation='relu'))  # 隐藏层
    model.add(Dense(1, activation='sigmoid'))  # 输出层
    model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
    model.summary()
    return model

5.模型训练并保持
准确率0.7072,之前使用的是猫狗各3000多张,准确率为0.5。
现在使用的是各6000多张,准确率0.7。
原始数据集有各12000多张,原谅我,使用全部数据集电脑太卡,没服务器,学生党。

start_time = time.time()
model = define_model()
model.fit(train_data, train_label, epochs=2)
model.save('猫狗分类.h5')
end_time = time.time()
run_time = (end_time - start_time) / 60
print(run_time)  #3.931485986709595
print('训练结束')
#0.7072

四.pyqt可视化结果

建立个简单版的qt猫狗预测系统吧。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Author: yudengwu
# @Date  : 2020/7/18
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

import cv2
import keras
from keras .models import load_model
import numpy as np
import re
class picture(QWidget):
    def __init__(self):
        super(picture, self).__init__()

        self.resize(600, 400)
        self.setWindowTitle("猫狗分类")
        self.btn = QPushButton()
        self.btn.setText("打开图片")
        self.btn.clicked.connect(self.openimage)
        self.label = QLabel()
        self.label.setText('图片路径')


        self.labelimage = QLabel()
        self.labelimage.setText("显示图片")
        #self.labelimage.setFixedSize(500, 400)#设置尺寸
        self.labelimage.setStyleSheet("QLabel{background:white;}"
                                 "QLabel{color:rgb(300,300,300,120);font-size:10px;font-weight:bold;font-family:宋体;}"
                                 )

        #预测按钮
        self.btnclass=QPushButton()
        self.btnclass.setText('点击预测分类')
        self.btnclass.clicked.connect(self.fenlei)
        self.labelclass=QLabel()
        self.labelclass.setText('预测类别')
        self.labelclass.setStyleSheet("font:16pt '楷体';border-width:2px;border-style: inset;border-color:gray")


        layout1=QVBoxLayout()
        layout1.addWidget(self.btn)
        layout1.addWidget(self.label)
        layout1.addWidget(self.labelimage)

        layout2 = QVBoxLayout()
        layout2.addWidget(self.btnclass)
        layout2.addWidget(self.labelclass)

        layout=QVBoxLayout()
        layout.addLayout(layout1)
        layout.addLayout(layout2)

        self.setLayout(layout)

    def openimage(self):
        imgName, imgType = QFileDialog.getOpenFileName(self, "打开图片", "", "*.jpg;;*.png;;All Files(*)")
        #jpg = QtGui.QPixmap(imgName).scaled(self.labelimage.width(), self.label.height())#适应labelimage尺寸,前提是label设置了尺寸
        jpg = QtGui.QPixmap(imgName)
        self.labelimage.setPixmap(jpg)
        self.label.setText(str(imgName))
    def fenlei(self):
        biaoqian = {'1': '猫', '0': '狗'}
        path=self.label.text()
        newName = re.sub('(D:/机器学习/学习草稿/)','', path)
        print(newName)
        img = cv2.imread(str(newName))


        img = cv2.resize(img, (100, 100))  # 使尺寸大小一样
        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        img = np.array(img) / 255
        img = img.astype(np.float64)
        img = img.reshape(-1, 100, 100, 1)
        model = load_model('猫狗分类.h5')
        predict_y = model.predict(img)
        pred_y = int(np.round(predict_y))
        print(pred_y)
        self.labelclass.setText(biaoqian[str(pred_y)])


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    my = picture()
    my.show()
    sys.exit(app.exec_())

说明:
newName = re.sub(’(D:/机器学习/学习草稿/)’,’’, path)
这是因为我图片放在了当前项目文件夹里,导致图片绝对路径含有中文,cv.imread()会出错,我去除掉中文部分,使模型读取相对路径
如果你要读取任意文件夹里的图片,要使图片绝对路径无中文。

可以固定图片显示尺寸;

#self.labelimage.setFixedSize(500, 400)#设置尺寸
 #jpg = QtGui.QPixmap(imgName).scaled(self.labelimage.width(), self.label.height())#适应labelimage尺寸,前提是label设置了尺寸

结果
预测界面:

pythoncnn图像分类 python图像分类代码_数据集_08

识别cat

pythoncnn图像分类 python图像分类代码_python_09


识别dog

pythoncnn图像分类 python图像分类代码_cv_10

这个结果还不能让我满意,等我多学习下知识再来吧。

当然也可以调用摄像头,实现对摄像头下的猫狗进行实时识别。
可以参考博文然后自己修改程序。就是一个定时器作用,每隔多少时间识别下摄像头下的物体。
opencv进阶学习笔记1: 调用摄像头用法大全(打开摄像头,打开摄像头并实时不断截屏,读取视频并截图)


pythoncnn图像分类 python图像分类代码_cv_11