若該文為原創文章,未經允許不得轉載
原博主博客地址: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
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/24319.html
標籤:C++
上一篇:C++98/11/17運算式類別
