图像连通域检测的2路算法Code
???????? 本文算法描述參考鏈接:http://blog.csdn.net/icvpr/article/details/10259577
兩遍掃描法:
(1)第一次掃描:
訪問當前像素B(x,y),如果B(x,y) == 1:
a、如果B(x,y)的領域中像素值都為0,則賦予B(x,y)一個新的label:
label += 1, B(x,y)?= label;
b、如果B(x,y)的領域中有像素值 > 1的像素Neighbors:
1)將Neighbors中的最小值賦予給B(x,y):
B(x,y)?= min{Neighbors}
2)記錄Neighbors中各個值(label)之間的相等關系,即這些值(label)同屬同一個連通區域;
?labelSet[i] = { label_m, .., label_n },labelSet[i]中的所有label都屬于同一個連通區域(注:這里可以有多種實現方式,只要能夠記錄這些具有相等關系的label之間的關系即可)
(2)第二次掃描:
訪問當前像素B(x,y),如果B(x,y) > 1:
a、找到與label = B(x,y)同屬相等關系的一個最小label值,賦予給B(x,y);
???????????????????????
算法代碼:
從直覺上看,二路法比堆棧法要快,其實速度只有堆棧法的1/5 - 1/10,代碼如下:
//使用兩遍掃描法//查找所有的連通域 //簡單方法,使用1-0矩陣//使用列標記的方法,收集所有的連通域 //需要使用倒排索引 bool CD2DetectInPic::searchConBy2Way(const cv::Mat& _binImg, float valueForeB, float valueForeUp,std::vector<std::vector<cv::Point > > &foreAreas) {int vFore = 255;int vBack = 0;foreAreas.resize(0);cv::Mat _lableImg;if (_binImg.channels()>1){cv::cvtColor(_binImg,_lableImg,cv::COLOR_BGR2GRAY);}else{_binImg.copyTo(_lableImg);}//一遍掃描,得出前景和背景點,進行標記//背景點標記為0,前景點標記為1 #ifdef SHOW_TEMPcv::imshow("",_lableImg);//cv::waitKey(0); #endifIplImage imageLabel = _lableImg;for (int i=0;i< imageLabel.height;++i){char* pI = (char*)imageLabel.imageData + i * imageLabel.widthStep;for (int j=0;j<imageLabel.width;++j ){if ( *pI >=valueForeB &&*pI <=valueForeUp)//避開單一值失誤!{*pI = vFore;} else{*pI = vBack;}++pI;}}//對label圖像進行遍歷,尋找連通域//對Mark矩陣,進行修改,不修改標識矩陣cv::Mat imageMark(&imageLabel);cv::Mat imageMarkRe = imageMark.clone();//使用線掃描的方法進行分離合并//使用set集合表示相等關系 #ifdef SHOW_TEMPcv::imshow("imageMarkRe",imageMarkRe);//cv::waitKey(1); #endifstd::stack<std::pair<int,int> > neighborPixels; //使用另外一個Label矩陣,用于記載當前點屬于哪個連通域cv::Mat conAreaLocMat = cv::Mat::zeros(_binImg.rows,_binImg.cols,CV_8UC1);cv::bitwise_not(conAreaLocMat,conAreaLocMat);IplImage imageConAreaLoc = conAreaLocMat;std::vector<std::pair<cv::Point,uchar> > conAreas(imageMark.cols);std::vector<std::vector<cv::Point> > conPointsAreas(0);//第n個連通域int counter =0;//用于標記為第N個連通域int idx =0;//用于哈希索引//設置重復集合//用于最后合并連通域//std::vector<std::vector<int> > overlapVec(0);//std::set<int,int> overlapSet;std::multimap<int,int> overlapMap;//使用多值哈希//對第一行進行連通域劃分{char* pLabel = (char*)imageLabel.imageData + 0* imageLabel.widthStep;char* pLoc = (char*)imageConAreaLoc.imageData + 0* imageConAreaLoc.widthStep;int lastL = *pLabel;if (vFore == lastL){++counter;*pLoc = counter;//計數從1開始,索引從0開始//++idx;std::pair<int,int> p(counter,idx);overlapMap.insert(p);//插入索引} else{ *pLoc = -1;}int lastLoc = *pLoc;++pLabel;++pLoc;for (int n=1; n< imageMark.cols; ++n){if (vFore == *pLabel )//若標記為連通域,則進行標記{if ( *pLabel==lastL )//若和上一個像素聯通,則標記為上一個的標記{*pLoc = lastLoc;} else{//若上一個為0,則增加標記符號,為新的標記++counter;*pLoc = counter;++idx;std::pair<int,int> p(counter,idx);overlapMap.insert(p);//插入索引//std::cout<< idx << "_" <<counter<<std::endl;}}else{*pLoc = -1;//若遇到第n個點為變化點,標記為第n個連通域的位置}lastL = *pLabel;lastLoc = *pLoc;++pLoc;++pLabel;}}//對圖片每一行進行尋找連通域,必須遍歷for (int m =1; m<imageMark.rows; ++m ){//對第一個像素進行指派//上一行的指針char* pLabelFore = (char*)imageLabel.imageData + (m-1)* imageLabel.widthStep;char* pLocFore = (char*)imageConAreaLoc.imageData + (m-1)* imageConAreaLoc.widthStep;//這一行的指針char* pLabel = (char*)imageLabel.imageData + m* imageLabel.widthStep;char* pLoc = (char*)imageConAreaLoc.imageData + m* imageConAreaLoc.widthStep;//標記第一個點,以上一行為準int foreL = *pLabelFore;int foreLoc = *pLocFore;int lastL = *pLabel;if (*pLabel == foreL)//若和上一行的值相等,則連通域位置和上一行的第一個值相等{*pLoc = *pLocFore;} else{if ( vFore == *pLabel){//若為連通域,則增加標記符號,為新的標記++counter;*pLoc = counter;++idx;std::pair<int,int> p(counter,idx);overlapMap.insert(p);//插入索引//std::cout<< idx << "_" <<counter<<std::endl;} else{*pLoc = -1;}}int lastLoc = *pLoc;for (int n=0; n< imageMark.cols-1; ++n){//尋找每一行的連通域lastL = *pLabel;//上一個像素的標記lastLoc = *pLoc;//上一個像素的連通域位置//到下一個像素++pLabel;++pLoc;++pLabelFore;++pLocFore;foreL = *pLabelFore;//上一行此位置像素的值foreLoc = *pLocFore;//上一個像素的連通域位置if (vBack == *pLabel )//若當前label為0{*pLoc = -1;}else{if (*pLabel != foreL )//若當前label不等于上一行像素的label{if (*pLabel != lastL ){//若當前label不等于上一個像素的label,則表示為新的連通域起始,新建集合//若為連通域,則增加標記符號,為新的標記++counter;*pLoc = counter;++idx;std::pair<int,int> p(counter,idx);overlapMap.insert(p);//插入索引//std::cout<< idx << "_" <<counter<<std::endl;} else{//若當前label等于上一個像素的label,則為連通,直接賦值*pLoc = lastLoc;}}else{//若等于前一行像素,就麻煩了!if (*pLabel != lastL)//若不等于上一個的像素label,則賦值為上一行的標記{*pLoc = foreLoc;} else{if (foreLoc == lastLoc)//若前一個和前一行相等{*pLoc = foreLoc;} else{//若前一個和前一行不相等//若等于前一個像素,需要合并集合//標記相同集合*pLoc = foreLoc;//合并哈希表//查找到值所在的哈希索引int idxF = ( *overlapMap.find(foreLoc) ).first;int idxFL =( *overlapMap.find(lastLoc) ).first;//map尋找的為key,而不是值int L1 = ( *overlapMap.find(foreLoc) ).second;int L2 = ( *overlapMap.find(lastLoc) ).second;if (L1 != L2)//若不在一個索引中,可以刪除{//將當前值插入到索引之中,不增加idx索引//必須先刪除再插入,以免誤刪除//去除掉舊的 鍵值 對//overlapMap.erase(idxFL);//一個參數為key,是錯誤的//int x = overlapMap.erase(lastLoc);overlapMap.erase(idxFL);//std::pair<int,int> p(idxF,lastLoc);std::pair<int,int> p(lastLoc,L1);overlapMap.insert(p);} else{}}}}}}}SYSTEMTIME sys; GetLocalTime( &sys ); int MileTs = sys.wSecond;int MileT = sys.wMilliseconds;//直接查找到位置conPointsAreas.resize(overlapMap.size());IplImage imageConArea= imageConAreaLoc;for (int i=0;i< imageConArea.height;++i){char* pI = (char*)imageConArea.imageData + i * imageConArea.widthStep;for (int j=0;j<imageConArea.width;++j ){if (*pI >0){cv::Point p(j,i);int idx = (*overlapMap.find((int)*pI)).second-1;conPointsAreas[idx ].push_back(p);}++pI;}}int validVec =0;foreAreas.resize(validVec);for (int i=0;i< conPointsAreas.size();++i){if (conPointsAreas[i].size() >0 ){++validVec;foreAreas.push_back(conPointsAreas[i]);}}GetLocalTime( &sys ); int MileT2 = sys.wMilliseconds;int DetaT = MileT2 - MileT;std::cout<< std::endl;std::cout<< "The CopyVec time is :"<< DetaT<<"mS..........."<< std::endl;std::cout<< std::endl;conPointsAreas.resize(0);return true; }總結
以上是生活随笔為你收集整理的图像连通域检测的2路算法Code的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不休的乌拉拉白狼王怎么打 汉典不字的基本
- 下一篇: 不休的乌拉拉雷霆剑龙怎么过 汉典不字的基