OpenCV DNN之YOLO實時對象檢測

OpenCV DNN之YOLO實時對象檢測

OpenCV在3.3.1的版本中開始正式支持Darknet網絡框架而且支持YOLO1與YOLO2以及YOLO Tiny網絡模型的導入與使用。YOLO是一種比SSD還要快的對象檢測網絡模型,算法做者在其論文中說FPS是Fast R-CNN的100倍,基於COCO數據集跟SSD網絡的各項指標對比
OpenCV DNN之YOLO實時對象檢測html

在最新的OpenCV3.4上我也測試了YOLO3,發現不支持,由於YOLO3有個新層類型shortcut,OpenCV3.4的Darknet暫時還不支持。這裏首先簡單的介紹一下YOLO網絡基本結構,而後在經過代碼演示Darknet支持的YOLO在OpenCV使用。算法

一:YOLO網絡

對象檢測網絡基本上能夠分爲兩種,一種稱爲兩步法、另一種稱爲一步法,很顯然基於圖像分類加上滑動窗口的方式最先的R-CNN就是兩步法的表明之一,兩步法的前面基本上是一個卷積神經網絡,能夠是VGGNet或者Inception之類的,而後再加上一個滑動窗口,可是這種方法太慢,因此就有了區域推薦(RP),預先推薦一些感興趣的區域,進行預言,這些方法廣泛有一個缺點,計算量比較大,致使性能低下沒法實時,而YOLO採樣了一種徹底不一樣的方法,達到對圖像每一個區域只計算一次(You Look at Once - YOLO),YOLO把圖像分爲13x13的Cell(網格):
OpenCV DNN之YOLO實時對象檢測
每一個Cell預測5個BOX,同時YOLO也會生成一個置信分數,告訴每一個BOX包含某個對象的可能性是多少,注意置信分數不會直接說明BOX內是檢測到何種對象,最終那些得分高的BOX被加粗顯示以下:
OpenCV DNN之YOLO實時對象檢測
對於每一個BOX來講,Cell會預測檢測對象類別,這部分的工做就像是一個分類器同樣,基於VOC數據集20中對象檢測,YOLO結合分數與分類信息對每一個BOX給出一個最終可能對象類型的可能性值,以下圖,×××區域85%可能性是狗:
OpenCV DNN之YOLO實時對象檢測網絡

由於總數是13x13的網格,每一個網格預言5個BOX,因此最終有854個BOX,證據代表絕大多數的BOX得分會很低,咱們只要保留30%BOX便可(取決於你本身的閾值設置),最終輸出:
OpenCV DNN之YOLO實時對象檢測框架

從上面能夠看出整個圖像只是被計算了一次,真正作到了下降計算量,提升了檢測實時性。上述檢測使用的YOLO的網絡結構以下:
OpenCV DNN之YOLO實時對象檢測ide

發現只有CNN層,沒有FC層,是否是簡單到爆,最後說一下爲何最後一層卷積層深度是125,
由於每一個Cell檢測5個BOX,對每一個BOX來講,包含以下數據性能

  • BOX自己信息,x、y、w、h
  • 置信分數
  • 基於VOC數據集的20個對象類別

因此對每一個BOX來講有25個參數,5個BOX= 5x25=125個參數。
上面是獲得的網絡模型就是tiny-YOLO網絡模型,能夠在移動端實時對象檢測。這個跟做者在論文中提到的稍微有點差別,論文中做者是輸入圖像爲448x448,分爲7x7的網格(Cell),結構以下:
OpenCV DNN之YOLO實時對象檢測學習

最終輸出是每一個Cell預測兩個BOX,作20個分類,它獲得最終是測試

  • BOX自己信息,x、y、w、h
  • 置信分數

深度 = SS(B5+20), 其中20個表示分類數目,S表示網絡分割,B表示BOX個數。S=七、B=2,最終輸出是77*303d

二:在OpenCV中使用YOLO

OpenCV在3.3.1版本中開始支持Darknet,可能有人會問,Darknet是什麼鬼,它是YOLO的做者本身搞出來的深度學習框架,支持C/C++/Python語言,支持YOLOv一、YOLOv二、YOLOv3等網絡模型訓練與使用。可是在OpenCV只是前饋網絡,只支持預測,不能訓練。OpenCV中基於YOLO模型我使用的是tiny-YOLO網絡模型,支持20中對象檢測。代碼實現步驟以下:
1. 加載網絡模型code

String modelConfiguration = "D:/vcprojects/images/dnn/yolov2-tiny-voc/yolov2-tiny-voc.cfg";
String modelBinary = "D:/vcprojects/images/dnn/yolov2-tiny-voc/yolov2-tiny-voc.weights";
dnn::Net net = readNetFromDarknet(modelConfiguration, modelBinary);
if (net.empty())
{
    printf("Could not load net...\n");
    return;
}

2. 加載分類信息

vector<string> classNamesVec;
ifstream classNamesFile("D:/vcprojects/images/dnn/yolov2-tiny-voc/voc.names");
if (classNamesFile.is_open())
{
    string className = "";
    while (std::getline(classNamesFile, className))
        classNamesVec.push_back(className);
}

3. 加載測試圖像

// 加載圖像
Mat frame = imread("D:/vcprojects/images/fastrcnn.jpg");
Mat inputBlob = blobFromImage(frame, 1 / 255.F, Size(416, 416), Scalar(), true, false);
net.setInput(inputBlob, "data");

4. 檢測與顯示

// 檢測
Mat detectionMat = net.forward("detection_out");
vector<double> layersTimings;
double freq = getTickFrequency() / 1000;
double time = net.getPerfProfile(layersTimings) / freq;
ostringstream ss;
ss << "detection time: " << time << " ms";
putText(frame, ss.str(), Point(20, 20), 0, 0.5, Scalar(0, 0, 255));

// 輸出結果
for (int i = 0; i < detectionMat.rows; i++)
{
    const int probability_index = 5;
    const int probability_size = detectionMat.cols - probability_index;
    float *prob_array_ptr = &detectionMat.at<float>(i, probability_index);
    size_t objectClass = max_element(prob_array_ptr, prob_array_ptr + probability_size) - prob_array_ptr;
    float confidence = detectionMat.at<float>(i, (int)objectClass + probability_index);
    if (confidence > confidenceThreshold)
    {
        float x = detectionMat.at<float>(i, 0);
        float y = detectionMat.at<float>(i, 1);
        float width = detectionMat.at<float>(i, 2);
        float height = detectionMat.at<float>(i, 3);
        int xLeftBottom = static_cast<int>((x - width / 2) * frame.cols);
        int yLeftBottom = static_cast<int>((y - height / 2) * frame.rows);
        int xRightTop = static_cast<int>((x + width / 2) * frame.cols);
        int yRightTop = static_cast<int>((y + height / 2) * frame.rows);
        Rect object(xLeftBottom, yLeftBottom,
            xRightTop - xLeftBottom,
            yRightTop - yLeftBottom);
        rectangle(frame, object, Scalar(0, 0, 255), 2, 8);
        if (objectClass < classNamesVec.size())
        {
            ss.str("");
            ss << confidence;
            String conf(ss.str());
            String label = String(classNamesVec[objectClass]) + ": " + conf;
            int baseLine = 0;
            Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
            rectangle(frame, Rect(Point(xLeftBottom, yLeftBottom),
                Size(labelSize.width, labelSize.height + baseLine)),
                Scalar(255, 255, 255), CV_FILLED);
            putText(frame, label, Point(xLeftBottom, yLeftBottom + labelSize.height),
                FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0));
        }
    }
}
imshow("YOLO-Detections", frame);

5. 運行效果
OpenCV DNN之YOLO實時對象檢測

個人課程:
學習OpenCV3.3深度神經網絡(DNN)模塊-應用視頻教程

相關文章
相關標籤/搜索