openCV 簡單實現身高測量(未考慮相機標定,windows)


(一) OpenCV3.1.0+VS2015開發環境配置

  • 下載OpenCV安裝包(筆者下載3.1.0版本)
  • 環境變量配置(opencv安裝路徑\build\x64\vc14\bin,注意的是x64文件夾下分為vc12和vc14兩個文件夾,他們對應於VS的版本,vc8 = Visual Studio 2005,vc9 = Visual Studio 2008,vc10 = Visual Studio 2010,vc11 = Visual Studio 2012,vc12 = Visual Studio 2013,vc14 = Visual Studio 2015)
  • VS2015配置。進入屬性管理器(View—>Other Windows—>Property Manger,展開目錄,選中Debug|Win64中的Microsoft.Cpp.x64.user,並右鍵點擊屬性(Properties)進入屬性界面。)
    1.  包含目錄配置(通用屬性(Common Properties)—>VC ++目錄—>包含目錄(Include Directories))。添加路徑:D:\Code\C\opencv\build\include;D:\Code\C\opencv\build\include\opencv;D:\Code\C\opencv\build\include\opencv2
    2. 配置庫文件目錄(Library Directories)。D:\Code\C\opencv\build\x64\vc14\lib(注意VS版本)
    3. 配置動態鏈接庫(Linker(鏈接庫)—>Input(輸入)—>Additional Dependencies(添加依賴))。D:\Code\C\opencv\build\x64\vc12\bin路徑下的。opencv_world310.lib和opencv_world310d.lib,這里兩個庫文件的區別就是:opencv_world310.lib是Release模式版本,而opencv_world310d.lib是Debug模式版本。

(二)算法思路

  • HOG特征提取獲得定位矩形框,筆者使用SVM分類優化+多尺度檢測獲得帶有矩形框的dst
/******************************HOG detector************************************************/
    dst = src.clone();
    vector<Rect> findrects, findrect;
    HOGDescriptor HOG;
    //SVM分類器
    HOG.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
    //多尺度檢測
    HOG.detectMultiScale(src, findrects, 0, Size(4, 4), Size(0, 0), 1.05, 2);
    //若rects有嵌套,則取最外面的矩形存入rect
    for (int i = 0; i < findrects.size(); i++)
    {
        Rect rect = findrects[i];
        int j = 0;
        for (; j < findrects.size(); j++)
            if (j != i && (rect & findrects[j]) == rect)
                break;
        if (j == findrects.size())
            findrect.push_back(rect);
    }
    Rect r;//用來選中所測圖像中的測量對象。
    r.height = -1;
    //框選出檢測結果並選中圖片測量對象
    for (int i = 0; i < findrect.size(); i++)
        r = r.height > findrect[i].height ? r : findrect[i];
  //HOG detector返回的矩形框比真正圖像輪廓大,所以我們減少矩形框的大小來使得更符合外界邊框
    r.x += cvRound(r.width*0.1);
    r.width = cvRound(r.width*0.8);
    r.y += cvRound(r.height*0.07);
    r.height = cvRound(r.height*0.8);
    Scalar color = Scalar(0,0,255);
    rectangle(dst, r.tl(), r.br(), color, 2);
//    imshow("src", src);
//    imshow("dst", dst);

原圖像和得到的dst圖像

  • Grabcut算法分割
/********************************grabCut**********************************************/
    cv::Mat mask = Mat::zeros(src.size(), CV_8UC1);//分割后的結果
    //兩個臨時矩陣變量,作為算法的中間變量使用
    cv::Mat bgModel, fgModel;
    cv::Rect rectangle(r.tl(),r.br());//圖像的前景對象也就是矩形選中圖像
    // GrabCut 分段
    cv::grabCut(src,    //輸入圖像
        mask,   //分段結果
        rectangle,// 包含前景的矩形 
        bgModel, fgModel, // 前景、背景
        1,        // 迭代次數
        cv::GC_INIT_WITH_RECT); // 用矩形
    
    // 得到可能是前景的像素
    //比較函數保留值為GC_PR_FGD的像素
    cv::compare(mask, cv::GC_PR_FGD, mask, cv::CMP_EQ);
    // 產生輸出圖像
    cv::Mat foreground(src.size(), CV_8UC3, cv::Scalar(0, 0, 0));
    //背景值為 GC_BGD=0,作為掩碼
    src.copyTo(foreground, mask);
    imshow("foreground", foreground);

處理后圖像

 

 

 

  • BorderMatting邊緣細化處理(由於版權問題,沒有相應接口需自己編寫)主要思想:把前景顏色值與估計值對比,選擇較小的差異值顏色代替,使得平滑最好
/********************************BorderMatting**********************************************/
    BorderMatting bm;
    Mat rst = Mat(src.size(), src.type());
    Mat rstBm;
    src.copyTo(rst);
    for (int i = 0; i<rst.rows; i++)
        for (int j = 0; j < rst.cols; j++)
        {
            if (mask.at<uchar>(i, j) == 0)
                rst.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
        }
    bm.Initialize(src, mask);
    rstBm=bm.Run();
    imshow("bordingmatting", rstBm);

筆者自己創建的BorderMatting類。

結果圖:

 

  •  灰度化、二值化便於計算
/******************************灰度化、二值化************************************************/

    Mat  rstGray, rstBin;
    cvtColor(rstBm, rstGray, CV_BGR2GRAY);//灰度化
//    imshow("灰度圖", rstGray);
    threshold(rstGray, rstBin, 100, 255, CV_THRESH_BINARY);//二值化
    imshow("二值圖", rstBin);

 

  •  最長像素距離計算。筆者一開始用歐氏距離遍歷計算得到,結果處理時間過長,就采用最簡單的簡化方法,最長像素點距離≈最高像素點與最低像素點歐氏距離。

 

/**********************************計算最長像素距離********************************************/
    double MAXPX = 0;
    int x1 = 0, y1 = 0;
    int x2 = 0, y2 = 0;
    int maxX1, maxX2, maxY1, maxY2;
    int i, j;
    int flag = 0;
    for (i = 0; i < rstBin.rows; i++)
    {
        for (j = 0; j < rstBin.cols; j++)
            if (rstBin.at<uchar>(i, j) == 255)
            {
                x1 = i;
                y1 = j;
                flag = 1;
                break;
            }
        if (flag)
            break;
    }
    flag = 0;
    for (i = rstBin.rows; i >= 0; i--)
    {
        for (j = 0; j < rstBin.cols; j++)
            if (rstBin.at<uchar>(i, j) == 255)
            {
                x2 = i;
                y2 = j;
                flag = 1;
                break;
            }
        if (flag)
            break;
    }
    cout << x1 << " " << y1 << endl;
    cout << x2 << " " << y2 << endl;
    line(src, Point(y1, x1), Point(y2, x2), Scalar(0, 0, 255), 3);
/*    for ( i = 1; i <= rstBin.rows*rstBin.cols; i++)//第一個像素點開始遍歷
    {
        x1 = i / rstBin.rows;
        y1 = !x1 ? (i - 1) : (i % x1 - 1);
        if (rstBin.at<uchar>(x1, y1) == 0)
            continue;
        for ( j = i + 1; j <= rstBin.rows*rstBin.cols; j++)
        {
            x2 = j / rstBin.rows;
            y2 = !x2 ? (j - 1) : (j % x2 - 1);
            if (rstBin.at<uchar>(x2, y2) == 255)//同為白色像素點時計算距離
            {
                double distance=abs(x2 - x1) +abs(y2 - y1);//避免歐氏距離計算浪費時間
                if (MAXPX < distance)
                {    
                    maxX1 = x1;
                    maxX2 = x2;
                    maxY1 = y1;
                    maxY2 = y2;
                    MAXPX = distance;
                }
            }
        }
    }
    */
    /*MAXPX = sqrt((maxX1 - maxX2)*(maxX1 - maxX2) + (maxY1 - maxY2)*(maxY1 - maxY2));
    cout << MAXPX << endl;
    cout << maxX1 << " " << maxY1 << endl;
    cout << maxX2 << " " << maxY2 << endl;
    line(src, Point(maxY1, maxX1), Point(maxY2, maxX2), Scalar(0, 0, 255), 3);
    */imshow("final", src);

注意像素點坐標順序。結果如下

 

 

像素與毫米的轉換 轉換還需要知道另一個參數:PPI(每英寸多少像素) 象素數 / PPI = 英寸數 英寸數 * 25.4 = 毫米數

筆者 設定系統參數焦距(3.5mm)不會變動(圖為35mm焦距所拍),物距1.1m,ppi:400(筆者電腦PPI260)

那么身高大概為:({[((709-152)^2+(281-247)^2)^0.5]/260}*25.4/35)*1.1≈1.71m

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM