連通域分析其實是一個路徑搜索問題,搜索方式就看聯通的規則(4聯通:上下左右算是聯通,8聯通:上下左右還有四個對角)
01矩陣背景是0,黑色的,有色區域是1,白色的,從圖像的左上角(最外圍的邊要去掉)進行遍歷,將找到的第一個值為1的像素點作為起點,對他進行連通域搜尋,將搜尋到的整個連通域內的像素點標為2(為了避免與本來的顏色1沖突)
繼續搜索像素值為1的點(之前聯通域分析過的已經將像素值改為大於1的值,所以像素值為1的就是還沒有分析過的),直到整個圖像遍歷完成
遍歷方法:
找到起點后初始化一個棧,存放連通域的點坐標(pair),將起點的橫縱坐標壓入棧中,連通域的標記值自增(第一個連通域的標記值為2,從2開始,第二個連通域的標記值為3,以此類推)
當棧不為空時,從棧頂取點,讀出他的橫縱坐標,在Label圖中,根據橫縱坐標對連通域進行對應標記值的賦值,然后將該點出棧,同時按照聯通規則,根據坐標找到它的相鄰像素點,看相鄰像素點的值是否為1,如果是1,則是連通域,將這個點入棧(判斷時記得要加上邊界條件,防止點下標溢出,我的代碼偷懶了沒有加,正常要加上 0<=Row<BinaryImg.rows&&0<=Col<BinaryImg.cols)
代碼
void SeedFilling(const Mat &BinaryImg, Mat &LabelImg) { if (BinaryImg.empty || BinaryImg.type() != CV_8UC1) return; BinaryImg.convertTo(LabelImg, CV_32SC1); int Label = 1;// 從2開始,防止和二值圖像的像素值重合 int Row = BinaryImg.rows - 1; int Col = BinaryImg.cols - 1; for (int i = 1; i < Row; ++i) { int* data = LabelImg.ptr<int>(i); for (int j = 1; j < Col - 1; ++j) { if (data[j] == 1) { stack<pair<int, int>> NeighborPixel; NeighborPixel.push(pair<int, int>(i, j)); ++Label;// 新標簽 while (!NeighborPixel.empty)// 棧不為空 { pair<int, int> CurPixel = NeighborPixel.top(); int CurRow = CurPixel.first; int CurCol = CurPixel.second; LabelImg.at<int>(CurRow, CurCol) = Label; NeighborPixel.pop();// 出棧 if (LabelImg.at<int>(CurRow, CurCol - 1) == 1) NeighborPixel.push(pair<int, int>(CurRow, CurCol - 1)); if (LabelImg.at<int>(CurRow, CurCol + 1) == 1) NeighborPixel.push(pair<int, int>(CurRow, CurCol + 1)); if (LabelImg.at<int>(CurRow - 1, CurCol) == 1) NeighborPixel.push(pair<int, int>(CurRow - 1, CurCol)); if (LabelImg.at<int>(CurRow + 1, CurCol) == 1) NeighborPixel.push(pair<int, int>(CurRow + 1, CurCol)); } } } return; } }