Tensorflow 进行图片分类

itcalvin 的个人博客 记录精彩的程序人生 本文由博客端 https://www.itcalvin.cn 主动推送
本贴最后更新于 297 天前,其中的信息可能已经时过境迁

Tensorflow 进行图片分类

一、准备图片(最好分辨率一致,如果不一致,用以下脚本可处理成分辨率一致)

#按照指定图像大小调整尺寸
def resize_image(image, height = IMAGE_SIZE_WIDTH, width = IMAGE_SIZE_HEIGHT):
    top, bottom, left, right = (0, 0, 0, 0)
    #获取图像尺寸
    h, w, _ = image.shape
    #对于长宽不相等的图片,找到最长的一边
    longest_edge = max(h, w)    
    #计算短边需要增加多上像素宽度使其与长边等长
    if h < longest_edge:
        dh = longest_edge - h
        top = dh // 2
        bottom = dh - top
    elif w < longest_edge:
        dw = longest_edge - w
        left = dw // 2
        right = dw - left
    else:
        pass 
    #RGB颜色
    BLACK = [0, 0, 0]
    #给图像增加边界,是图片长、宽等长,cv2.BORDER_CONSTANT指定边界颜色由value指定
    constant = cv2.copyMakeBorder(image, top , bottom, left, right, cv2.BORDER_CONSTANT, value = BLACK)
    #调整图像大小并返回
    return cv2.resize(constant, (height, width))

二、图片读取(转成 tensorflow 能识别的格式)

PS:代码中的 GHHZZ、GHJK、GHQSZ 等是图片所在文件夹的名称,我将它转成数字存储在数组中
#读取训练数据
images = []
labels = []
def read_path(path_name):    
    for dir_item in os.listdir(path_name):
        #从初始路径开始叠加,合并成可识别的操作路径
        full_path = os.path.abspath(os.path.join(path_name, dir_item))
        
        if os.path.isdir(full_path):    #如果是文件夹,继续递归调用
            read_path(full_path)
        else:   #文件
            if dir_item.lower().endswith('.jpg'):
                # try:
                #     image = cv2.imread(full_path)                
                #     image = resize_image(image, IMAGE_SIZE, IMAGE_SIZE)
                # except Exception as e:
                #     print('error_path:{0}'.format(full_path))
                #     break
                image = cv2.imread(full_path)                
                image = resize_image(image, IMAGE_SIZE_WIDTH, IMAGE_SIZE_HEIGHT)
                #放开这个代码,可以看到resize_image()函数的实际调用效果
                #cv2.imwrite('1.jpg', image)
                images.append(image)
                #labels.append(path_name)  
                if path_name.endswith('GHHZZ'):
                    labels.append(0) 
                elif path_name.endswith('GHJK'):
                    labels.append(1) 
                elif path_name.endswith('GHQSZ'):
                    labels.append(2)  
                elif path_name.endswith('GHYBB'):
                    labels.append(3)  
                elif path_name.endswith('GHYKB'):
                    labels.append(4)  
                # elif path_name.endswith('GJJK'):
                #     labels.append(6) 
                # elif path_name.endswith('GJMWB'):
                #     labels.append(7) 
                # elif path_name.endswith('GJQSZ'):
                #     labels.append(8) 
                # elif path_name.endswith('GJQYC'):
                #     labels.append(9) 
                else:
                    labels.append(0)            
    return images,labels
    
 
#从指定路径读取训练数据
def load_dataset(path_name):
    images,labels = read_path(path_name)    
    #print(labels)
    #将输入的所有图片转成四维数组,尺寸为(图片数量*IMAGE_SIZE*IMAGE_SIZE*3)
    #图片为64 * 64像素,一个像素3个颜色值(RGB)
    images = np.array(images)
    print(images.shape)     
    return images, labels
 
if __name__ == '__main__':
    if len(sys.argv) != 1:
        print("Usage:%s path_name\r\n" % (sys.argv[0]))    
    else:
        images, labels = load_dataset("./data")

三、准备图片识别 CNN 模型

# coding = utf-8

import numpy as np
import tensorflow as tf
from tensorflow import keras
from load_data import load_dataset, resize_image, IMAGE_SIZE_WIDTH,IMAGE_SIZE_HEIGHT


#CNN网络模型类            
class Model:
    def __init__(self):
        self.model = None

    #建立模型
    def build_model(self, dataset, nb_classes = 5):
        #构建一个空的网络模型,它是一个线性堆叠模型,各神经网络层会被顺序添加,专业名称为序贯模型或线性堆叠模型
        self.model =tf.keras.Sequential()

        # 添加神经网络层
        self.model.add(keras.layers.Convolution2D(filters=32, kernel_size=(3, 3),padding='same', activation='relu',
                                     input_shape = dataset.input_shape))
        self.model.add(keras.layers.Convolution2D(filters=32, kernel_size=(3, 3), activation='relu'))
        self.model.add(keras.layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        self.model.add(keras.layers.Dropout(0.25))
        self.model.add(keras.layers.Convolution2D(filters=64, kernel_size=(3, 3),padding='same',  activation='relu'))
        self.model.add(keras.layers.Convolution2D(filters=64, kernel_size=(3, 3), activation='relu'))
        self.model.add(keras.layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
        self.model.add(keras.layers.Dropout(0.25))
        self.model.add(keras.layers.Flatten())
        self.model.add(keras.layers.Dense(512,activation='relu'))
        self.model.add(keras.layers.Dropout(0.5))
        self.model.add(keras.layers.Dense(nb_classes,activation='softmax'))


        #输出模型概况
        self.model.summary()

    #训练模型
    def train(self, dataset, batch_size = 32, nb_epoch = 20, data_augmentation = True):        
        # sgd =keras.optimizers.SGD(lr = 0.01, decay = 1e-6, 
        #           momentum = 0.9, nesterov = True) #采用SGD+momentum的优化器进行训练,首先生成一个优化器对象  
        adad= keras.optimizers.Adadelta(lr = 0.01,rho=0.95,epsilon=1e-08,decay = 1e-6)
        self.model.compile(loss='categorical_crossentropy',
                           optimizer=adad,
                           metrics=['accuracy'])   #完成实际的模型配置工作
        
        callbacks = [
            # Interrupt training if `val_loss` stops improving for over 2 epochs
            keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),
            # Write TensorBoard logs to `./logs` directory
            keras.callbacks.TensorBoard(log_dir='./logs')
            ]

        #不使用数据提升,所谓的提升就是从我们提供的训练数据中利用旋转、翻转、加噪声等方法创造新的
        #训练数据,有意识的提升训练数据规模,增加模型训练量
        if not data_augmentation:            
            self.model.fit(dataset.train_images,
                           dataset.train_labels,
                           batch_size = batch_size,
                           nb_epoch = nb_epoch,
                           validation_data = (dataset.valid_images, dataset.valid_labels),
                           shuffle = True,
                           callbacks=callbacks
                           )
        #使用实时数据提升
        else:            
            #定义数据生成器用于数据提升,其返回一个生成器对象datagen,datagen每被调用一
            #次其生成一组数据(顺序生成),节省内存,其实就是python的数据生成器
            datagen =keras.preprocessing.image.ImageDataGenerator(
                featurewise_center = False,             #是否使输入数据去中心化(均值为0),
                samplewise_center  = False,             #是否使输入数据的每个样本均值为0
                featurewise_std_normalization = False,  #是否数据标准化(输入数据除以数据集的标准差)
                samplewise_std_normalization  = False,  #是否将每个样本数据除以自身的标准差
                zca_whitening = False,                  #是否对输入数据施以ZCA白化
                rotation_range = 20,                    #数据提升时图片随机转动的角度(范围为0~180)
                width_shift_range  = 0.2,               #数据提升时图片水平偏移的幅度(单位为图片宽度的占比,0~1之间的浮点数)
                height_shift_range = 0.2,               #同上,只不过这里是垂直
                horizontal_flip = True,                 #是否进行随机水平翻转
                vertical_flip = False)                  #是否进行随机垂直翻转
 
            #计算整个训练样本集的数量以用于特征值归一化、ZCA白化等处理
            datagen.fit(dataset.train_images)                        
 
            #利用生成器开始训练模型
            self.model.fit_generator(datagen.flow(dataset.train_images, dataset.train_labels,
                                                   batch_size = batch_size),
                                     steps_per_epoch = dataset.train_images.shape[0]/batch_size,
                                     epochs = nb_epoch,
                                     validation_data = (dataset.valid_images, dataset.valid_labels),
                                     callbacks=callbacks)    
    
    MODEL_PATH = './calvin.model.h5'
    def save_model(self, file_path = MODEL_PATH):
         self.model.save(file_path)
 
    def load_model(self, file_path = MODEL_PATH):
         self.model = keras.models.load_model(file_path)
 
    def evaluate(self, dataset):
         score = self.model.evaluate(dataset.test_images, dataset.test_labels, verbose = 1)
         print("%s: %.2f%%" % (self.model.metrics_names[1], score[1] * 100))
 
    #
    def predict(self, image):    
        #依然是根据后端系统确定维度顺序
        if  image.shape != (1, IMAGE_SIZE_WIDTH, IMAGE_SIZE_HEIGHT, 3):
            image = resize_image(image)
            image = image.reshape((1, IMAGE_SIZE_WIDTH, IMAGE_SIZE_HEIGHT, 3))                    
        
        #浮点并归一化
        image = image.astype('float32')
        image /= 255
        
        #给出输入属于各个类别的概率,我们是二值类别,则该函数会给出输入图像属于0和1的概率各为多少
        result = self.model.predict_proba(image)
        print('result:', result)
        
        #给出类别预测:0或者1
        result = self.model.predict_classes(image)        
 
        #返回类别预测结果
        return result[0]

四、开启识别模式并随机取样验证数据集的准确性

import random
from load_data import load_dataset, resize_image, IMAGE_SIZE_WIDTH,IMAGE_SIZE_HEIGHT
from tf_dataset import Dataset
from tf_model import Model 

if __name__ == '__main__':    
    dataset = Dataset('./data/')    
    dataset.load()
    model = Model()
    model.build_model(dataset)
    model.train(dataset)
    model.save_model(file_path = './model/calvin.model.h5')
     
if __name__ == '__main__':    
    dataset = Dataset('./data/')    
    dataset.load()
    #评估模型
    model = Model()
    model.load_model(file_path = './model/calvin.model.h5')
    model.evaluate(dataset)    
  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    445 引用 • 622 回帖 • 537 关注
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    16 引用 • 18 回帖

赞助商 我要投放

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...