日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

【Python2】Keras_ResNet 在Cifar10数据集上分类,Flask框架部署目标检测模型

發布時間:2024/4/24 python 90 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Python2】Keras_ResNet 在Cifar10数据集上分类,Flask框架部署目标检测模型 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 1.導入庫
  • 2.數據準備
    • 2.1 加載訓練集
    • 2.2 加載測試集
    • 2.3 對類別做One-Hot編碼
    • 2.4 對圖片像素的0-255值做歸一化,并減去均值
  • 3.搭建神經網絡
    • 3.1 定義函數resnet_layer,返回值是經過resnet_layer計算的結果
    • 3.2 定義函數resnet_v1,返回值是模型對象
    • 3.3 定義函數resnet_v2,返回值是模型對象
    • 3.4 實例化模型對象
    • 3.5 多GPU并行訓練
    • 3.6 打印模型架構信息
  • 4. 模型訓練
    • 4.1 規劃學習率(訓練到后期時學習率需減小)
    • 4.2 模型訓練時的參數設置
    • 4.3 使用圖像增強的結果做模型訓練
  • 5.模型評估
    • 5.1 加載訓練好的模型
    • 5.2 計算訓練集的準確率
    • 5.3 計算測試集的準確率
  • 6. 模型測試結果可視化
    • 6.1 隨機選100張圖可視化
    • 6.2 隨機選取100張圖片的同時,要求10個類別,每個類別取10張
  • 7.Keras中權重文件的讀寫
    • 7.1 使用load_model方法加載模型文件
    • 7.2 使用save_weights方法保存權重文件
    • 7.3 使用load_weights方法加載權重文件
  • 8. 圖像分類:純終端運行
    • 8.1 在code文件夾下新建image_server.py寫入下面服務端代碼
    • 8.2 在code文件夾下新建image_client.py寫入下面客戶端代碼
    • 8.3 cmd文件雙擊啟動
  • 9.圖像分類:有web頁面
    • 9.1 新建_11_web_image_server_2.py文件寫入下面服務端代碼:
    • 9.2 新建templates文件夾里_12_web_page.html文件
    • 9.3 cmd文件雙擊啟動
  • 10.目標檢測:純終端運行
    • 10.1 新建_21_yolov3.py文件
    • 10.2 cmd文件雙擊啟動


1.導入庫

1.keras(resnet):https://github.com/keras-team/keras/blob/master/examples/cifar10_resnet.py
2.用到的cifar10數據集和模型權重鏈接:https://pan.baidu.com/s/1L4oZAPg_9B_YipPeihmBwQ 提取碼:8jcw
5個data_batch文件,每個1萬數據



# 加入下面2行,可以使py代碼文件中的修改即時生效 %load_ext autoreload %autoreload 2 # 下行如果python2使用print,也得加上括號 from __future__ import print_function import keras # 導入keras庫的這4種層:全連接層Dense,2維卷積層Conv2D,批量歸一化層BatchNormalization,激活層Activation from keras.layers import Dense, Conv2D, BatchNormalization, Activation # 導入keras庫的這3種層:平均2維池化層AveratePooling2D, 輸入層Input,激活層Activation from keras.layers import AveragePooling2D, Input, Flatten # 導入keras庫的優化器:Adam優化器 from keras.optimizers import Adam # 導入keras庫的回傳函數:模型檢查點ModelCheckpoint, LearningRateScheduler from keras.callbacks import ModelCheckpoint, LearningRateScheduler # 導入keras庫的回傳函數:學習率遞減ReduceLROnPlateau from keras.callbacks import ReduceLROnPlateau # 導入keras庫的圖片處理函數:圖片數據生成器 from keras.preprocessing.image import ImageDataGenerator # 導入keras庫的正則化函數:L2正則化 from keras.regularizers import l2 # 導入keras庫的后端:backend中文叫做后端,取別名為K from keras import backend as K # 導入keras庫的模型函數:Model from keras.models import Model # 導入keras庫的數據集類:cifar10 from keras.datasets import cifar10 # 導入必需的常用庫 import numpy as np import os

2.數據準備

2.1 加載訓練集

官方代碼沒有指定路徑:

from keras.datasets.cifar import load_batch #load_batch用于加載指定路徑 ## 加載數據集cifar10里面的訓練集 def load_train_dataset(dirPath='../resources/cifar-10-batches-py/'):train_sample_quantity = 50000image_width = 32image_height = 32channel_quantity = 3train_X = np.zeros((train_sample_quantity, channel_quantity, image_width, image_height),dtype='uint8')train_y = np.zeros((train_sample_quantity, ),dtype='uint8') #下面循環寫入相同大小零矩陣for i in range(1, 6):fileName = 'data_batch_%d' %i #看文件名格式filePath = os.path.join(dirPath, fileName)startIndex = (i - 1) * 10000endIndex = i * 10000 #用到了load_batch,訓練集定義5萬,循環5次,0-1萬行,1萬-2萬行....train_X[startIndex:endIndex, :, :, :], train_y[startIndex:endIndex] = load_batch(filePath)print('train_X矩陣轉置前:', train_X.shape)# 從官網上下載的數據集的4個維度為樣本個數n、通道數c、寬度w、高度h# Keras基于Tensorflow,數據的維度順序要求:樣本個數n、寬度w、高度h、通道數c,所以使用np.transpose完成矩陣轉置train_X = train_X.transpose(0, 2, 3, 1)print('train_X矩陣轉置后:', train_X.shape)return train_X, train_ydirPath = '../resources/cifar-10-batches-py/' train_imageData, train_y = load_train_dataset()

2.2 加載測試集

# 加載數據集cifar10里面的測試集 def load_test_dataset(dirPath='../resources/cifar-10-batches-py/'):fileName = 'test_batch'filePath = os.path.join(dirPath, fileName)test_X, test_y = load_batch(filePath)print('test_X矩陣轉置前:', test_X.shape)test_X = test_X.transpose(0, 2, 3, 1)print('test_X矩陣轉置后:', test_X.shape)return test_X, test_ydirPath = '../resources/cifar-10-batches-py/' test_imageData, test_y = load_test_dataset()

2.3 對類別做One-Hot編碼

# 對類別ID做One-Hot編碼 from keras.utils import to_categoricalclass_quantity = 10 train_Y = to_categorical(train_y, class_quantity) test_Y = to_categorical(test_y, class_quantity)

2.4 對圖片像素的0-255值做歸一化,并減去均值

train_X = train_imageData.astype('float32') / 255 test_X = test_imageData.astype('float32') / 255 pixel_mean = np.mean(train_X, axis=0) print('pixel_mean.shape:', pixel_mean.shape) train_X = train_X - pixel_mean test_X = test_X - pixel_mean

3.搭建神經網絡

3.1 定義函數resnet_layer,返回值是經過resnet_layer計算的結果

def resnet_layer(inputs, #定義了一層resnet_layernum_filters=16,kernel_size=3,strides=1,activation='relu',batch_normalization=True,conv_first=True):conv = Conv2D(num_filters,kernel_size=kernel_size,strides=strides,padding='same',kernel_initializer='he_normal',kernel_regularizer=l2(1e-4))x = inputsif conv_first: #為resnet V1x = conv(x) #conv為一函數,相當于下圖weightif batch_normalization:x = BatchNormalization()(x)if activation is not None:x = Activation(activation)(x)else: #為resnet V2if batch_normalization:x = BatchNormalization()(x) #BatchNormalization()實例化一個函數對象if activation is not None:x = Activation(activation)(x)x = conv(x)return x

3.2 定義函數resnet_v1,返回值是模型對象

def resnet_v1(input_shape, depth, num_classes=10):if (depth - 2) % 6 != 0:raise ValueError('depth should be 6n+2 (eg 20, 32, 44 in [a])')# Start model definition.num_filters = 16num_res_blocks = int((depth - 2) / 6)inputs = Input(shape=input_shape)x = resnet_layer(inputs=inputs)# Instantiate the stack of residual unitsfor stack in range(3):for res_block in range(num_res_blocks):strides = 1if stack > 0 and res_block == 0: # first layer but not first stackstrides = 2 # downsampley = resnet_layer(inputs=x,num_filters=num_filters,strides=strides)y = resnet_layer(inputs=y,num_filters=num_filters,activation=None)if stack > 0 and res_block == 0: # first layer but not first stack# linear projection residual shortcut connection to match# changed dimsx = resnet_layer(inputs=x,num_filters=num_filters,kernel_size=1,strides=strides,activation=None,batch_normalization=False)x = keras.layers.add([x, y])x = Activation('relu')(x)num_filters *= 2# Add classifier on top.# v1 does not use BN after last shortcut connection-ReLUx = AveragePooling2D(pool_size=8)(x)y = Flatten()(x)outputs = Dense(num_classes,activation='softmax',kernel_initializer='he_normal')(y)# Instantiate model.model = Model(inputs=inputs, outputs=outputs)return model

3.3 定義函數resnet_v2,返回值是模型對象

def resnet_v2(input_shape, depth, num_classes=10):if (depth - 2) % 9 != 0:#深度必須是9n+2,比如20層,56層,110層raise ValueError('depth should be 9n+2 (eg 56 or 110 in [b])')# Start model definition.num_filters_in = 16 # 卷積核數量num_res_blocks = int((depth - 2) / 9)inputs = Input(shape=input_shape)x = resnet_layer(inputs=inputs, # resnet lay 0如下表,第一次調用了一層resnet_layernum_filters=num_filters_in,conv_first=True)# Instantiate the stack of residual units 實例化剩余單元的堆棧for stage in range(3):for res_block in range(num_res_blocks):activation = 'relu'batch_normalization = Truestrides = 1if stage == 0: #如果stage和res_block == 0,不進行activation,batch_normalization,num_filters_out = num_filters_in * 4if res_block == 0: # first layer and first stageactivation = Nonebatch_normalization = Falseelse:num_filters_out = num_filters_in * 2if res_block == 0: # first layer but not first stagestrides = 2 # downsample# bottleneck residual unity = resnet_layer(inputs=x,num_filters=num_filters_in,kernel_size=1,strides=strides,activation=activation,batch_normalization=batch_normalization,conv_first=False)y = resnet_layer(inputs=y,num_filters=num_filters_in,conv_first=False)y = resnet_layer(inputs=y,num_filters=num_filters_out,kernel_size=1,conv_first=False)if res_block == 0:# linear projection residual shortcut connection to match# changed dimsx = resnet_layer(inputs=x,num_filters=num_filters_out,kernel_size=1,strides=strides,activation=None,batch_normalization=False)x = keras.layers.add([x, y]) #實現shotcutnum_filters_in = num_filters_out#如上三個resnet_layer調用一個shotcut# Add classifier on top.# v2 has BN-ReLU before Poolingx = BatchNormalization()(x) #如下三行代碼對應下表對應后面幾行x = Activation('relu')(x)x = AveragePooling2D(pool_size=8)(x)y = Flatten()(x)outputs = Dense(num_classes,activation='softmax',kernel_initializer='he_normal')(y)# Instantiate model.model = Model(inputs=inputs, outputs=outputs)return model

3.4 實例化模型對象

# Model version # Orig paper: version = 1 (ResNet v1), Improved ResNet: version = 2 (ResNet v2) version = 2 # Computed depth from supplied model parameter n n = 2 if version == 1:depth = n * 6 + 2 elif version == 2:depth = n * 9 + 2# 根據ResNet版本,獲取對應的模型對象 if version == 2:model = resnet_v2(input_shape=input_shape, depth=depth) else:model = resnet_v1(input_shape=input_shape, depth=depth)

3.5 多GPU并行訓練

https://github.com/matterport/Mask_RCNN/blob/master/mrcnn/parallel_model.py
打開parallel_model.py文件,在原來文件加入紅點一行:

from parallel_model import ParallelModelgpu_count = 2 model = ParallelModel(model, gpu_count) model.compile(loss='categorical_crossentropy',optimizer=Adam(lr=0.001),metrics=['accuracy'])

3.6 打印模型架構信息

# Model name, depth and version model_type = 'ResNet%dv%d' % (depth, version) print(model_type) model.summary()


如下所示三個resnet_layer為一個shotcut,但如上圖是兩個layer再加,作者用的三個效果更好

用于數據集cifar10的圖像分類網絡ResNet20v2架構處理層_0: 輸入層,調用keras.layers中的Input類,通過傳遞參數shape實例化對象數據層_0:矩陣形狀: N x 32 x 32 x 3resnet_layer_0 #對應2.3節第一個x = resnet_layer #第一個3表示上層通道數或卷積核fliter數 處理層_1:conv2d_1 卷積核形狀: 3 x 3 x 3 x 16 / 1 #輸入3大小3*3輸出16,步長1,因為輸出是16,所以加16個b #所以3*3*3*16=432(卷積核占的參數量)再加16為第一層conv2d_1參數個數448 數據層_1:矩陣形狀: N x 32 x 32 x 16 #None表示batch size 處理層_2:batch_normalization_1數據層_2: 矩陣形狀: N x 32 x 32 x 16處理層_3:activation_1數據層_3:矩陣形狀: N x 32 x 32 x 16 resnet_layer_1 處理層_4:conv2d_2 卷積核形狀: 16 x 1 x 1 x 16 / 1 數據層_4:矩陣形狀: N x 32 x 32 x 16resnet_layer_2 處理層_5:batch_normalization_2數據層_5:矩陣形狀: N x 32 x 32 x 16處理層_6:activation_2數據層_6: 矩陣形狀: N x 32 x 32 x 16處理層_7:conv2d_3 卷積核形狀: 16 x 3 x 3 x 16 / 1數據層_7:矩陣形狀: N x 32 x 32 x 16resnet_layer_3 處理層_8: batch_normalization_3數據層_8:矩陣形狀: N x 32 x 32 x 16處理層_9:activation_3 數據層_9:矩陣形狀: N x 32 x 32 x 16處理層_10:conv2d_4 卷積核形狀: 16 x 1 x 1 x 64 / 1數據層_10:矩陣形狀: N x 32 x 32 x 64shortcut_1 #不默認對上層處理,取指定層處理,相加,route_1是自己定義的處理層_11:route_1 取數據層_3 #數據層_3和下行數據層_11是同一層數據層_11:矩陣形狀: N x 32 x 32 x 16 處理層_12:conv2d_5 卷積核形狀: 16 x 1 x 1 x 64 / 1#(32-1+0)/1 +1=32,公式里第一個32表示上層長32,第二個32是得出這層長#N x 32 x 32 x 16經過16 x 1 x 1 x 64 / 1處理得出N x 32 x 32 x 64即下行數據層_12:矩陣形狀: N x 32 x 32 x 64 處理層_13:add_1 把數據層_10、數據層_12相加數據層_13:矩陣形狀: N x 32 x 32 x 64 /////////////////////////////////////////////////////////////////////// resnet_layer_4處理層_14:batch_normalization_4數據層_14:矩陣形狀: N x 32 x 32 x 64處理層_15:activation_4數據層_15:矩陣形狀: N x 32 x 32 x 64處理層_16:conv2d_6 卷積核形狀: 64 x 1 x 1 x 16 / 1 數據層_16:矩陣形狀: N x 32 x 32 x 16resnet_layer_5 處理層_17:batch_normalization_5數據層_17:矩陣形狀: N x 32 x 32 x 16處理層_18:activation_5數據層_18:矩陣形狀: N x 32 x 32 x 16處理層_19:conv2d_7 卷積核形狀: 16 x 3 x 3 x 16 / 1 數據層_19:矩陣形狀: N x 32 x 32 x 16resnet_layer_6處理層_20:batch_normalization_6數據層_20:矩陣形狀: N x 32 x 32 x 16處理層_21:activation_6數據層_21:矩陣形狀: N x 32 x 32 x 16處理層_22:conv2d_8 卷積核形狀: 16 x 1 x 1 x 64 / 1數據層_22:矩陣形狀: N x 32 x 32 x 64shortcut_2處理層_23:add_2 把數據層_22、數據層_13相加數據層_23:矩陣形狀: N x 32 x 32 x 64//////////////////////////////////////////////////////////////////////resnet_layer_7 處理層_24:batch_normalization_7 數據層_24:矩陣形狀: N x 32 x 32 x 64處理層_25:activation_7數據層_25:矩陣形狀: N x 32 x 32 x 64處理層_26:conv2d_9 卷積核形狀: 64 x 1 x 1 x 64 / 2數據層_26:矩陣形狀: N x 16 x 16 x 64 #通道擴展指最后位64變大,#下采樣指由32*32變為16*16resnet_layer_8處理層_25:batch_normalization_8數據層_25:矩陣形狀: N x 16 x 16 x 64處理層_26:activation_8數據層_26:矩陣形狀: N x 16 x 16 x 64處理層_27:conv2d_10 卷積核形狀: 64 x 3 x 3 x 64 / 1數據層_27:矩陣形狀: N x 16 x 16 x 64resnet_layer_9處理層_28:batch_normalization_9數據層_28:矩陣形狀: N x 16 x 16 x 64處理層_29:activation_9數據層_29:矩陣形狀 N x 16 x 16 x 64處理層_30:conv2d_11 卷積核形狀: 64 x 1 x 1 x 128 / 1數據層_30:矩陣形狀 N x 16 x 16 x 128shortcut_3處理層_31:route_2 取數據層_23數據層_31:矩陣形狀: N x 32 x 32 x 64處理層_32:conv2d_12 卷積核形狀: 64 x 1 x 1 x 128 / 2數據層_32:矩陣形狀: N x 16 x 16 x 128處理層_33:add_3 把數據層_32、數據層_30相加數據層_33:矩陣形狀: N x 16 x 16 x 128 ////////////////////////////////////////////////////////////////////resnet_layer_10 處理層_34:batch_normalization_10數據層_34:矩陣形狀: N x 16 x 16 x 128處理層_35:activation_10數據層_35:矩陣形狀: N x 16 x 16 x 128處理層_36:conv2d_13 卷積核形狀: 128 x 1 x 1 x 64 / 1數據層_36:矩陣形狀: N x 16 x 16 x 64resnet_layer_11處理層_37:batch_normalization_11數據層_37:矩陣形狀: N x 16 x 16 x 64處理層_38:activation_11數據層_38:矩陣形狀: N x 16 x 16 x 64處理層_39:conv2d_14 卷積核形狀: 64 x 3 x 3 x 64 / 1數據層_39:矩陣形狀: N x 16 x 16 x 64resnet_layer_12 處理層_40:batch_normalization_12數據層_40:矩陣形狀: N x 16 x 16 x 64處理層_41:activation_12數據層_41:矩陣形狀: N x 16 x 16 x 64處理層_42:conv2d_15 卷積核形狀: 64 x 1 x 1 x 128 / 1數據層_42:矩陣形狀: N x 16 x 16 x 128shortcut_4處理層_43:add_4 把數據層_42、數據層_33相加數據層_43:矩陣形狀: N x 16 x 16 x 128/////////////////////////////////////////////////////////////////////////resnet_layer_13 處理層_44:batch_normalization_13數據層_44:矩陣形狀: N x 16 x 16 x 128處理層_45:activation_13數據層_45:矩陣形狀: N x 16 x 16 x 128處理層_46:conv2d_16 卷積核形狀: 128 x 1 x 1 x 128 / 2數據層_46:矩陣形狀: N x 8 x 8 x 128 resnet_layer_14 處理層_47:batch_normalization_14數據層_47:矩陣形狀: N x 8 x 8 x 128處理層_48:activation_14數據層_48:矩陣形狀: N x 8 x 8 x 128處理層_49:conv2d_17 卷積核形狀: 128 x 3 x 3 x 128 / 1數據層_49:矩陣形狀: N x 8 x 8 x 128resnet_layer_15 處理層_50:batch_normalization_15數據層_50:矩陣形狀: N x 8 x 8 x 128處理層_51:activation_15數據層_51:矩陣形狀: N x 8 x 8 x 128處理層_52:conv2d_18 卷積核形狀: 128 x 1 x 1 x 256 / 1數據層_52:矩陣形狀: N x 8 x 8 x 256shortcut_5處理層_53:route_3 取數據層_43數據層_53:矩陣形狀: N x 16 x 16 x 128 處理層_54:conv2d_19 卷積核形狀: 128 x 1 x 1 x 256 / 2數據層_54:矩陣形狀: N x 8 x 8 x 256處理層_55:add_5 把數據層_54、數據層_52相加數據層_55:矩陣形狀: N x 8 x 8 x 256 //////////////////////////////////////////////////////////////////////// resnet_layer_16 處理層_56:batch_normalization_16數據層_56:矩陣形狀: N x 8 x 8 x 256 處理層_57:activation_16數據層_57:矩陣形狀: N x 8 x 8 x 256 處理層_58:conv2d_20 卷積核形狀: 256 x 1 x 1 x 128 / 1數據層_58:矩陣形狀: N x 8 x 8 x 128 resnet_layer_17處理層_59:batch_normalization_17數據層_59:矩陣形狀: N x 8 x 8 x 128處理層_60:activation_17數據層_60:矩陣形狀: N x 8 x 8 x 128 處理層_61:conv2d_21 卷積核形狀: 128 x 3 x 3 x 128 / 1數據層_61:矩陣形狀: N x 8 x 8 x 128resnet_layer_18處理層_62:batch_normalization_18數據層_62:矩陣形狀: N x 8 x 8 x 128處理層_63:activation_18數據層_63:矩陣形狀: N x 8 x 8 x 128 處理層_64:conv2d_22 卷積核形狀: 128 x 1 x 1 x 256 / 1數據層_64:矩陣形狀: N x 8 x 8 x 256shortcut_6 #18層每3次一共做了6次shortcut處理層_62:add_6 把數據層_64、數據層_55相加數據層_62:矩陣形狀: N x 8 x 8 x 256/////////////////////////////////////////////////////////////////////classifier#如果是yoloV3就是yolo層了處理層_63:batch_normalization_19數據層_63:矩陣形狀: N x 8 x 8 x 256處理層_64:activation_19數據層_64:矩陣形狀: N x 8 x 8 x 256處理層_65:average_pooling2d_1數據層_65:矩陣形狀: N x 1 x 1 x 256處理層_66:flatten_1數據層_66:矩陣形狀: N x 256處理層_67:dense_1數據層_67:矩陣形狀: N x 10

所以ResNet20_V2:這個20是conv共19層(layer0)+dense1層

4. 模型訓練

4.1 規劃學習率(訓練到后期時學習率需減小)

def lr_schedule(epoch):lr = 1e-3if epoch > 180:lr *= 0.5e-3elif epoch > 160:lr *= 1e-3elif epoch > 120:lr *= 1e-2elif epoch > 80:lr *= 1e-1print('Learning rate: ', lr)return lr

4.2 模型訓練時的參數設置

# Training parameters batch_size = 64 # orig paper trained all networks with batch_size=128 epochs = 200# Prepare model model saving directory. save_dir = os.path.abspath('../resources/saved_models') model_name = 'cifar10_%s_model.{epoch:03d}.h5' % model_type if not os.path.isdir(save_dir):os.makedirs(save_dir) filepath = os.path.join(save_dir, model_name)# Prepare callbacks for model saving and for learning rate adjustment. checkpoint = ModelCheckpoint(filepath=filepath,monitor='val_acc',verbose=0,save_best_only=True) lr_scheduler = LearningRateScheduler(lr_schedule) lr_reducer = ReduceLROnPlateau(factor=np.sqrt(0.1),cooldown=0,patience=5,min_lr=0.5e-6) callbacks = [checkpoint, lr_reducer, lr_scheduler]

4.3 使用圖像增強的結果做模型訓練

data_augmentation = True if data_augmentation:print('Using real-time data augmentation.')# This will do preprocessing and realtime data augmentation:datagen = ImageDataGenerator(# set input mean to 0 over the datasetfeaturewise_center=False,# set each sample mean to 0samplewise_center=False,# divide inputs by std of datasetfeaturewise_std_normalization=False,# divide each input by its stdsamplewise_std_normalization=False,# apply ZCA whiteningzca_whitening=False,# epsilon for ZCA whiteningzca_epsilon=1e-06,# randomly rotate images in the range (deg 0 to 180)rotation_range=0,# randomly shift images horizontallywidth_shift_range=0.1,# randomly shift images verticallyheight_shift_range=0.1,# set range for random shearshear_range=0.,# set range for random zoomzoom_range=0.,# set range for random channel shiftschannel_shift_range=0.,# set mode for filling points outside the input boundariesfill_mode='nearest',# value used for fill_mode = "constant"cval=0.,# randomly flip imageshorizontal_flip=True,# randomly flip imagesvertical_flip=False,# set rescaling factor (applied before any other transformation)rescale=None,# set function that will be applied on each inputpreprocessing_function=None,# image data format, either "channels_first" or "channels_last"data_format=None,# fraction of images reserved for validation (strictly between 0 and 1)validation_split=0.0)datagen.fit(x_train)# Fit the model on the batches generated by datagen.flow().model.fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size),validation_data=(x_test, y_test),epochs=epochs,verbose=1, workers=4,callbacks=callbacks)


附:python的生成器與keras.preprocessing.image文件的ImageDataGenerator類的關系:
問號可以找在哪個路徑:


如下圖打開image.py發現是繼承image:


找image:

5.模型評估

5.1 加載訓練好的模型

from keras.models import load_model from keras.optimizers import Adammodel_filePath = '../resources/saved_models/cifar10_ResNet56v2_model.162.h5' model = load_model(model_filePath) model.compile(loss='categorical_crossentropy',optimizer=Adam(lr=0.001),metrics=['accuracy'])

5.2 計算訓練集的準確率

scores = model.evaluate(train_X, train_Y, verbose=1, batch_size=1000) print('Test loss:%.6f' %scores[0]) print('Test accuracy:%.6f' %scores[1])

5.3 計算測試集的準確率

scores = model.evaluate(test_X, test_Y, verbose=1, batch_size=1000) print('Test loss:%.6f' %scores[0]) print('Test accuracy:%.6f' %scores[1])

6. 模型測試結果可視化

6.1 隨機選100張圖可視化

import math import matplotlib.pyplot as plt import numpy as np %matplotlib inline import matplotlib matplotlib.rcParams['font.sans-serif'] = ['SimHei'] import randomdef draw_image(position, image, title, isTrue):plt.subplot(*position)plt.imshow(image)plt.axis('off')if not isTrue:plt.title(title, color='red')else:plt.title(title)def batch_draw_images(model, batch_size, test_imageData, test_X, test_y, id2name_dict):index_list = list(range(len(test_imageData)))selected_index_list = random.sample(index_list, batch_size)true_imageData = test_imageData[selected_index_list]true_X = test_X[selected_index_list]true_y = np.array(test_y)[selected_index_list]predict_Y = model.predict(true_X)predict_y = np.argmax(predict_Y, axis=1)row_number = math.ceil(batch_size ** 0.5)column_number = row_numberplt.figure(figsize=(row_number+8, column_number+8))for i in range(row_number):for j in range(column_number):index = i * column_number + jif index < batch_size:position = (row_number, column_number, index+1)image = true_imageData[index]actual_classId = true_y[index]predict_classId = predict_y[index]isTrue = actual_classId==predict_classIdactual_className = id2name_dict[actual_classId]predict_className = id2name_dict[predict_classId]title = 'actual:%s\npredict:%s' %(actual_className, predict_className)draw_image(position, image, title, isTrue)batch_size = 100 #展示100張圖 className_list = ['飛機', '汽車', '鳥', '貓', '鹿', '狗', '青蛙', '馬', '船', '卡車'] id2name_dict = {a:b for a, b in enumerate(className_list)} batch_draw_images(model, batch_size, test_imageData, test_X, test_y, id2name_dict) plt.show()

6.2 隨機選取100張圖片的同時,要求10個類別,每個類別取10張

def get_selectedIndexList(test_y, batch_size):assert batch_size % 10 == 0, 'batch_size must be times by 10, or you change function get_selectedIndexList'column_number = int(batch_size / 10)classId_ndarray = np.unique(test_y)selected_index_list = []for i, classId in enumerate(classId_ndarray):index_ndarray = np.where(test_y==classId)[0]selected_index_ndarray = np.random.choice(index_ndarray, column_number)selected_index_list.extend(selected_index_ndarray.tolist())return selected_index_list def batch_draw_images_2(model, selected_index_list, test_imageData, test_X, test_y, id2name_dict):true_imageData = test_imageData[selected_index_list]true_X = test_X[selected_index_list]true_y = np.array(test_y)[selected_index_list]predict_Y = model.predict(true_X)predict_y = np.argmax(predict_Y, axis=1)row_number = math.ceil(batch_size ** 0.5)column_number = row_numberplt.figure(figsize=(row_number+8, column_number+8))for i in range(row_number):for j in range(column_number):index = i * column_number + jif index < batch_size:position = (row_number, column_number, index+1)image = true_imageData[index]actual_classId = true_y[index]predict_classId = predict_y[index]isTrue = actual_classId==predict_classIdactual_className = id2name_dict[actual_classId]predict_className = id2name_dict[predict_classId]title = 'actual:%s\npredict:%s' %(actual_className, predict_className)draw_image(position, image, title, isTrue)batch_size = 100 className_list = ['飛機', '汽車', '鳥', '貓', '鹿', '狗', '青蛙', '馬', '船', '卡車'] id2name_dict = {a:b for a, b in enumerate(className_list)} selected_index_list = get_selectedIndexList(test_y, batch_size) batch_draw_images_2(model, selected_index_list, test_imageData, test_X, test_y, id2name_dict) plt.show()

7.Keras中權重文件的讀寫

7.1 使用load_model方法加載模型文件

from keras.models import load_model from keras.optimizers import Adammodel_filePath = '../resources/saved_models/cifar10_ResNet56v2_model.162.h5' model = load_model(model_filePath) model.compile(loss='categorical_crossentropy',optimizer=Adam(lr=0.001),metrics=['accuracy'])

7.2 使用save_weights方法保存權重文件

weights_h5FilePath = '../resources/saved_models/resnet56v2_weights.h5' model.save_weights(weights_h5FilePath)

7.3 使用load_weights方法加載權重文件

input_shape = (32, 32, 3) depth = 56 model = resnet_v2(input_shape, depth) weights_h5FilePath = '../resources/saved_models/resnet56v2_weights.h5' model.load_weights(weights_h5FilePath) model.compile(loss='categorical_crossentropy',optimizer=Adam(lr=0.001),metrics=['accuracy'])

8. 圖像分類:純終端運行

服務端開啟服務后,flask庫等待接收post請求。
客戶端在cmd中傳入需要檢測圖片的路徑,requests庫發起post請求并打印返回值。
新建:模型部署(在線預測)文件夾,創建以下兩個文件夾,將網盤資源放入resources中。鏈接:https://pan.baidu.com/s/1X0PbGLnjO2vQsQFcwdMgYw 提取碼:z8ax

在resources文件夾中:

在yolov3文件夾中:

8.1 在code文件夾下新建image_server.py寫入下面服務端代碼

# -*- coding: utf-8 -*- # 導入常用的庫 import time import os import cv2 import numpy as np from flask import request, Flask # 導入flask庫的Flask類和request對象 app = Flask(__name__) #實例化Flask對象,這個對象變量是appimport pickle # 訓練時保存的pixel_mean.pickle ,導入pickle加載圖像數據要減去的(像素均值pixel_mean) with open('../resources/pixel_mean.pickle', 'rb') as file: pixel_mean = pickle.load(file) #'rb'是以二進制流讀入文件賦給file變量className_list = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'] id2name_dict = {a:b for a, b in enumerate(className_list)} # 定義字典id2name_dict:把種類索引 轉換為 種類名稱 from keras.models import load_model from keras.optimizers import Adam model_filePath = '../resources/cifar10_ResNet56v2_model.162.h5' # 加載圖像分類模型ResNet56 model = load_model(model_filePath) model.compile(loss='categorical_crossentropy',optimizer=Adam(lr=0.001),metrics=['accuracy']) def get_imageNdarray(imageFilePath):image_ndarray = cv2.imread(imageFilePath) # 根據圖片文件路徑,cv2.imread獲取圖像數據矩陣 resized_image_ndarray = cv2.resize(image_ndarray,(32, 32), #大小統一變為32*32interpolation=cv2.INTER_AREA)return resized_image_ndarraydef process_imageNdarray(image_ndarray, pixel_mean): #模型預測前必要的圖像處理,image_ndarray是32*32*3和pixel_mean大小一樣rgb_image_ndarray = image_ndarray[:, :, ::-1]image_ndarray_1 = rgb_image_ndarray / 255image_ndarray_2 = image_ndarray_1 - pixel_meanreturn image_ndarray_2def predict_image(model, imageFilePath, id2name_dict): #使用模型對指定圖片文件路徑完成圖像分類,返回值為預測的種類名稱 image_ndarray = get_imageNdarray(imageFilePath) #get_imageNdarray方法獲得圖像數據矩陣processed_image_ndarray = process_imageNdarray(image_ndarray, pixel_mean)#process_imageNdarray方法獲得圖像處理后的數據inputs = processed_image_ndarray[np.newaxis, ...] #np.newaxis將32*32*3變為1*32*32*3,因為模型只接受4維矩陣predict_Y = model.predict(inputs)[0] # 4維第一維表示batch,所以可輸入多張圖片predict_y = np.argmax(predict_Y) # Y:對于每個類別的預測概率,y:哪個類別概率最大predict_className = id2name_dict[predict_y] # 種類id轉成種類名稱print('對此圖片路徑 %s 的預測結果為 %s' %(imageFilePath, predict_className))return predict_className@app.route("/", methods=['POST'])# 只有下面函數和服務端相關,其他都是定義一個函數然后返回結果 # 定義回調(callback)函數,接收來自/的post請求,并返回預測結果。回調函數就像是開了一個線程,我先運行主線程,相關的計算問題拋給回調函數,讓它先算,我繼續運行我的程序,等你算出來了發回給我就行,不影響我當前執行 def anyname_you_like(): # 其他地方不會引用該函數startTime = time.time() # 單位是秒received_file = request.files['file'] #往下涉及到了前端:因為request(從flask庫導入)是個對象所以才有.files屬性,里面的'file'對應image_client.py中file_dict(按要求輸入)中'file'imageFileName = received_file.filename #received_file類似于列表if received_file: # 如果不為空received_dirPath = '../resources/received_images' #上一級文件夾if not os.path.isdir(received_dirPath):os.makedirs(received_dirPath) #如果沒有的話就創建它imageFilePath = os.path.join(received_dirPath, imageFileName)#(圖像文件名)放入(圖像文件夾)中形成(圖像文件路徑)received_file.save(imageFilePath)#將(接收到的文件)保存到(文件路徑)print('圖片文件保存到此路徑:%s' % imageFilePath)usedTime = time.time() - startTimeprint('接收圖片并保存,總共耗時%.2f秒' % usedTime)startTime = time.time() #調用predict_image方法,上面定義過predict_className = predict_image(model, imageFilePath, id2name_dict)usedTime = time.time() - startTimeprint('完成對接收圖片的分類預測,總共耗時%.2f秒' % usedTime)return predict_classNameelse:return 'failed'# 主函數 if __name__ == "__main__":print('在開啟服務前,先測試predict_image函數')imageFilePath = '../resources/images/001.jpg'predict_className = predict_image(model, imageFilePath, id2name_dict) #測試函數有沒有寫對,寫錯的話下一步就不進行,model是在外面定義的,在main方法里可以用。app.run("127.0.0.1", port=5000) #第一個參數ip地址,第二個參數是開在哪個端口#如果接收到 127.0.0.1:5000/ 的話調用上面回調函數anyname_you_like()方法,如果/后加其他東西就要寫其他回調函數

8.2 在code文件夾下新建image_client.py寫入下面客戶端代碼

import requests import osif __name__ == "__main__": # 主函數 url = "http://127.0.0.1:5000"while True: #可循環輸入圖片input_content = input('輸入圖片路徑,輸入-1退出,默認值(../resources/images/001.jpg): ') #input()以字符串的方式獲取用戶輸入if input_content.strip() == "": input_content = '../resources/images/001.jpg'if input_content.strip() == "-1":breakelif not os.path.exists(input_content.strip()):print('輸入圖片路徑不正確,請重新輸入')else:imageFilePath = input_content.strip() # 下面是正文:怎么把數據發過去# 下面一行用os.path.split將文件路徑中文件名提取出來,第一段文件夾名,第二段文件名imageFileName = os.path.split(imageFilePath)[1] # 取第二段文件名 file_dict = { # file_dict為一字典,鍵是'file',值是一元組包含三元素 #必須按file_dict方法傳入'file':(imageFileName, # 做post請求時有這三行規范,第一段文件名open(imageFilePath,'rb'), # 第二段二進制流'image/jpg')} # 第三段后綴名類型 result = requests.post(url, files=file_dict) # 這里files參數(默認)對應服務端的filespredict_className = result.text # result是響應response為一對象有.text屬性,result.text為一字符串print('圖片路徑:%s 預測結果為:%s\n' %(imageFilePath, predict_className))

8.3 cmd文件雙擊啟動

在code文件夾下新建名為開啟服務_ResNet56v2圖片分類模型.cmd文件,寫入:

python image_server.py pause

在code文件夾下新建名為客戶端發送圖片數據做分類檢測.cmd文件,寫入:

python image_client.py pause

運行結果:傳入如下圖圖片路徑如:../resources/images/004jpg

9.圖像分類:有web頁面

提交post請求后,不做網頁跳轉,停留在原始網頁。從而使圖像、預測種類名同時顯示在同一網頁,在網頁中可以改變圖片的顯示大小。

9.1 新建_11_web_image_server_2.py文件寫入下面服務端代碼:

# -*- coding: utf-8 -*- # 導入常用的庫 import time import os import cv2 import numpy as np # 導入flask庫 from flask import Flask, render_template, request, jsonify # render_template為返回一個網頁,返回jsonify數據 # Flask, render_template, request, jsonify是四個方法from keras.models import load_model from keras.optimizers import Adam # 導入pickle庫 import pickle# 實例化Flask對象 app = Flask(__name__) # 設置開啟web服務后,如果更新html文件,可以使更新立即生效 app.jinja_env.auto_reload = True app.config['TEMPLATES_AUTO_RELOAD'] = True# 調用pickle庫的load方法,加載圖像數據處理時需要減去的像素均值pixel_mean with open('../resources/pixel_mean.pickle', 'rb') as file:pixel_mean = pickle.load(file) # 加載已經訓練好的cifar10數據集的ResNet56圖片分類模型的模型文件 model_filePath = '../resources/cifar10_ResNet56v2_model.162.h5' model = load_model(model_filePath) model.compile(loss='categorical_crossentropy',optimizer=Adam(lr=0.001),metrics=['accuracy']) # 定義函數classId_to_className,把種類索引轉換為種類名稱 def classId_to_className(classId):category_list = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']className = category_list[classId]return className# 根據圖片文件的路徑獲取圖像矩陣 def get_imageNdarray(imageFilePath):image_ndarray = cv2.imread(imageFilePath)resized_image_ndarray = cv2.resize(image_ndarray,(32, 32),interpolation=cv2.INTER_AREA)return resized_image_ndarray# 模型預測前必要的圖像處理 def process_imageNdarray(image_ndarray, pixel_mean):rgb_image_ndarray = image_ndarray[:, :, ::-1]image_ndarray_1 = rgb_image_ndarray / 255image_ndarray_2 = image_ndarray_1 - pixel_meanreturn image_ndarray_2# 使用模型對指定圖片文件路徑完成圖像分類,返回值為預測的種類名稱 def predict_image(model, imageFilePath):image_ndarray = get_imageNdarray(imageFilePath)processed_image_ndarray = process_imageNdarray(image_ndarray, pixel_mean)inputs = processed_image_ndarray[np.newaxis, ...]predict_Y = model.predict(inputs)[0]predict_y = np.argmax(predict_Y)predict_classId = predict_ypredict_className = classId_to_className(predict_classId)print('對此圖片路徑 %s 的預測結果為 %s' %(imageFilePath, predict_className))return predict_className# 訪問首頁時的調用函數 @app.route('/') def index_page(): # flask庫要求'_05_web_page.html'必須在templates文件夾下 return render_template('_05_web_page.html') # anyname_you_like()是使用predict_image這個API服務時的調用函數 ,POST指向一個url鏈接 # 下行/前:.route是IP地址和端口。/后:predict_image:調用下行anyname_you_like()方法 @app.route("/predict_image", methods=['POST']) def anyname_you_like():startTime = time.time()# 解析接收到的圖片 # post傳送 request請求 ,post過來是個字典file相當于一個鍵received_file = request.files['input_image'] #input_image對應_05_web_page.html中input_imageimageFileName = received_file.filenameif received_file:# 保存接收的圖片到指定文件夾received_dirPath = '../resources/received_images'if not os.path.isdir(received_dirPath):os.makedirs(received_dirPath)imageFilePath = os.path.join(received_dirPath, imageFileName)received_file.save(imageFilePath)print('圖片文件保存到此路徑:%s' % imageFilePath)usedTime = time.time() - startTimeprint('接收圖片并保存,總共耗時%.2f秒' % usedTime)# 對指定圖片路徑的圖片做分類預測,并打印耗時,返回預測種類名稱startTime = time.time()predict_className = predict_image(model, imageFilePath)usedTime = time.time() - startTimeprint('完成對接收圖片的分類預測,總共耗時%.2f秒' % usedTime)return jsonify(predict_className=predict_className) # jsonify是個字典 # 所有post請求返回要么jsonify,要么新的網頁鏈接# 主函數(和之前一樣) if __name__ == "__main__":print('在開啟服務前,先測試predict_image函數')imageFilePath = '../resources/images/001.jpg'predict_className = predict_image(model, imageFilePath)app.run("127.0.0.1", port=5000)

9.2 新建templates文件夾里_12_web_page.html文件

<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"> #meta參數意思,字符編碼設為UTF-8<title>實現需求web</title> #對應頁面標題<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> #從網絡下載并運用jQuery庫<script>$(document).ready(function(){ #$符就是find element$("#image_file").change(function(){ #不管什么標簽,只要id="image_file",就找到下面input這個元素,只要發生改變change,函數function()作為參數傳入change這個方法里var file = $(this)[0].files[0]; #取到文件$(".img").attr("src", URL.createObjectURL(file));}); # #號表示id=,.號表示class=$("button#button_1").click(function(){ #當被點擊促發function()函數var formData = new FormData($("#upload_form")[0]); #找("#upload_form")這個元素,結果是一個list,找到第一個。$("#upload_form")[0]這個作為參數傳給FormData,FormData變為對象再賦值給formData變量,formData變量在下面被調用,不能寫在函數外,唯獨python可以$.ajax({ #字典類型url: "/predict_image", #對應.py文件中"/predict_image"發起POST請求type: 'POST',data: formData, #發送表格數據processData: false,contentType: false,success: function(return_data){ #如果發出請求成功傳回結果$("#image_className").text("圖片中物體的種類名稱:" + return_data["predict_className"]) #找到"#image_className"標簽,改文本內容為后面}, #"predict_className"是個鍵,找到對應值error: function(return_data){ #返回的狀態碼是404alert("上傳失敗!")}}) });}); </script></head><body> #<body>里是網頁顯示的內容<form id="upload_form" enctype="multipart/form-data"> #</form>做表格,方便做post請求<input type="file" name="input_image" id="image_file"/></form> <div> #<div>就是分開一行<img src="" class="img" /></div><button id="button_1">提交信息</button> #設置一個按鈕顯示提交信息<p id="image_className"></p> #p代表文本</body> </html>

9.3 cmd文件雙擊啟動

在code文件夾下新建名為_11_開啟web服務.cmd文件,寫入

python _11_web_image_server_2.py pause

復制下圖綠框到瀏覽器,每上傳一張圖,resources文件夾里多出received_images文件夾,里面存著上傳的圖片。

10.目標檢測:純終端運行

10.1 新建_21_yolov3.py文件

# -*- coding: utf-8 -*- # 導入常用的庫 import os import time import numpy as np import cv2 # 導入keras庫 from keras import backend as K from keras.layers import Input # 導入yolo3文件夾中mode.py、utils.py這2個代碼文件中的方法 from yolo3.model import yolo_eval, yolo_body from yolo3.utils import letterbox_image # 導入PIL畫圖庫 from PIL import Image, ImageFont, ImageDraw# 通過種類的數量,每個種類對應的顏色,顏色變量color為rgb這3個數值組成的元組 import colorsys def get_colorList(category_quantity):hsv_list = []for i in range(category_quantity):hue = i / category_quantitysaturation = 1value = 1hsv = (hue, saturation, value)hsv_list.append(hsv)colorFloat_list = [colorsys.hsv_to_rgb(*k) for k in hsv_list]color_list = [tuple([int(x * 255) for x in k]) for k in colorFloat_list]return color_list # 定義類Detector class Detector(object):defaults = {"weights_h5FilePath": '../resources/yolov3/yolov3_weights.h5',"anchor_txtFilePath": '../resources/yolov3/yolov3_anchors.txt',"category_txtFilePath": '../resources/yolov3/coco.names',"score" : 0.3,"iou" : 0.35,"model_image_size" : (416, 416) #must be a multiple of 32}@classmethoddef get_defaults(cls, n):if n in cls.defaults:return cls.defaults[n]else:return 'Unrecognized attribute name "%s"' %n# 實例化對象后的初始化方法def __init__(self, **kwargs):self.__dict__.update(self.defaults) # 設置默認值self.__dict__.update(kwargs) # 根據傳入的參數更新self.category_list = self.get_categoryList()self.anchor_ndarray = self.get_anchorNdarray()self.session = K.get_session()self.boxes, self.scores, self.classes = self.generate()# 從文本文件中解析出物體種類列表category_list,要求每個種類占一行def get_categoryList(self):with open(self.category_txtFilePath, 'r', encoding='utf8') as file:fileContent = file.read()line_list = [k.strip() for k in fileContent.split('\n') if k.strip()!='']category_list = line_listreturn category_list # 從表示anchor的文本文件中解析出anchor_ndarraydef get_anchorNdarray(self):with open(self.anchor_txtFilePath, 'r', encoding='utf8') as file:number_list = [float(k) for k in file.read().split(',')]anchor_ndarray = np.array(number_list).reshape(-1, 2)return anchor_ndarray# 加載模型,準備好繪圖的顏色,準備模型運算的輸出def generate(self):# 在Keras中,如果模型訓練完成后只保存了權重,那么需要先構建網絡,再加載權重文件num_anchors = len(self.anchor_ndarray)num_classes = len(self.category_list)self.yolo_model = yolo_body(Input(shape=(None, None, 3)),num_anchors//3,num_classes)self.yolo_model.load_weights(self.weights_h5FilePath)# 給不同類別的物體準備不同顏色的方框category_quantity = len(self.category_list)self.color_list = get_colorList(category_quantity)# 目標檢測的輸出:方框box,得分score,類別classself.input_image_size = K.placeholder(shape=(2, ))boxes, scores, classes = yolo_eval(self.yolo_model.output,self.anchor_ndarray,category_quantity,self.input_image_size,score_threshold=self.score,iou_threshold=self.iou)return boxes, scores, classes# 檢測圖片def detect_image(self, image):startTime = time.time()# 模型網絡結構運算所需的數據準備boxed_image = letterbox_image(image, tuple(reversed(self.model_image_size)))image_data = np.array(boxed_image).astype('float') / 255image_data = np.expand_dims(image_data, 0) # Add batch dimension.# 模型網絡結構運算out_boxes, out_scores, out_classes = self.session.run([self.boxes, self.scores, self.classes],feed_dict={self.yolo_model.input: image_data,self.input_image_size: [image.size[1], image.size[0]],})# 調用ImageFont.truetype方法實例化畫圖字體對象font = ImageFont.truetype(font='../resources/yolov3/FiraMono-Medium.otf',size=np.floor(2e-2 * image.size[1] + 0.5).astype('int32'))thickness = (image.size[0] + image.size[1]) // 300# 循環繪制若干個方框for i, c in enumerate(out_classes):# 調用ImageDraw.Draw方法實例化畫圖對象draw = ImageDraw.Draw(image)# 定義方框上方文字內容predicted_class = self.category_list[c]score = out_scores[i]label = '{} {:.2f}'.format(predicted_class, score)label_size = draw.textsize(label, font)box = out_boxes[i]top, left, bottom, right = boxtop = max(0, np.floor(top + 0.5).astype('int32'))left = max(0, np.floor(left + 0.5).astype('int32'))bottom = min(image.size[1], np.floor(bottom + 0.5).astype('int32'))right = min(image.size[0], np.floor(right + 0.5).astype('int32'))# 如果方框在圖片中的位置過于靠上,調整文字區域if top - label_size[1] >= 0:text_origin = np.array([left, top - label_size[1]])else:text_origin = np.array([left, top + 1])# 方框厚度為多少,則畫多少個矩形for j in range(thickness):draw.rectangle([left + j, top + j, right - j, bottom - j],outline=self.color_list[c])# 繪制方框中的文字draw.rectangle([tuple(text_origin), tuple(text_origin + label_size)],fill=self.color_list[c])draw.text(text_origin, label, fill=(0, 0, 0), font=font)del draw# 打印檢測圖片使用的時間usedTime = time.time() - startTimeprint('檢測這張圖片用時%.2f秒' %(usedTime))return image# 使用cv2庫顯示圖片 def cv2_display(image_ndarray):windowName = "object_detection_result"cv2.imshow(windowName, image_ndarray)while True:pressKey = cv2.waitKey(0)# 按Esc鍵或者q鍵可以關閉顯示窗口if 27 == pressKey or ord('q') == pressKey:cv2.destroyAllWindows()break# 主函數 if __name__ == "__main__":detector = Detector(weights_h5FilePath='../resources/yolov3/yolov3_weights.h5',anchor_txtFilePath='../resources/yolov3/yolov3_anchors.txt',category_txtFilePath='../resources/yolov3/coco.names')while True:input_content = input('輸入圖片路徑,輸入-1退出,默認值(../resources/images/person.jpg): ')if input_content.strip() == "":input_content = '../resources/images/person.jpg'if input_content.strip() == "-1":breakelif not os.path.exists(input_content.strip()):print('輸入圖片路徑不正確,請重新輸入')else:imageFilePath = input_content.strip()image = Image.open(imageFilePath)drawed_image = detector.detect_image(image)# 使用cv2庫顯示已經畫框的圖片,cv2庫圖像數據矩陣第3維度的通道順序是bgrdrawed_image_ndarray = np.array(drawed_image)bgr_image_ndarray = drawed_image_ndarray[..., ::-1]cv2_display(bgr_image_ndarray)

10.2 cmd文件雙擊啟動

在code文件夾下新建名為_22_使用YOLOv3檢測單張圖片.cmd文件,寫入

python _21_yolov3.py pause

在cmd中輸入圖片路徑,或者按Enter鍵使用默認值,運行結果如下圖所示。在cv窗口界面可以按Esc鍵或者q鍵退出,在cmd中可以多次輸入路徑檢測多張圖片。

總結

以上是生活随笔為你收集整理的【Python2】Keras_ResNet 在Cifar10数据集上分类,Flask框架部署目标检测模型的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。