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

来源:https://www.cnblogs.com/qq21497936/archive/2020/05/24/12953036.html
-Advertisement-
Play Games

若該文為原創文章,未經允許不得轉載原博主博客地址:https://blog.csdn.net/qq21497936原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062本文章博客地址:https://blog.csdn.n ...


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

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

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

 

前言

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

 

Demo

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

 

分水嶺演算法

概述

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

原理

  分水嶺比較經典的計算方法是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{.}表示梯度運算。
  分水嶺演算法對微弱邊緣具有良好的響應,圖像中的雜訊、物體錶面細微的灰度變化,都會產生過度分割的現象。但同時,分水嶺演算法對微弱邊緣具有良好的響應,是得到封閉連續邊緣的保證的。

分水嶺函數原型

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

Demo涉及到的相關知識

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

 

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分鐘帶你深入瞭解圖像的矩(圖文並茂+淺顯易懂+程式源碼)
下一篇:持續補充中…

 

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 通常大家開發大部分是本地git push 提交,伺服器上git pull 手動更新。git 可以使用webhook實現自動部署。webhook是倉庫平臺的一個鉤子事件,通過hook 鉤子監聽代碼,回調通知(通知地址就是你在各個git倉庫平臺中填寫的webhook地址,一般在你的某個項目倉庫如mypr ...
  • 根據碎片的生命周期,我們知道onAttach()方法首先會被執行,因此在這裡做一些數據初始化的操作,比如調用getNews()方法獲取幾條模擬的新聞數據,以及完成NewsAdapter的創建,然後在onCreateView()方法中載入了news_title_frag佈局,並給新聞列表的ListVi ...
  • 1/ 概述 利用Spring Boot作為基礎框架,Spring Security作為安全框架,WebSocket作為通信框架,實現點對點聊天和群聊天。 2/ 所需依賴 Spring Boot 版本 1.5.3,使用MongoDB存儲數據(非必須),Maven依賴如下: 配置文件內容: 大致程式結構 ...
  • 新開一坑——Elements of Financial Risk Management in Python 用 python 完成 " Elements of Financial Risk Management (Second Edition)" 一書的課後實踐練習,希望年底之前能完成吧。 項目地址 ...
  • 最近開始學Go語言,但是在使用VS Code 編寫Go的時候出現了插件無法下載的問題。最初我的解決辦法也是從github下載再安裝,但是我並不喜歡這種做法,因為我要在多台pc上使用VS Code編寫Go,所以我覺要重覆多次很麻煩,而且插件的安裝也非常麻煩,我曾經一度想放棄學習Go語言,沒錯因為安裝插 ...
  • 本教程源碼請訪問: "tutorial_demo" 上篇教程我們學習瞭如何使用工廠模式解耦,把對象的創建由程式員交給自定義的工廠類,在這篇教程我們將學到如何使用Spring的IOC解決程式的耦合問題。 一、什麼是IOC IOC:Inversion of Control,控制反轉,將創建對象的權力交給 ...
  • 前言 繼上一篇使用Flutter開發的抖音國際版 後再次擼一個國內版抖音,大部分功能已完成,主要是Flutter開發APP速度很爽, 先看下圖 項目主要結構介紹 這次主要的改動在api.dart 及douyin.dart里,國內抖音的api是不同的,另外地址以及實體類也不一樣。詳細下麵介紹. 抖音的 ...
  • 為什麼數組要從零開始? 如題,數組第一個元素為什麼要從零開始,而不從一開始?感覺這很反人類呀,正常來講,一個集合的開始,不應該從一嗎? 對於這個問題,我覺得可以從以下兩方面來考慮。 1設計層面 我們先瞭解一下數組最基本的結構和定址方式(即實現方式)。 現在市面上無論是C、Java、PHP,還是Go或 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...