???把圖像用一維坐標(biāo)表示,二維和三維不好畫,必須用matlab了,我不會(huì)用,意思可以表述到位
第一步:找到圖像的局部最低點(diǎn),這個(gè)方法很多了,可以用一個(gè)內(nèi)核去找,也可以一個(gè)一個(gè)比較,實(shí)現(xiàn)起來不難。
第二步:從最低點(diǎn)開始注水,水開始網(wǎng)上滿(圖像的說法就是梯度法),其中那些最低點(diǎn)已經(jīng)被標(biāo)記,不會(huì)被淹沒,那些中間點(diǎn)是被淹沒的。
第三步:找到局部最高點(diǎn),就是圖中3位置對(duì)應(yīng)的兩個(gè)點(diǎn)。
第四步:這樣基于局部最小值,和找到的局部最大值,就可以分割圖像了。
分類圖
模擬結(jié)果圖
是不是感覺上面的方法很好,也很簡(jiǎn)單?接著看下面的圖:
利用上面的步驟,第一步找到了三個(gè)點(diǎn),然后第二步開始漫水,這三個(gè)點(diǎn)都被記錄下來了,又找到兩個(gè)局部最大值。
這是我們想要的嗎?
回答是否定的!其中中間那個(gè)最小值我們不需要,因?yàn)橹皇且粋€(gè)很少并且很小的噪點(diǎn)而已,我們不需要圖像分割的那么細(xì)致。
缺陷顯露出來了吧?沒關(guān)系,下面我們的opencv把這個(gè)問題解決了。
模擬分類圖
模擬結(jié)果圖
opencv改進(jìn)的分水嶺算法:
針對(duì)上面出現(xiàn)的問題,我們想到的是能不能給這種小細(xì)節(jié)一個(gè)標(biāo)記,讓它不屬于我們找的最小的點(diǎn)呢?
opencv對(duì)其改進(jìn)就是使用了人工標(biāo)記的方法,我們標(biāo)記一些點(diǎn),基于這些點(diǎn)去引導(dǎo)分水嶺算法的進(jìn)行,效果很好!
比如我們對(duì)上面的圖像標(biāo)記了兩個(gè)三角形,第一步我們找到三個(gè)局部最小點(diǎn),第二步淹沒的時(shí)候三個(gè)點(diǎn)都被淹沒了,然而中間那個(gè)沒被標(biāo)記,那就淹死了(沒有救生圈),其余兩個(gè)點(diǎn)保留,這樣就可以達(dá)到我們的想要的結(jié)果了。
注釋:這里的標(biāo)記是用不同的標(biāo)號(hào)進(jìn)行的,我為了方便使用了同樣的三角形了。因?yàn)闃?biāo)記用來分類,所以不同的標(biāo)記打上不同的標(biāo)號(hào)!這在下面opencv程序中體現(xiàn)了。。。
模擬分類圖
模擬結(jié)果圖
注釋:具體的實(shí)現(xiàn)沒有完成,感覺原理懂了會(huì)使用了這樣就可以了,當(dāng)你需要深入的時(shí)候再去研究實(shí)現(xiàn)的算法,當(dāng)你淺淺的使用懂了原理應(yīng)該會(huì)改一點(diǎn),面試過了完全可以??!哈哈哈~~
opencv實(shí)現(xiàn):
#include
#include
using namespace cv;
using namespace std;
void waterSegment(InputArray& _src, OutputArray& _dst, int& noOfSegment);
int main(int argc, char** argv) {
Mat inputImage = imread("coins.jpg");
assert(!inputImage.data);
Mat graImage, outputImage;
int offSegment;
waterSegment(inputImage, outputImage, offSegment);
waitKey(0);
return 0;
}
void waterSegment(InputArray& _src,OutputArray& _dst,int& noOfSegment)
{
Mat src = _src.getMat();//dst = _dst.getMat();
Mat grayImage;
cvtColor(src, grayImage,CV_BGR2GRAY);
threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);
Mat kernel = getStructuringElement(MORPH_RECT, Size(9, 9), Point(-1, -1));
morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
distanceTransform(grayImage, grayImage, DIST_L2, DIST_MASK_3, 5);
normalize(grayImage, grayImage,0,1, NORM_MINMAX);
grayImage.convertTo(grayImage, CV_8UC1);
threshold(grayImage, grayImage,0,255, THRESH_BINARY | THRESH_OTSU);
morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
vector
vector
Mat showImage = Mat::zeros(grayImage.size(), CV_32SC1);
findContours(grayImage, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));
for (size_t i = 0; i < contours.size(); i++)
{
//這里static_cast
drawContours(showImage, contours, static_cast
}
Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(src, src, MORPH_ERODE, k);
watershed(src, showImage);
//隨機(jī)分配顏色
vector
for (size_t i = 0; i < contours.size(); i++) {
int r = theRNG().uniform(0, 255);
int g = theRNG().uniform(0, 255);
int b = theRNG().uniform(0, 255);
colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
}
// 顯示
Mat dst = Mat::zeros(showImage.size(), CV_8UC3);
int index = 0;
for (int row = 0; row < showImage.rows; row++) {
for (int col = 0; col < showImage.cols; col++) {
index = showImage.at
if (index > 0 && index <= contours.size()) {
dst.at
}
else if (index == -1)
{
dst.at
}
else {
dst.at
}
}
}
}
分水嶺合并代碼:
void segMerge(Mat& image, Mat& segments, int& numSeg)
{
vector
int newNumSeg = numSeg;
//初始化變量長(zhǎng)度的Vector
for (size_t i = 0; i < newNumSeg; i++)
{
Mat sample;
samples.push_back(sample);
}
for (size_t i = 0; i < segments.rows; i++)
{
for (size_t j = 0; j < segments.cols; j++)
{
int index = segments.at
if (index >= 0 && index <= newNumSeg)//把同一個(gè)區(qū)域的點(diǎn)合并到一個(gè)Mat中
{
if (!samples[index].data)//數(shù)據(jù)為空不能合并,否則報(bào)錯(cuò)
{
samples[index] = image(Rect(j, i, 1, 1));
}
else//按行合并
{
vconcat(samples[index], image(Rect(j, i, 2, 1)), samples[index]);
}
}
//if (index >= 0 && index <= newNumSeg)
// samples[index].push_back(image(Rect(j, i, 1, 1)));
}
}
vector
Mat hsv_base;
int h_bins = 35;
int s_bins = 30;
int histSize[2] = { h_bins , s_bins };
float h_range[2] = { 0,256 };
float s_range[2] = { 0,180 };
const float* range[2] = { h_range,s_range };
int channels[2] = { 0,1 };
Mat hist_base;
for (size_t i = 1; i < numSeg; i++)
{
if (samples[i].dims > 0)
{
cvtColor(samples[i], hsv_base, CV_BGR2HSV);
calcHist(&hsv_base, 1, channels, Mat(), hist_base, 2, histSize, range);
normalize(hist_base, hist_base, 0, 1, NORM_MINMAX);
hist_bases.push_back(hist_base);
}
else
{
hist_bases.push_back(Mat());
}
}
double similarity = 0;
vector
for (size_t i = 0; i < hist_bases.size(); i++)
{
for (size_t j = i+1; j < hist_bases.size(); j++)
{
if (!merged[j])//未合并的區(qū)域進(jìn)行相似性判斷
{
if (hist_bases[i].dims > 0 && hist_bases[j].dims > 0)//這里維數(shù)判斷沒必要,直接用個(gè)data就可以了
{
similarity = compareHist(hist_bases[i], hist_bases[j], HISTCMP_BHATTACHARYYA);
if (similarity > 0.8)
{
merged[j] = true;//被合并的區(qū)域標(biāo)志位true
if (i != j)//這里沒必要,i不可能等于j
{
newNumSeg --;//分割部分減少
for (size_t p = 0; p < segments.rows; p++)
{
for (size_t k = 0; k < segments.cols; k++)
{
int index = segments.at
if (index == j) segments.at
}
}
}
}
}
}
}
}
numSeg = newNumSeg;//返回合并之后的區(qū)域數(shù)量
}
-
OpenCV
+關(guān)注
關(guān)注
31文章
635瀏覽量
41425
原文標(biāo)題:分水嶺算法(理論+opencv實(shí)現(xiàn))
文章出處:【微信號(hào):AI_shequ,微信公眾號(hào):人工智能愛好者社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論