在本課程中,您將學習使用預先訓練的模型來檢測給定圖像中的對象。您將使用擠壓網預先訓練的模塊,以極高的精度檢測和分類給定圖像中的對象。
打開一個新的Juypter筆記本,按照步驟開發這個圖像分類應用程式。
Importing Libraries
首先,我們使用以下代碼導入所需的包−
from caffe2.proto import caffe2_pb2 from caffe2.python import core, workspace, models import numpy as np import skimage.io import skimage.transform from matplotlib import pyplot import os import urllib.request as urllib2 import operator
接下來,我們設置幾個變量;
INPUT_IMAGE_SIZE = 227 mean = 128
用於訓練的圖像顯然大小不一。所有這些圖像必須轉換成一個固定的大小,以便進行準確的訓練。同樣,測試圖像和要在生產環境中預測的圖像也必須轉換爲大小,與培訓期間使用的大小相同。因此,我們在上面創建一個名爲INPUT_IMAGE_SIZE的變量,其值爲227。因此,在分類器中使用之前,我們會將所有圖像轉換爲大小227x227。
我們還聲明了一個名爲mean的變量,其值128,稍後將用於改進分類結果。
接下來,我們將開發兩個圖像處理功能。
Image Processing
圖像處理包括兩個步驟。第一個是調整圖像的大小,第二個是集中裁剪圖像。對於這兩個步驟,我們將編寫兩個用於調整大小和裁剪的函數。
Image Resizing
首先,我們將編寫一個調整圖像大小的函數。如前所述,我們將把圖像大小調整爲227x227。因此,讓我們定義函數resize如下−
def resize(img, input_height, input_width):
我們用寬度除以高度得到圖像的長寬比。
original_aspect = img.shape[1]/float(img.shape[0])
如果縱橫比大於1,則表示圖像很寬,即處於橫向模式。現在,我們調整圖像高度並使用以下代碼返回調整大小的圖像−
if(original_aspect>1): new_height = int(original_aspect * input_height) return skimage.transform.resize(img, (input_width, new_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
如果縱橫比小於1,則表示縱向模式。我們現在使用下面的代碼調整寬度;
if(original_aspect<1): new_width = int(input_width/original_aspect) return skimage.transform.resize(img, (new_width, input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
如果縱橫比等於1,則不進行任何高度/寬度調整。
if(original_aspect == 1): return skimage.transform.resize(img, (input_width, input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
下面給出了完整的功能代碼,供您快速參考;
def resize(img, input_height, input_width): original_aspect = img.shape[1]/float(img.shape[0]) if(original_aspect>1): new_height = int(original_aspect * input_height) return skimage.transform.resize(img, (input_width, new_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None) if(original_aspect<1): new_width = int(input_width/original_aspect) return skimage.transform.resize(img, (new_width, input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None) if(original_aspect == 1): return skimage.transform.resize(img, (input_width, input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
現在我們將編寫一個函數來裁剪圖像的中心。
Image Cropping
我們聲明crop_image函數如下−
def crop_image(img,cropx,cropy):
我們使用以下語句提取圖像的維度&負;
y,x,c = img.shape
我們使用以下兩行代碼爲圖像創建一個新的起點−
startx = x//2-(cropx//2) starty = y//2-(cropy//2)
最後,我們通過創建具有新維度的圖像對象來返回裁剪後的圖像;
return img[starty:starty+cropy,startx:startx+cropx]
下面給出了整個函數代碼,供您快速參考;
def crop_image(img,cropx,cropy): y,x,c = img.shape startx = x//2-(cropx//2) starty = y//2-(cropy//2) return img[starty:starty+cropy,startx:startx+cropx]
現在,我們將編寫代碼來測試這些函數。
Processing Image
首先,將圖像文件複製到項目目錄中的images子文件夾中。在項目中複製tree.jpg文件。下面的Python代碼加載圖像並將其顯示在控制台上−
img = skimage.img_as_float(skimage.io.imread("images/tree.jpg")).astype(np.float32) print("Original Image Shape: " , img.shape) pyplot.figure() pyplot.imshow(img) pyplot.title('Original image')
輸出如下所示;
請注意,原始圖像的大小爲600 x 960。我們需要將其調整到我們的規格227 x 227。調用前面定義的resize函數來完成這個任務。
img = resize(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE) print("Image Shape after resizing: " , img.shape) pyplot.figure() pyplot.imshow(img) pyplot.title('Resized image')
輸出如下所示;
請注意,現在圖像大小爲227 x 363。我們需要將其裁剪爲227 x 227以作爲算法的最終提要。爲此,我們調用前面定義的crop函數。
img = crop_image(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE) print("Image Shape after cropping: " , img.shape) pyplot.figure() pyplot.imshow(img) pyplot.title('Center Cropped')
下面提到的是代碼的輸出&;
此時,圖像的大小爲227 x 227,並準備好進行進一步處理。我們現在交換圖像軸,將三種顏色提取到三個不同的區域。
img = img.swapaxes(1, 2).swapaxes(0, 1) print("CHW Image Shape: " , img.shape)
下面給出的是輸出值;
CHW Image Shape: (3, 227, 227)
請注意,最後一個軸現在已成爲數組中的第一個維度。我們現在將使用以下代碼繪製三個通道−
pyplot.figure() for i in range(3): pyplot.subplot(1, 3, i+1) pyplot.imshow(img[i]) pyplot.axis('off') pyplot.title('RGB channel %d' % (i+1))
輸出如下所示;
最後,我們對圖像進行一些額外的處理,例如將紅綠藍轉換爲藍綠紅(RGB到BGR),去除平均值以獲得更好的結果,並使用以下三行代碼&減號添加批處理大小軸;
# convert RGB --> BGR img = img[(2, 1, 0), :, :] # remove mean img = img * 255 - mean # add batch size axis img = img[np.newaxis, :, :, :].astype(np.float32)
此時,您的圖像是NCHW格式的,可以輸入我們的網絡。接下來,我們將加載預先訓練好的模型文件,並將上面的圖像輸入其中進行預測。
Predicting Objects in Processed Image
我們首先爲Caffe的預先訓練模型中定義的init和predict網絡設置路徑。
Setting Model File Paths
請記住,在前面的討論中,所有經過預培訓的模型都安裝在models文件夾中。我們將此文件夾的路徑設置如下−
CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")
我們按照以下方式設置squezenet模型的init戔netprotobuf文件的路徑−
INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb')
同樣,我們設置了到predict_netprotobuf的路徑,如下所示−
PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb')
我們列印這兩條路徑用於診斷目的&負;
print(INIT_NET) print(PREDICT_NET)
上面的代碼和輸出在這裡給出,供您快速參考;
CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models") INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb') PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb') print(INIT_NET) print(PREDICT_NET)
輸出如下所述;
/anaconda3/lib/python3.7/site-packages/caffe2/python/models/squeezenet/init_net.pb /anaconda3/lib/python3.7/site-packages/caffe2/python/models/squeezenet/predict_net.pb
接下來,我們將創建一個預測。
Creating Predictor
我們使用以下兩個語句來讀取模型文件−
with open(INIT_NET, "rb") as f: init_net = f.read() with open(PREDICT_NET, "rb") as f: predict_net = f.read()
通過將指針作爲參數傳遞給predictor函數來創建predictor。
p = workspace.Predictor(init_net, predict_net)
p對象是預測器,用於預測任何給定圖像中的對象。請注意,每個輸入圖像必須採用NCHW格式,就像我們之前對tree.jpg文件所做的那樣。
Predicting Objects
預測給定圖像中的對象很簡單——只需執行一行命令。對於給定圖像中的對象檢測,我們對predictor對象調用run方法。
results = p.run({'data': img})
預測結果現在可以在results對象中使用,我們將其轉換爲數組以提高可讀性。
results = np.asarray(results)
使用以下語句列印數組的維度以供您理解−
print("results shape: ", results.shape)
輸出如下所示;
results shape: (1, 1, 1000, 1, 1)
我們現在將刪除不必要的軸&負;
preds = np.squeeze(results)
現在可以通過獲取preds數組中的max值來檢索最上面的謂詞。
curr_pred, curr_conf = max(enumerate(preds), key=operator.itemgetter(1)) print("Prediction: ", curr_pred) print("Confidence: ", curr_conf)
輸出如下所示;
Prediction: 984 Confidence: 0.89235985
如您所見,該模型預測了一個索引值984且置信度89%的對象。984的指數對我們理解探測到什麼樣的物體沒有多大意義。我們需要使用對象的索引值獲取該對象的字符串化名稱。github存儲庫中提供了模型識別的對象類型及其相應的索引值。
現在,我們將看到如何檢索索引值爲984的對象的名稱。
Stringifying Result
我們創建一個指向github存儲庫的URL對象,如下所示−
codes = "https://gist.githubusercontent.com/aaronmarkham/cd3a6b6ac0 71eca6f7b4a6e40e6038aa/raw/9edb4038a37da6b5a44c3b5bc52e448ff09bfe5b/alexnet_codes"
我們閱讀了URL的內容;
response = urllib2.urlopen(codes)
響應將包含所有代碼及其說明的列表。下面的幾行響應可以幫助您理解它包含的內容;
5: 'electric ray, crampfish, numbfish, torpedo', 6: 'stingray', 7: 'cock', 8: 'hen', 9: 'ostrich, Struthio camelus', 10: 'brambling, Fringilla montifringilla',
現在,我們使用afor循環遍歷整個數組以定位所需的984代碼,如下所示−
for line in response: mystring = line.decode('ascii') code, result = mystring.partition(":")[::2] code = code.strip() result = result.replace("'", "") if (code == str(curr_pred)): name = result.split(",")[0][1:] print("Model predicts", name, "with", curr_conf, "confidence")
當您運行代碼時,您將看到以下輸出;
Model predicts rapeseed with 0.89235985 confidence
現在可以在另一個圖像上嘗試該模型。
Predicting a Different Image
要預測另一個圖像,只需將圖像文件複製到項目目錄的images文件夾中。這是以前的tree.jpg文件存儲的目錄。更改代碼中圖像文件的名稱。只需更改一次,如下所示
img = skimage.img_as_float(skimage.io.imread("images/pretzel.jpg")).astype(np.float32)
原始圖片和預測結果如下所示;
輸出如下所述;
Model predicts pretzel with 0.99999976 confidence
如您所見,預先訓練的模型能夠以非常高的精度檢測給定圖像中的對象。
Full Source
上述代碼的完整原始碼使用預先訓練的模型來檢測給定圖像中的對象,這裡將提到這些代碼,供您快速參考;
def crop_image(img,cropx,cropy): y,x,c = img.shape startx = x//2-(cropx//2) starty = y//2-(cropy//2) return img[starty:starty+cropy,startx:startx+cropx] img = skimage.img_as_float(skimage.io.imread("images/pretzel.jpg")).astype(np.float32) print("Original Image Shape: " , img.shape) pyplot.figure() pyplot.imshow(img) pyplot.title('Original image') img = resize(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE) print("Image Shape after resizing: " , img.shape) pyplot.figure() pyplot.imshow(img) pyplot.title('Resized image') img = crop_image(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE) print("Image Shape after cropping: " , img.shape) pyplot.figure() pyplot.imshow(img) pyplot.title('Center Cropped') img = img.swapaxes(1, 2).swapaxes(0, 1) print("CHW Image Shape: " , img.shape) pyplot.figure() for i in range(3): pyplot.subplot(1, 3, i+1) pyplot.imshow(img[i]) pyplot.axis('off') pyplot.title('RGB channel %d' % (i+1)) # convert RGB --> BGR img = img[(2, 1, 0), :, :] # remove mean img = img * 255 - mean # add batch size axis img = img[np.newaxis, :, :, :].astype(np.float32) CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models") INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb') PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb') print(INIT_NET) print(PREDICT_NET) with open(INIT_NET, "rb") as f: init_net = f.read() with open(PREDICT_NET, "rb") as f: predict_net = f.read() p = workspace.Predictor(init_net, predict_net) results = p.run({'data': img}) results = np.asarray(results) print("results shape: ", results.shape) preds = np.squeeze(results) curr_pred, curr_conf = max(enumerate(preds), key=operator.itemgetter(1)) print("Prediction: ", curr_pred) print("Confidence: ", curr_conf) codes = "https://gist.githubusercontent.com/aaronmarkham/cd3a6b6ac071eca6f7b4a6e40e6038aa/raw/9edb4038a37da6b5a44c3b5bc52e448ff09bfe5b/alexnet_codes" response = urllib2.urlopen(codes) for line in response: mystring = line.decode('ascii') code, result = mystring.partition(":")[::2] code = code.strip() result = result.replace("'", "") if (code == str(curr_pred)): name = result.split(",")[0][1:] print("Model predicts", name, "with", curr_conf, "confidence")
此時,您知道如何使用預先訓練的模型對數據集進行預測。
接下來是學習如何在Caffe2中定義神經網絡(NN)架構,並在數據集上訓練它們。我們現在將學習如何創建一個簡單的單層NN。