網路上有另一種辨識17種花卉的討論,請先安裝如下套件
pip install tensorflow==2.10.1 matplotlib opencv-python
收集圖片
請由如下網址下載 flows_17.zip,然後解開後置於專案之下。
下載 : flowers_17.zip
label.txt
flowers圖片共有17種,每種 80 張圖片,所以共有1360張圖片,1~80是水仙(Narcissus),81~160是雪花蓮(Snowdrop),請先於專案下新增 label.txt,然後輸入如下資料。
1 80 Narcissus 81 160 Snowdrop 161 240 LilyValley 241 320 Bluebell 321 400 Crocus 401 480 Iris 481 560 Tigerlily 561 640 Daffodil 641 720 Fritillary 721 800 Sunflower 801 880 Daisy 881 960 ColtsFoot 961 1040 Dandelion 1041 1120 Cowslip 1121 1200 Buttercup 1201 1280 Windflower 1281 1360 Pansy
分類訓練圖片及驗証圖片
將所有的圖片分類成 train_images及 test_images二個目錄,每個目錄又有17種花卉目錄。
請新增 “分割資料.py” 檔,程式碼如下。
import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' import random import shutil import numpy as np flowers=[] #flowers_array=np.empty(0, dtype=object) flowers_array=[] with open('label.txt')as file: for line in file: line=line.strip() cols=line.split() s=int(cols[0]) e=int(cols[1]) flower=cols[2] flowers.append(cols[2]) #flowers_array = np.r_[flowers_array, [flower] * (e-s+1)] flowers_array += [flower] * (e-s+1) flowers_array=np.array(flowers_array) print(flowers_array) in_path="flowers_17" train_path='train_images' test_path='test_images' if os.path.exists(train_path): shutil.rmtree(train_path) os.mkdir(train_path) if os.path.exists(test_path): shutil.rmtree(test_path) os.mkdir(test_path) for flower in flowers: os.mkdir(os.path.join(train_path, flower)) os.mkdir(os.path.join(test_path, flower)) datas=list(zip(os.listdir(in_path),flowers_array)) random.seed(1) random.shuffle(datas) print(datas) train=int(len(datas)*0.9) for file, dir in datas[:train]: source = os.path.join(in_path, file) dest = os.path.join(train_path, dir, file) print(f'copy {source} => {dest}') shutil.copy(source, dest) for file, dir in datas[train:]: source = os.path.join(in_path, file) dest = os.path.join(test_path, dir, file) print(f'copy {source} => {dest}') shutil.copy(source, dest)
list 相加與 np.r_ 效能
上述代碼中,原本是使用 np.r_ 建立陣列,但這種方式的效能極差,所以改用 list += 來加速。由如下程式碼可知,list 相加的效能確實高出許多
import numpy as np import time a=[] t1=time.time() for i in range(1000): a+=["hello"]*1000 a=np.array(a) t2=time.time() print(a) print(f'list相加 : {t2-t1}秒') a=np.empty(0,dtype=object) t1=time.time() for i in range(1000): a=np.r_[a, ["hello"]*1000] t2=time.time() print(a) print(f'np.r_ : {t2-t1}秒') 結果: ['hello' 'hello' 'hello' ... 'hello' 'hello' 'hello'] list相加 : 0.10912036895751953秒 ['hello' 'hello' 'hello' ... 'hello' 'hello' 'hello'] np.r_ : 4.773515701293945秒
訓練模型
建立模型跟上述的5種花卉雷同。
import os import shutil import cv2 from keras import Sequential from keras.applications import VGG19 from keras.layers import GlobalAveragePooling2D, Dense, BatchNormalization, Dropout from keras.optimizers import Adam import pylab as plt os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' import numpy as np flowers=[] with open('label.txt','r')as file: for line in file: line=line.strip() cols=line.split() flowers.append(cols[2]) train_imgs=[] train_labels=[] test_imgs=[] test_labels=[] train_path="train_images" test_path="test_images" #製作訓練資料 for flower in flowers: for file in os.listdir(os.path.join(train_path, flower)): full=os.path.join(train_path, flower, file) img=cv2.imdecode( np.fromfile(full, dtype=np.uint8), cv2.IMREAD_COLOR )[:,:,::-1].copy() img=cv2.resize(img, (224,224), interpolation=cv2.INTER_LINEAR) train_imgs.append(img) train_labels.append(flower) train_imgs=np.array(train_imgs) #製作測試資料 for flower in flowers: for file in os.listdir(os.path.join(test_path, flower)): img=cv2.imdecode(np.fromfile(os.path.join(test_path, flower, file), dtype=np.uint8), cv2.IMREAD_UNCHANGED) img=cv2.resize(img, (224,224), interpolation=cv2.INTER_LINEAR) test_imgs.append(img) test_labels.append(flower) test_imgs=np.array(test_imgs) #one hot train_onehot=np.zeros([len(train_labels),17], dtype=np.int32) test_onehot=np.zeros([len(test_labels),17], dtype=np.int32) for i in range(len(train_onehot)): train_onehot[i][flowers.index(train_labels[i])]=1 print(train_onehot[i]) for i in range(len(test_onehot)): test_onehot[i][flowers.index(test_labels[i])]=1 print(test_labels[i],test_onehot[i]) #建模 model_base=VGG19(weights='imagenet', include_top=False, input_shape=(224,224,3)) for layer in model_base.layers: layer.trainable=False model=Sequential() model.add(model_base) model.add(GlobalAveragePooling2D()) model.add(Dense(256,activation='relu')) model.add(BatchNormalization()) model.add(Dense(64,activation='relu')) model.add(BatchNormalization()) model.add(Dropout(0.2)) model.add(Dense(17, activation='softmax')) model.compile(optimizer=Adam(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy'] ) #訓練 history=model.fit( train_imgs, train_onehot, batch_size=64, epochs=50, validation_data=(test_imgs, test_onehot) ) if os.path.exists('./flower_17_model'): shutil.rmtree('./flower_17_model') model.save("flower_17_model") p1=plt.plot(history.history['accuracy'], label='training acc') p2=plt.plot(history.history['val_accuracy'], label='val acc') p3=plt.plot(history.history['loss'], label='training loss') p4=plt.plot(history.history['val_loss'], label='val loss') plt.legend() plt.show()
辨識圖片
上述模型若是無法訓練的話,請由如下網址下載本人已訓練好的模型
下載模型 : flower_17_model
將要辨識的圖片放在 ./images裏面,然後開始辨識。
import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' import keras import cv2 import numpy as np import pylab as plt from keras.applications.vgg19 import preprocess_input model=keras.models.load_model('flower_17_model') flowers=[] with open('label.txt')as file: for line in file: line=line.strip() cols=line.split() flowers.append(cols[2]) print(flowers) img_path='./images' plt.figure(figsize=(12,6)) for i, file in enumerate(os.listdir(img_path)): img=cv2.imdecode( np.fromfile(os.path.join(img_path, file), dtype=np.uint8), cv2.IMREAD_UNCHANGED ) x=cv2.resize(img, (224, 224), interpolation=cv2.INTER_LINEAR) x=cv2.cvtColor(x, cv2.COLOR_BGR2RGB) #VGG19規定在偵測(predict)時,必需傳入 #(1,224,224,3)的資料格式 x=np.expand_dims(x, axis=0) #預處理圖片及偵測圖片 x=preprocess_input(x) out=model.predict(x) print(out) idx=out[0].argmax() score=out[0][idx] name=flowers[idx] print(name) txt=f'{name}\n{score*100:.2f}%' plt.subplot(2,5,i+1) plt.title(txt) plt.axis("off") plt.imshow(img[:,:,::-1].copy()) plt.show()