OpenCV開發筆記(五十):紅胖子8分鐘帶你深入瞭解輪廓凸包(圖文並茂+淺顯易懂+程序源碼)

若該文爲原創文章,未經允許不得轉載
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105943604
各位讀者,知識無窮而人力有窮,要麼改需求,要麼找專業人士,要麼自己研究

目錄

前言

Demo

輪廓凸包

概述

尋找凸包函數原型

輪廓凸包缺陷

概述

尋找凸包缺陷函數原型

Demo源碼

工程模板:對應版本號v1.45.0

入坑

入坑一:錯誤“Error: Assertion failed (hpoints > 0) in convexityDefects”


紅胖子(紅模仿)的博文大全:開發技術集合(包含Qt實用技術、樹莓派、三維、OpenCV、OpenGL、ffmpeg、OSG、單片機、軟硬結合等等)持續更新中...(點擊傳送門)

OpenCV開發專欄(點擊傳送門)

 

    OpenCV開發筆記(五十):紅胖子8分鐘帶你深入瞭解輪廓凸包(圖文並茂+淺顯易懂+程序源碼)

 

 

前言

      紅胖子來也!!!

      輪過提取完後,可以尋找物體輪廓的凸包,這樣可以把凸包區域提取出來,作爲特徵點提取識別的依據,這是一種場景,還有更多的場景,凸包提取出來後做凸包缺陷分析,那麼就可以識別物體種類等等,不同的物體一般都具有不同的凸包缺陷。

 

Demo

.

 

輪廓凸包

概述

      凸包(convex Hull)是能最小包圍給定二維平面上的點集合的凸多邊形,將二維平面上的點當作釘子,然後使用橡皮筋將所有的釘子包括進去,最後得出的就是凸包。

      如下圖手勢:

尋找凸包函數原型

void convexHull( InputArray points,
                 OutputArray hull,
                 bool clockwise = false,
                 bool returnPoints = true );
  • 參數一:InpuArray類型的points,std::vector<cv::Point>即可,輸入二維點;
  • 參數二:OutputArray類型的hull,外殼輸出凸面外殼。它可以是索引的整數向量,也可以是點的向量。在第一種情況,外殼元素是原始外殼點凸的基於0的索引數組(因爲凸殼點集是原始點集的子集)std::vector<int>。在第二個外殼元素是凸面外殼點本身,std::vector<cv::Point>。
  • 參數三:bool類型的clockwise,默認值爲false,逆時針方向。順時針方向標誌。如果爲真,則輸出凸包將順時針方向。否則,它是逆時針方向的。假定座標系有其X軸指向向右,Y軸向上。
  • 參數四:boo類型的returnPoints,默認值爲true,參數二返回點的集合。返回點操作標誌。對於矩陣,當標誌爲真時,函數返回凸面外殼點。否則,返回凸殼點的索引。當輸出數組是std::vector,標誌被忽略,輸出取決於returnPoints,false返回std::vector<int>,true時返回std::vector<Point>

 

輪廓凸包缺陷

概述

      對於凸包後的物體,有凹陷進去的情況,任何2點外圍邊界凹進去的都認爲是一個凸缺陷。

      凸包缺陷,就是凸包裏面凹陷最深的點到當前缺錢外圍最短的距離(做處置線)

      對之前的輪廓手掌做凸包檢測,如下圖(純目測收工,可能有點不準確,以Demo效果爲準):

當知道識別物體是手掌,通過凸包缺陷,最簡單的,我們通過手掌正面反面的缺陷,可以識別出手掌的手勢了(當然較簡單的)。

尋找凸包缺陷函數原型

void convexityDefects( InputArray contour,
                       InputArray convexhull,
                       OutputArray convexityDefects );
  • 參數一:InpuArray類型的contour,輪廓輸入;
  • 參數二:InputArray類型的hull,外殼輸出凸面外殼;
  • 參數三:OutputArray類型的convexityDefects,凸性缺陷的輸出向量。數組向量:開始索引、結束索引、最遠點索引、固定深度,其中索引是基於0的索引在凸性缺陷的起始、結束和最遠點的原始輪廓中,以及fixpt_depth是最遠的像素點距離。

 

Demo源碼

void OpenCVManager::testConvexHull()
{
    QString fileName1 =
            "E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/22.jpg";
    cv::Mat srcMat = cv::imread(fileName1.toStdString());
    cv::Mat dstMat;
    int width = 300;
    int height = 200;

    cv::resize(srcMat, srcMat, cv::Size(width, height));

    cv::String windowName = _windowTitle.toStdString();
    cvui::init(windowName);

    cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 3,
                                         srcMat.rows * 3),
                                srcMat.type());
    int sigmaS = 100;
    int sigmaR = 1.0;

    int thresh = 215;
    int maxval = 255;

    while(true)
    {
        // 刷新全圖黑色
        windowMat = cv::Scalar(0, 0, 0);

        // 原圖複製
        cv::Mat mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));
        cv::addWeighted(mat, 0.0f, srcMat, 1.0f, 0.0f, mat);

        cv::Mat tempMat;
        {
            {
                cvui::printf(windowMat, 75 + width * 1, 40 + height * 0, "sigmaS");
                cvui::trackbar(windowMat, 75 + width * 1, 50 + height * 0, 165, &sigmaS, 101, 10000);
                cvui::printf(windowMat, 75 + width * 1, 90 + height * 0, "sigmaR");
                cvui::trackbar(windowMat, 75 + width * 1, 100, 165 + height * 0, &sigmaR, 1, 100);

                // 使用自適應流形應用高維濾波。
                cv::Ptr<cv::ximgproc::AdaptiveManifoldFilter> pAdaptiveManifoldFilter
                        = cv::ximgproc::createAMFilter(sigmaS/100.0f, sigmaR/100.0f, true);
                pAdaptiveManifoldFilter->filter(srcMat, tempMat);
                // 效果圖copy
                mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));
                cv::addWeighted(mat, 0.0f, tempMat, 1.0f, 0.0f, mat);
            }

            //  轉爲灰度圖像
            cv::cvtColor(tempMat, tempMat, cv::COLOR_BGR2GRAY);

            {
                // 調整閾值化的參數thresh
                cvui::printf(windowMat, 75 + width * 1, 20 + height * 1, "thresh");
                cvui::trackbar(windowMat, 75 + width * 1, 40 + height * 1, 165, &thresh, 0, 255);
                // 調整閾值化的參數maxval
                cvui::printf(windowMat, 75 + width * 1, 80 + height * 1, "maxval");
                cvui::trackbar(windowMat, 75 + width * 1, 100 + height * 1, 165, &maxval, 0, 255);

                // 閾值化,注意:此處使用了THRESH_BINARY_INV,白色是255,255,255所以反轉閾值化
                cv::threshold(tempMat, tempMat, thresh, maxval, cv::THRESH_BINARY_INV);
                // 效果圖copy
                mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));

                //  轉還圖像
                cv::Mat grayMat;
                cv::cvtColor(tempMat, grayMat, cv::COLOR_GRAY2BGR);
                cv::addWeighted(mat, 0.0f, grayMat, 1.0f, 0.0f, mat);
            }

            // 尋找輪廓
            {
                std::vector<std::vector<cv::Point>> contours;
                std::vector<cv::Vec4i> hierarchy;
                // 查找輪廓:RETR_EXTERNAL-最外層輪廓
                cv::findContours(tempMat, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
                // 遍歷所有頂層輪廓,並繪製出來
                dstMat = srcMat.clone();
                cv::Mat emptyMat = srcMat.clone();
                emptyMat = cv::Scalar(0,0,0);
                // 輪廓contours[i]對應4個hierarchy元素hierarchy[i][0]~ hierarchy[i][3],
                // hierarchy[i][0]表示後一個輪廓的索引編號
                // hierarchy[i][1]前一個輪廓的索引編號
                // hierarchy[i][2]父輪廓的索引編號
                // hierarchy[i][3]內嵌輪廓的索引編號
                for(int index = 0; index >=0; index = hierarchy[index][0])
                {

                    if(hierarchy.size() <= 0)
                    {
                        break;
                    }
                    cv::Scalar color;
                    if(index < hierarchy.size() / 3)
                    {
                        color = cv::Scalar(250 / (hierarchy.size() / 3) * index, 125, 255);
                    }else if(index < hierarchy.size() / 3 * 2)
                    {
                        color = cv::Scalar(255, 250 / (hierarchy.size() / 3) * (index - hierarchy.size() / 3), 125);
                    }else
                    {
                        color = cv::Scalar(125, 255, 250 /
                                           (hierarchy.size() / 3 == 0? 1 : hierarchy.size() / 3) * (index - hierarchy.size() / 3 * 2));
                    }
                    // 繪製輪廓裏面的第幾個
                    cv::drawContours(emptyMat, contours, index, color, CV_FILLED, 8, hierarchy);

                    // 尋找最大凸包
                    std::vector<cv::Point> hullPoints;
                    std::vector<int> hullIndex;
                    cv::convexHull(contours[index], hullPoints, false, true);
                    cv::convexHull(contours[index], hullIndex, false, false);
                    // 繪製點
                    for(int index2 = 1; index2 < hullPoints.size(); index2++)
                    {
                        cv::line(mat, hullPoints.at(index2 - 1), hullPoints.at(index2), cv::Scalar(0, 0, 255));
                        cv::line(dstMat, hullPoints.at(index2 - 1), hullPoints.at(index2), cv::Scalar(0, 0, 255));
                        if(index == hullPoints.size() - 1)
                        {
                            cv::line(dstMat, hullPoints.at(0), hullPoints.at(index2), cv::Scalar(0, 0, 255));
                            cv::line(dstMat, hullPoints.at(0), hullPoints.at(index2), cv::Scalar(0, 0, 255));
                        }
                    }
                    qDebug() << __FILE__ << __LINE__ << "index =" << index << "total =" << hierarchy.size();

                    if(hullPoints.size() > 0)
                    {
                        // 繪製缺陷的點,打印調試出缺陷的距離
                        std::vector<cv::Vec4i> detectVec4i;
                        qDebug() << __FILE__ << __LINE__ << contours.at(index).size() << hullPoints.size() << index;
                        cv::convexityDefects(contours[index], hullIndex, detectVec4i);
                        // 輸出輪廓的缺陷點與值,畫出最遠距離的點
                        for(int index2 = 0; index2 < detectVec4i.size(); index2++)
                        {
                            QString str = QString("detect %1:").arg(index2);
                            str += QString("start point(%1,%2),").arg(contours[index][detectVec4i[index2][0]].x).arg(contours[index][detectVec4i[index2][0]].y);
                            str += QString("end point(%1,%2),").arg(contours[index][detectVec4i[index2][1]].x).arg(contours[index][detectVec4i[index2][1]].y);
                            str += QString("defect point(%1,%2),").arg(contours[index][detectVec4i[index2][2]].x).arg(contours[index][detectVec4i[index2][2]].y);
                            str += QString("distance: %1").arg(detectVec4i[index2][3]);
                            cvui::printf(windowMat, 0, index2 * 16, 0.5, 0xFF0000, str.toUtf8().constData());
                            // 繪製點
                            cv::circle(dstMat, contours[index][detectVec4i[index2][2]], 2.0, cv::Scalar(255, 0, 0), 2);
                        }
                    }
                }
                // 效果圖copy
                mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                                cv::Range(srcMat.cols * 1, srcMat.cols * 2));
                cv::addWeighted(mat, 0.0f, emptyMat, 1.0f, 0.0f, mat);
                // 效果圖copy
                mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                                cv::Range(srcMat.cols * 2, srcMat.cols * 3));
                cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);


            }
        }

        // 更新
        cvui::update();
        // 顯示
        cv::imshow(windowName, windowMat);
        // esc鍵退出
        if(cv::waitKey(25) == 27)
        {
            break;
        }
    }
}

 

工程模板:對應版本號v1.45.0

      對應版本號v1.45.0

 

入坑

入坑一:錯誤“Error: Assertion failed (hpoints > 0) in convexityDefects”

原因:

輸入的hullPoints錯誤;

解決:

      輸入第一個參數,由輸入點std::vector<cv::Point>改爲索引整數,std::vector<int>(這個需要在尋找凸包裏面第四個參數返回點改爲false,即返回索引)。

 

原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105943604

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章