圖像連通域標記演算法
⑴ opencv如何標記連通區域 並且提取連通區域
代碼
1)Two-pass演算法的一種實現
說明:
基於OpenCV和C++實現,領域:4-領域。實現與演算法描述稍有差別(具體為記錄具有相等關系的label方法實現上)。
// Connected Component Analysis/Labeling By Two-Pass Algorithm
// Author: www.icvpr.com
// Blog : http://blog.csdn.net/icvpr
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <map>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
void icvprCcaByTwoPass(const cv::Mat& _binImg, cv::Mat& _lableImg)
{
// connected component analysis (4-component)
// use two-pass algorithm
// 1. first pass: label each foreground pixel with a label
// 2. second pass: visit each labeled pixel and merge neighbor labels
//
// foreground pixel: _binImg(x,y) = 1
// background pixel: _binImg(x,y) = 0
if (_binImg.empty() ||
_binImg.type() != CV_8UC1)
{
return ;
}
// 1. first pass
_lableImg.release() ;
_binImg.convertTo(_lableImg, CV_32SC1) ;
int label = 1 ; // start by 2
std::vector<int> labelSet ;
labelSet.push_back(0) ; // background: 0
labelSet.push_back(1) ; // foreground: 1
int rows = _binImg.rows - 1 ;
int cols = _binImg.cols - 1 ;
for (int i = 1; i < rows; i++)
{
int* data_preRow = _lableImg.ptr<int>(i-1) ;
int* data_curRow = _lableImg.ptr<int>(i) ;
for (int j = 1; j < cols; j++)
{
if (data_curRow[j] == 1)
{
std::vector<int> neighborLabels ;
neighborLabels.reserve(2) ;
int leftPixel = data_curRow[j-1] ;
int upPixel = data_preRow[j] ;
if ( leftPixel > 1)
{
neighborLabels.push_back(leftPixel) ;
}
if (upPixel > 1)
{
neighborLabels.push_back(upPixel) ;
}
if (neighborLabels.empty())
{
labelSet.push_back(++label) ; // assign to a new label
data_curRow[j] = label ;
labelSet[label] = label ;
}
else
{
std::sort(neighborLabels.begin(), neighborLabels.end()) ;
int smallestLabel = neighborLabels[0] ;
data_curRow[j] = smallestLabel ;
// save equivalence
for (size_t k = 1; k < neighborLabels.size(); k++)
{
int tempLabel = neighborLabels[k] ;
int& oldSmallestLabel = labelSet[tempLabel] ;
if (oldSmallestLabel > smallestLabel)
{
labelSet[oldSmallestLabel] = smallestLabel ;
oldSmallestLabel = smallestLabel ;
}
else if (oldSmallestLabel < smallestLabel)
{
labelSet[smallestLabel] = oldSmallestLabel ;
}
}
}
}
}
}
// update equivalent labels
// assigned with the smallest label in each equivalent label set
for (size_t i = 2; i < labelSet.size(); i++)
{
int curLabel = labelSet[i] ;
int preLabel = labelSet[curLabel] ;
while (preLabel != curLabel)
{
curLabel = preLabel ;
preLabel = labelSet[preLabel] ;
}
labelSet[i] = curLabel ;
}
// 2. second pass
for (int i = 0; i < rows; i++)
{
int* data = _lableImg.ptr<int>(i) ;
for (int j = 0; j < cols; j++)
{
int& pixelLabel = data[j] ;
pixelLabel = labelSet[pixelLabel] ;
}
}
}
2)Seed-Filling種子填充方法
說明:
基於OpenCV和C++實現;領域:4-領域。
// Connected Component Analysis/Labeling By Seed-Filling Algorithm
// Author: www.icvpr.com
// Blog : http://blog.csdn.net/icvpr
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <map>
#include <stack>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
void icvprCcaBySeedFill(const cv::Mat& _binImg, cv::Mat& _lableImg)
{
// connected component analysis (4-component)
// use seed filling algorithm
// 1. begin with a foreground pixel and push its foreground neighbors into a stack;
// 2. pop the top pixel on the stack and label it with the same label until the stack is empty
//
// foreground pixel: _binImg(x,y) = 1
// background pixel: _binImg(x,y) = 0
if (_binImg.empty() ||
_binImg.type() != CV_8UC1)
{
return ;
}
_lableImg.release() ;
_binImg.convertTo(_lableImg, CV_32SC1) ;
int label = 1 ; // start by 2
int rows = _binImg.rows - 1 ;
int cols = _binImg.cols - 1 ;
for (int i = 1; i < rows-1; i++)
{
int* data= _lableImg.ptr<int>(i) ;
for (int j = 1; j < cols-1; j++)
{
if (data[j] == 1)
{
std::stack<std::pair<int,int>> neighborPixels ;
neighborPixels.push(std::pair<int,int>(i,j)) ; // pixel position: <i,j>
++label ; // begin with a new label
while (!neighborPixels.empty())
{
// get the top pixel on the stack and label it with the same label
std::pair<int,int> curPixel = neighborPixels.top() ;
int curX = curPixel.first ;
int curY = curPixel.second ;
_lableImg.at<int>(curX, curY) = label ;
// pop the top pixel
neighborPixels.pop() ;
// push the 4-neighbors (foreground pixels)
if (_lableImg.at<int>(curX, curY-1) == 1)
{// left pixel
neighborPixels.push(std::pair<int,int>(curX, curY-1)) ;
}
if (_lableImg.at<int>(curX, curY+1) == 1)
{// right pixel
neighborPixels.push(std::pair<int,int>(curX, curY+1)) ;
}
if (_lableImg.at<int>(curX-1, curY) == 1)
{// up pixel
neighborPixels.push(std::pair<int,int>(curX-1, curY)) ;
}
if (_lableImg.at<int>(curX+1, curY) == 1)
{// down pixel
neighborPixels.push(std::pair<int,int>(curX+1, curY)) ;
}
}
}
}
}
}
3)顏色標記(用於顯示)
// Connected Component Analysis/Labeling -- Color Labeling
// Author: www.icvpr.com
// Blog : http://blog.csdn.net/icvpr
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <map>
#include <stack>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
cv::Scalar icvprGetRandomColor()
{
uchar r = 255 * (rand()/(1.0 + RAND_MAX));
uchar g = 255 * (rand()/(1.0 + RAND_MAX));
uchar b = 255 * (rand()/(1.0 + RAND_MAX));
return cv::Scalar(b,g,r) ;
}
void icvprLabelColor(const cv::Mat& _labelImg, cv::Mat& _colorLabelImg)
{
if (_labelImg.empty() ||
_labelImg.type() != CV_32SC1)
{
return ;
}
std::map<int, cv::Scalar> colors ;
int rows = _labelImg.rows ;
int cols = _labelImg.cols ;
_colorLabelImg.release() ;
_colorLabelImg.create(rows, cols, CV_8UC3) ;
_colorLabelImg = cv::Scalar::all(0) ;
for (int i = 0; i < rows; i++)
{
const int* data_src = (int*)_labelImg.ptr<int>(i) ;
uchar* data_dst = _colorLabelImg.ptr<uchar>(i) ;
for (int j = 0; j < cols; j++)
{
int pixelValue = data_src[j] ;
if (pixelValue > 1)
{
if (colors.count(pixelValue) <= 0)
{
colors[pixelValue] = icvprGetRandomColor() ;
}
cv::Scalar color = colors[pixelValue] ;
*data_dst++ = color[0] ;
*data_dst++ = color[1] ;
*data_dst++ = color[2] ;
}
else
{
data_dst++ ;
data_dst++ ;
data_dst++ ;
}
}
}
}
4)測試程序
// Connected Component Analysis/Labeling -- Test code
// Author: www.icvpr.com
// Blog : http://blog.csdn.net/icvpr
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <map>
#include <stack>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int main(int argc, char** argv)
{
cv::Mat binImage = cv::imread("../icvpr.com.jpg", 0) ;
cv::threshold(binImage, binImage, 50, 1, CV_THRESH_BINARY_INV) ;
// connected component labeling
cv::Mat labelImg ;
icvprCcaByTwoPass(binImage, labelImg) ;
//icvprCcaBySeedFill(binImage, labelImg) ;
// show result
cv::Mat grayImg ;
labelImg *= 10 ;
labelImg.convertTo(grayImg, CV_8UC1) ;
cv::imshow("labelImg", grayImg) ;
cv::Mat colorLabelImg ;
icvprLabelColor(labelImg, colorLabelImg) ;
cv::imshow("colorImg", colorLabelImg) ;
cv::waitKey(0) ;
return 0 ;
}
⑵ 什麼是圖像連通域分析
連通區域(connected
component)一般是指圖像中具有相同像素值且位置相鄰的前景像素點組成的圖像區域(region,blob)。就是像素值相近的同一塊區域
⑶ java代碼怎麼實現計算圖像二值連通區域的質心
一:幾何距(Geometric Moments)知識與質心尋找原理
1. Image Moments是圖像處理中非常有用的演算法,可以用來計算區域圖像的質心,方向等幾何特性,同時Mpq的高階具有旋轉不變性,可以用來實現圖像比較分類,正是因為Moments有這些特性,很多手繪油畫效果也會基於該演算法來模擬實現。它的數學表達為:
它的低階M00,M01, M10可以用來計算質心,中心化以後M11,M02,M20可以用來計算區域的方向/角度
2. 什麼是質心
就是通過該點,區域達到一種質量上的平衡狀態,可能物理學上講的比較多,簡單點的說就是規則幾何物體的中心,不規則的可以通過掛繩子的方法來尋找。
二:演算法流程
1. 輸入圖像轉換為二值圖像
2. 通過連通組件標記演算法找到所有的連通區域,並分別標記
3. 對每個連通區域運用計算幾何距演算法得到質心
4. 用不同顏色繪制連通區域與質心,輸出處理後圖像
三:演算法效果
左邊為原圖, 右邊藍色為連通組件標記演算法處理以後結果,白色點為質心
四:關鍵代碼解析
1. 計算幾何距演算法代碼
doublem00 = moments(pixels, width, height, 0, 0);
doublexCr = moments(pixels, width, height, 1, 0) / m00;// row
doubleyCr = moments(pixels, width, height, 0, 1) / m00;// column
return new double[]{xCr, yCr};
⑷ NI Vision:二值圖像連通域標記演算法
前面說到,要使用Labwindows + NI Vision(IMAQ Vision)這套商用開發框架來做數圖課設。很明顯,這套虛擬儀器開發平台由NI Instrument(美國國家儀器公司)開發的。大名鼎鼎的Labview軟體就是這個公司開發的。相比較而言,Labwindows使用ANSI C開發,但應用場景是差不多的。
在做課程作業的時候,遇到了一個很有趣的應用。輸入是米粒,比背景灰度要低,目的是輸出米粒的顆數、面積、周長和孔數,這是工業上的一個很常見的應用。具體處理過程是二值化後使用低通濾波,並計算各種性質。
界面設計如下,可以看到米粒的詳細情況。
讓我感興趣的,是通過怎樣的演算法能夠得到米粒的數量?之前曾經用過OpenCV中找最大外界矩形這個函數,但沒有具體了解演算法實現。直覺告訴我原理應該是相似的。
可以看到,每一個米粒之間都是不連通的。這里就就提出了一個概念。 連通區域(Connected Component) 是指圖像中相鄰並有相同像素值的圖像區域。 連通區域分析(Connected Component Analysis,Connected Component Labeling) 是指將圖像中的各個連通區域找出並標記。
二值圖像分析最重要的方法就是連通區域標記,它是所有二值圖像分析的基礎,它通過對二值圖像中白色像素(目標)的標記,讓每個單獨的連通區域形成一個被標識的塊,進一步的我們就可以獲取這些塊的輪廓、外接矩形、質心、不變矩等幾何參數。如果要得到米粒的數量,那麼通過連通區域分析(這里是二值圖像的連通區域分析),就可以得到標記的數量,從而得到米粒的數量。
下面這幅圖中,如果考慮4鄰接,則有3個連通區域,8鄰接則是2個。
從連通區域的定義可以知道,一個連通區域是由具有相同像素值的相鄰像素組成像素集合,因此,我們就可以通過這兩個條件在圖像中尋找連通區域,對於找到的每個連通區域,我們賦予其一個唯一的 標識(Label) ,以區別其他連通區域。
連通區域分析的基本演算法有兩種:1)Two-Pass兩便掃描法 2)Seed-Filling種子填充法 。
兩遍掃描法(Two-Pass),正如其名,指的就是通過掃描兩遍圖像,就可以將圖像中存在的所有連通區域找出並標記。
說了一堆數學語言,其實用圖很好理解
種子填充方法來源於計算機圖形學,常用於對某個圖形進行填充。它基於區域生長演算法。至於區域生長演算法是什麼,可以參照我的這篇 文章 。
同樣的,上動圖
NI Vision 中的運算元定義如下
OpenCV中也有相應的運算元
這里參照其他博客實現一下Two-Pass演算法,Seed-Filling演算法就偷懶不搞了。
Reference:
OpenCV實現圖像連通組件標記與分析
OpenCV-二值圖像連通域分析
數字圖像處理技術 ——鄧繼忠(我的任課老師)
⑸ 急~跪求對二值圖像連通域分析的Matlab程序,最好後面有注釋,方便我看懂,謝謝了~
%最大連通域提取 其作用是找到最大的連通域
if length(size(bw))>2
bw=rgb2gray(bw); %將圖像進行二值化處理
end
L=bwlabel(bw); %標記圖像
stats=regionprops(L);
Ar=cat(1,stats.Area);
ind=find(Ar==max(Ar));
bw=zeros(size(bw));
bw(L==ind)=1;
⑹ 對圖像中連通域進行標記並計算面積matlab
matlab里有連通域標記的函數bwlabel。
至於求連通域面積,stats=regionprops(ibw,'Area');%ibw是標記號的圖。
求中值也有這函數的,median(A)。
⑺ 連通區域
連通區域(Connected Component)一般是指圖像中具有相同像素值且位置相鄰的前景像素點組成的圖像區域,連通區域分析是指將圖像中的各個連通區域找出並標記。
連通區域分析是一種在CV和圖像分析處理的眾多應用領域中較為常用和基本的方法。
例如:OCR識別中字元分割提取(車牌識別、文本識別、字幕識別等)、視覺跟蹤中的運動前景目標分割與提取(行人入侵檢測、遺留物體檢測、基於視覺的車輛檢測與跟蹤等)、醫學圖像處理(感興趣目標區域提取)等。
在需要將前景目標提取出來以便後續進行處理的應用場景中都能夠用到連通區域分析方法,通常連通區域分析處理的對象是一張二值化後的圖像。
在圖像中,最小的單位是像素,每個像素周圍有鄰接像素,常見的鄰接關系有2種:4鄰接與8鄰接。
如果A與B連通,B與C連通,則A與C連通,在視覺上看來,彼此連通的點形成了一個區域,而不連通的點形成了不同的區域。這樣的一個所有的點彼此連通點構成的集合,我們稱為一個連通區域。
兩遍掃描法( Two-Pass ),正如其名,指的就是通過掃描兩遍圖像,將圖像中存在的所有連通域找出並標記。
第一次掃描:
•從左上角開始遍歷像素點,找到第一個像素為255的點,label=1;
•當該像素的左鄰像素和上鄰像素為無效值時,給該像素置一個新的label值,label ++,記錄集合;
•當該像素的左鄰像素或者上鄰像素有一個為有效值時,將有效值像素的label賦給該像素的label值;
•當該像素的左鄰像素和上鄰像素都為有效值時,選取其中較小的label值賦給該像素的label值
第二次掃描:
•對每個點的label進行更新,更新為其對於其集合中最小的label
步驟
1、載入原始圖像
2、閾值分割,將圖像分割為黑白兩個部分
3、對圖像進行開運算,即先腐蝕在膨脹
4、對開運算的結果再進行 膨脹,得到大部分是背景的區域
5、通過距離變換 Distance Transform 獲取前景區域
6、背景區域sure_bg 和前景區域sure_fg相減,得到即有前景又有背景的重合區域
7、連通區域處理
8、最後使用分水嶺演算法
⑻ 將一張圖二值化後,有很多連通區域,我想分別求出每一塊連通區域的面積,不知道有什麼好一點的演算法
圖像處理里有一種叫做Labeling處理的演算法。
可以把二值圖劃分區域,標出不同的區域編號。
只要計算每種編號的個數,就是對應區域的面積了。
如果沒看懂,不是演算法難,是我表達的不好。哈。
⑼ matlab對二值圖像標記,求連通區域的周長,面積。matlab具體代碼
二值圖像中背景標記為0,聯通域標記為1,二值圖像記為a
面積的求法:
k=regionprops(a,'Area'); %k得到的是一個structure結構
S=k.Area;
周長求法:
L=length(find(bwperim(a,4)==1));
%bwperim是求圖像聯通域邊界區,對標記邊界的1進行計數。
ps:可以好好看下regionprops的用法,形態學方面的有一些很有用的處理。
⑽ 誰能給個好用的連通域處理的演算法,要8鄰域的
樓主要處理的圖像是什麼樣的?黑白圖像還是彩色的?我這里有個以前用過的只針對黑白圖像的連通域處理,目標是白色,背景是黑色的,不過,如果你的圖像不是這樣的,那先二值化,然後變換為黑色背景白色目標的圖片 CodeGo.net,再調用我這個函數,先看懂我這個函數,然後修改為可以處理白色背景黑色目標 具體函數
其中THRESHOLD_JOINNUM 是個宏定義……
你要先把圖像的邊緣像素處理下,把邊緣1到2個像素寬度的位置,都置為背景色,否則,可能得不到正確的結果,也可能造成訪問越界。也就是把四周的邊緣像素都置為背景色 然後,如果你仔細看這個函數的實現的話,其實是使用兩次連通域處理,一次是用來把黑色的小區域變為白色,也就是填補目標區域中可能出現的小黑洞。然後第二次處理才是用來刪除那些小於閾值的白色目標小連通域 這個函數你要看明白才能正確使用。看吧。
2. 樓主要處理的圖像是什麼樣的?黑白圖像還是彩色的?我這里有個以前用過的只針對黑白圖像的連通域處理,目標是白色,背景是黑色的,不過,如果你的圖像不是這樣的,那先二值化,然後變換為黑色背景白色目標的圖片,再調用我這個函數,先看懂我這個函數,然後修改為可以處理白色背景黑色目標 具體函數