本次筆記要整理的內容是:通過OpenCV的dnn模塊來調用OpenCV自帶的inception5h模型,並實現物體識別。該模型可識別的類別總共有1000類,包含動物、日常用品、交通工具等等,範圍非常廣泛。下面通過代碼逐步整理。
首先,我們需要加載模型,並設置計算後臺和目標設備。
//加載opencv自帶的tenserflow模型實現圖像分類
const string tf_net_model_path = "D:\\opencv_c++\\opencv_tutorial\\data\\models\\inception5h\\tensorflow_inception_graph.pb";
const string labels_txt_path = "D:\\opencv_c++\\opencv_tutorial\\data\\models\\inception5h\\imagenet_comp_graph_label_strings.txt";
//加載tf模型
dnn::Net tf_net = dnn::readNetFromTensorflow(tf_net_model_path);
tf_net.setPreferableBackend(DNN_BACKEND_OPENCV); //設置計算後臺,DNN_BACKEND_INFERENCE_ENGINE表示使用openVINO進行加速計算,但是一些模型上使用會報錯;DNN_BACKEND_OPENCV表示使用opencv自帶的dnn後臺進行計算
tf_net.setPreferableTarget(DNN_TARGET_CPU); //設置計算目標設備
if (tf_net.empty())
{
cout << "read model file fail" << endl;
}
然後從文件中加載、並組織成可以索引類別的標籤集。
//讀取分類標籤集
ifstream fp(labels_txt_path);
vector<string>class_labels;
if (!fp.is_open())
{
cout << "labels file can't open" << endl;
exit(-1);
}
while (!fp.eof()) //如果指針不位於文件末尾,就繼續讀取文件
{
string class_name;
getline(fp, class_name); //讀取文件中的每一行數據
if (0 != class_name.length()) //如果不是空行,就將這一行的數據保存起來
{
class_labels.push_back(class_name);
}
}
然後讀取測試圖像。
//讀取測試圖像
Mat test_image = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\pig.jpg");
resize(test_image, test_image, Size(700, 700));
imshow("test_image", test_image);
接着是非常重要的一步,就是將輸入神經網絡的圖像進行預處理,將圖像轉化爲一個4維的blob。
Mat inputBlob = dnn::blobFromImage(test_image, 1.0, Size(224, 224), Scalar(117,117,117), true, false, 5);
APIblobFromImage()
的參數含義如下:
(1)參數image:要輸入神經網絡進行處理或者分類的圖片;
(2)參數scalefactor:將圖像減去平均值之後,對每個像素點的像素值進行一定的尺度縮放,它的默認值是1;例如當scalefactor=1/2,則對圖像進行減均值操作後,再對每個像素點的像素值乘1/2;
(3)參數size:神經網絡(模型)在訓練的時候要求輸入的圖片尺寸,由所使用的模型來決定;
(4)參數mean:對圖像減去的平均值,如果我們需要對RGB圖片的三個通道分別減去不同的值,那麼可以使用3組平均值,如果只使用一組,那麼就默認對三個通道減去一樣的值。
對圖片的R、G、B三通道求取平均像素值,然後將每個像素點減去平均值,就可以得到像素點之間的相對值,可以消除同一場景下不同光照情況的圖片對最終分類的影響。該值由訓練模型時所使用的數據集決定。
(5)參數swapRB:OpenCV中圖像的通道順序是BGR,但是有些神經網絡的輸入圖像其通道順序是RGB;當測試圖像和神經網絡要求的輸入圖像的通道順序不同時,則將swapRB=true,即交換輸入圖像的R、B通道;該值由所使用的神經網絡的輸入圖像要求所決定;
(6)參數crop:表示是否對要輸入神經網絡的測試圖像進行裁剪,一般默認爲false;
(7)參數depth:返回的blob的深度。
接着就可以將處理好的blob傳入神經網絡中進行前向傳播了,並得到預測結果。
Mat prob;
//設置神經網絡的輸入,可以指定爲某一層的輸入,默認第一層神經層爲輸入層
tf_net.setInput(inputBlob);
//對神經網絡進行前向傳播,得到一個預測結果;具體結構由所使用的神經網絡輸出層決定
prob = tf_net.forward();
得到預測結果後,需要將結果做一定的處理,也就是把結果矩陣轉換爲1行多列的矩陣,其中每一列就是一個類別的置信度。我們獲取其中數值最大的一列,那麼該列的x座標在標籤集中對應的類別就是輸出的預測類別。
//將預測結果轉化爲一行多列的單通道Mat對象,每一列爲一個標籤的置信度
Mat prob_mat = prob.reshape(1, 1);
//尋找預測結果中置信度最大的某個分類,即爲輸入測試圖像的預測分類
double maxval;
Point maxloc;
minMaxLoc(prob_mat, NULL, &maxval, NULL, &maxloc);
int classID = maxloc.x;
maxval = maxval * 100;
最後對預測結果進行可視化輸出。
//輸出預測結果
string confidence = to_string(maxval);
string img_class = class_labels[classID];
putText(test_image, img_class+confidence+"%", Point(50, 50), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2, LINE_AA);
imshow("retult", test_image);
cout <<"運行時間: "<< run_time << "s" << endl;
運行結果如下:
到這裏,我們就實現了在OpenCV中dnn模塊來調用inception5h模型來進行圖像分類的功能,那麼本次筆記到此結束啦。
PS:本人的註釋比較雜,既有自己的心得體會也有網上查閱資料時摘抄下的知識內容,所以如有雷同,純屬我向前輩學習的致敬,如果有前輩覺得我的筆記內容侵犯了您的知識產權,請和我聯繫,我會將涉及到的博文內容刪除,謝謝!