OpenCV開發筆記(五十九):紅胖子8分鐘帶你深刻了解分水嶺算法(圖文並茂+淺顯易懂+程序源碼)

若該文爲原創文章,未經容許不得轉載
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:http://www.javashuo.com/article/p-wxwjppoc-mo.html
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/106258388
各位讀者,知識無窮而人力有窮,要麼改需求,要麼找專業人士,要麼本身研究
紅胖子(紅模仿)的博文大全:開發技術集合(包含Qt實用技術、樹莓派、三維、OpenCV、OpenGL、ffmpeg、OSG、單片機、軟硬結合等等)持續更新中…(點擊傳送門)算法

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

上一篇:《OpenCV開發筆記(五十八):紅胖子8分鐘帶你深刻了解圖像的矩(圖文並茂+淺顯易懂+程序源碼)
下一篇:持續補充中… 函數


前言

  紅胖子,來也!
  作識別,有時候需求要識別物體,物體在背景上比較雜,可是其邊緣與背景圖相差大,這個時候可使用分水嶺算法突出兩邊的顏色對比度,從而更好的分割。 ui


Demo

  在這裏插入圖片描述
  在這裏插入圖片描述
  在這裏插入圖片描述
  在這裏插入圖片描述
  在這裏插入圖片描述
  


分水嶺算法

概述

  分水嶺分割方法,是一種基於拓撲理論的數學形態學的分割方法,其基本思想是把圖像看做是測地學上的拓撲地貌,圖像中每一點像素的灰度值表示該點的海拔高度,每個局部極小值及其影響區域稱爲集水盆,而集水盆的邊界則造成分水嶺。簡單來講就是根據圖像相鄰的像素差值,分紅不一樣區域,將各區域染成不一樣顏色,其適合使用者已經能夠標記已知對象或背景中的一部分。
 &emp;分水嶺的概念和造成能夠經過模擬浸入過程來講明。在每個局部極小值表面,刺穿一個小孔,而後把整個模型慢慢浸入水中,隨着浸入的加深,每個局部極小值的影響域慢慢向外擴展,在兩個集水盆匯合處構築大壩,即造成分水嶺。
  分水嶺的計算過程是一個迭代標註過程。spa

原理

  分水嶺比較經典的計算方法是L. Vincent提出的。在該算法中,分水嶺計算分兩個步驟,一個是排序過程,一個是淹沒過程。首先對每一個像素的灰度級進行從低到高排序,而後在從低到高實現淹沒過程當中,對每個局部極小值在h階高度的影響域採用先進先出(FIFO)結構進行判斷及標註。
  分水嶺變換獲得的是輸入圖像的集水盆圖像,集水盆之間的邊界點,即爲分水嶺。顯然,分水嶺表示的是輸入圖像極大值點。所以,爲獲得圖像的邊緣信息,一般把梯度圖像做爲輸入圖像,即
g(x,y)=grad(f(x,y))={[f(x,y)-f(x-1,y)]2[f(x,y)-f(x,y-1)]2}0.5
式中,f(x,y)表示原始圖像,grad{.}表示梯度運算。
  分水嶺算法對微弱邊緣具備良好的響應,圖像中的噪聲、物體表面細微的灰度變化,都會產生過分分割的現象。但同時,分水嶺算法對微弱邊緣具備良好的響應,是獲得封閉連續邊緣的保證的。.net

分水嶺函數原型

void watershed(InputArray image, InputOutputArray markers )
  • 參數一:lnputArray類型的src,輸入圖像,即源圖像,填Mat類的對象便可,且需爲8位三通道的彩色圖像。
  • 參數二:InputOutputArray類型的markers,在執行分水嶺函數watershed以前,必須對該參數進行處理,它應該包含不一樣區域的輪廓,每一個輪廓有一個本身惟一的編號,輪廓的定位能夠經過Opencv中findContours方法實現,這個是執行分水嶺以前的要求。


Demo涉及到的相關知識

  均值濾波:《OpenCV開發筆記(十五):算法基礎之線性濾波-均值濾波
  canny邊緣檢測:《OpenCV開發筆記(三十七):紅胖子8分鐘帶你深刻了解邊緣檢測和Canny算子邊緣檢測(圖文並茂+淺顯易懂+程序源碼)
  查找與繪製輪廓:《OpenCV開發筆記(四十九):紅胖子8分鐘帶你深刻了解輪廓識別(圖文並茂+淺顯易懂+程序源碼)》 code


Demo源碼

void OpenCVManager::testWatersheed()
{
    QString fileName1 =
            "E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/5.jpg";
    int width = 400;
    int height = 300;

    cv::Mat srcMat = cv::imread(fileName1.toStdString());
    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 * 2, srcMat.rows * 3),
                                srcMat.type());

    int threshold1 = 200;
    int threshold2 = 100;
    while(true)
    {
        windowMat = cv::Scalar(0, 0, 0);

        cv::Mat mat;

        cv::Mat tempMat;
        // 原圖先copy到左邊
        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 grayMat;
            cv::cvtColor(srcMat, grayMat, cv::COLOR_BGR2GRAY);
            // copy
            mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
                            cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::Mat grayMat2;
            cv::cvtColor(grayMat, grayMat2, cv::COLOR_GRAY2BGR);
            cv::addWeighted(mat, 0.0f, grayMat2, 1.0f, 0.0f, mat);

            // 均值濾波
            cv::blur(grayMat, tempMat, cv::Size(3, 3));

            cvui::printf(windowMat, width * 1 + 20, height * 1 + 20, "threshold1");
            cvui::trackbar(windowMat, width * 1 + 20, height * 1 + 40, 200, &threshold1, 0, 255);
            cvui::printf(windowMat, width * 1 + 20, height * 1 + 100, "threshold2");
            cvui::trackbar(windowMat, width * 1 + 20, height * 1 + 120, 200, &threshold2, 0, 255);

            // canny邊緣檢測
            cv::Canny(tempMat, tempMat, threshold1, threshold2);
            // copy
            mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
                            cv::Range(srcMat.cols * 0, srcMat.cols * 1));
            cv::cvtColor(tempMat, grayMat2, cv::COLOR_GRAY2BGR);
            cv::addWeighted(mat, 0.0f, grayMat2, 1.0f, 0.0f, mat);

            // 查找輪廓
            std::vector<std::vector<cv::Point>> contours;
            std::vector<cv::Vec4i> hierarchy;
            cv::findContours(tempMat, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);

            // 繪製輪廓
            cv::Mat maskers = cv::Mat::zeros(grayMat.size(), CV_32SC1);
            maskers = cv::Scalar::all(0);
            cv::Mat tMat = srcMat.clone();
            tMat = cv::Scalar(0, 0, 0);
            for(int index = 0; index < contours.size(); index++)
            {
                cv::drawContours(maskers, contours, index, cv::Scalar::all(index+1));
                cv::drawContours(tMat, contours, index, cv::Scalar(0, 0, 255));
            }
            // copy
            mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                            cv::Range(srcMat.cols * 0, srcMat.cols * 1));
            cv::addWeighted(mat, 0.0f, tMat, 1.0f, 0.0f, mat);

            // 分水嶺
            cv::watershed(srcMat, maskers);
            cv::Mat watershedImage(maskers.size(), CV_8UC3) ;
            for(int i = 0 ; i < maskers.rows ; i++ )
            {
                for(int j = 0 ; j < maskers.cols; j++)
                {
                    int index = maskers.at<int>(i, j);
                    if(index == -1)
                    {
                        watershedImage.at<cv::Vec3b>(i, j) = cv::Vec3b(255, 255, 255);
                    }else if( index <= 0 || index > contours.size() )
                    {
                        watershedImage.at<cv::Vec3b>(i, j) = cv::Vec3b(0, 0, 0);
                    }else
                    {
                        watershedImage.at<cv::Vec3b>(i, j) = cv::Vec3b((index - 5 > 0 ? 0 : index % 5) * 50,
                                                                       (index - 5 > 0 ? index - 5 : 0) % 5 * 50,
                                                                       (index - 10 > 0 ? index - 10 : 0) % 5 * 50);
                    }
                    // 混合灰皮圖和 分水嶺效果 圖 並顯 示最終的窗 口
                }
            }
            // copy
            mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                            cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::addWeighted(mat, 0.0f, watershedImage, 1.0f, 0.0f, mat);

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


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

  對應版本號v1.53.0對象


上一篇:《OpenCV開發筆記(五十八):紅胖子8分鐘帶你深刻了解圖像的矩(圖文並茂+淺顯易懂+程序源碼)
下一篇:持續補充中… blog


原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:http://www.javashuo.com/article/p-wxwjppoc-mo.html
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/106258388排序

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息