regionConnectAnalysis 是通过种子填充方法来寻找连通区域的,这种方法相比与two-pass的方法较慢。
/*! @function
*******************************************************************************
<PRE>
Function Name : regionConnectAnalysis
-------------------------------------------------------------------------------
Function : Region connected analysis using seed filling method
-------------------------------------------------------------------------------
Parameters : < cv::Mat& binaryImage, cv::Mat& labelImage,int RegionNum >
[IN] param 1 : binaryImage target image
[OUT] param 2 : labelImage Image combine by label
[IN] param 3 : RegionNum Region's number
Return : bool
Reference:https://blog.csdn.net/icvpr/article/details/10259577
<PRE>
******************************************************************************/
int regionConnectAnalysis(cv::Mat& binaryImage, cv::Mat& labelImage)
{
// check input image
if (binaryImage.empty() || binaryImage.type() != CV_8UC1)
return false;
// clear output image
labelImage.release();
binaryImage.convertTo(labelImage, CV_32SC1);
// 8-neigbor Mat construction
int DIR[8][2]={ { -1, -1 }, { -1, 0 }, { -1, 1 },
{0, -1}, { 0, 1 } ,
{1, -1}, { 1, 0 }, { 1, 1 } };
// seed filling method
int label = 1;
cv::Point tmpPoint;
int rows = binaryImage.rows;
int cols = binaryImage.cols;
//cv::Point tmpPoint = cv::Point(0, 0);
for (int i = 0; i < rows; i++){
for (int j = 0; j < cols; j++){
if (labelImage.at<int>(i,j) == 1)
{
std::vector < cv::Point > neigborPixels;
neigborPixels.push_back(cv::Point(j, i));
++label;
while (!neigborPixels.empty()){
cv::Point seedPoint;
seedPoint = neigborPixels.back();
neigborPixels.pop_back();
labelImage.at<int>(seedPoint) = label;
// push the 8-neighbor
for (int k = 0; k < 8; ++k){
tmpPoint.x = seedPoint.x + DIR[k][0];
tmpPoint.y = seedPoint.y + DIR[k][1];
if (tmpPoint.x<0 || tmpPoint.y<0 || tmpPoint.x>(cols - 1) || tmpPoint.y>(rows - 1))
continue;
int tmpVal = labelImage.at<int>(tmpPoint);
if (tmpVal == 1)
neigborPixels.push_back(tmpPoint);
}
}
}
}
}
return label - 1;
}
当得出labelImage后,可以通过索引值找出感兴趣的区域,这里给出两种方法,一种是遍历Mat的每个像素,比较label ;另一个是通过建立灰度值为label的图像进行比较;
//遍历法
/*! @function
*******************************************************************************
<PRE>
Function Name : selectRegion
-------------------------------------------------------------------------------
Function : Select region from regions by label
-------------------------------------------------------------------------------
Parameters : < cv::Mat& labelImage, cv::Mat& regionImage, int labelIndex >
[IN] param 1 : labelImage target image
[OUT] param 2 : regionImage interest region
[IN] param 3 : labelIndex label Index
Return : bool
<PRE>
******************************************************************************/
bool selectRegion(cv::Mat& labelImage, cv::Mat& regionImage, int labelIndex)
{
// check labelImage
if (labelImage.empty() || labelImage.type() != CV_32SC1)
return false;
// clear regionImage
regionImage.release();
regionImage.create(labelImage.size(), CV_8UC1);
// select
int rows = labelImage.rows;
int cols = labelImage.cols;
cv::Point tmpPoint = cv::Point(0, 0);
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
tmpPoint = cv::Point(i, j);
if (labelImage.at<int>(tmpPoint) == labelIndex)
regionImage.at<uchar>(tmpPoint) = 255;
else
regionImage.at<uchar>(tmpPoint) = 0;
}
}
return true
}
//Mat比较法
/*! @function
*******************************************************************************
<PRE>
Function Name : selectRegionOptimize;
-------------------------------------------------------------------------------
Function : Select region from regions by label
-------------------------------------------------------------------------------
Parameters : < cv::Mat& labelImage, cv::Mat& regionImage, int labelIndex >
[IN] param 1 : labelImage target image
[OUT] param 2 : regionImage interest region
[IN] param 3 : labelIndex label Index
Return : bool
<PRE>
******************************************************************************/
bool selectRegionOptimize(cv::Mat& labelImage, cv::Mat& regionImage, int labelIndex)
{
// check labelImage
if (labelImage.empty() || labelImage.type() != CV_32SC1)
return false;
// clear regionImage
regionImage.release();
regionImage.create(labelImage.size(), CV_8UC1);
// find interest region
cv::Mat tmp(labelImage.size(), CV_32SC1, cv::Scalar((double)labelIndex));
regionImage = labelImage == tmp;
return true;
}
分析这种方法的快慢
int main()
{
Mat src = imread("D:/ImageProcessing Images/DIP3E_Original_Images_CH09/Fig0916(a)(region-filling-reflections).tif");
if (src.empty())
{
cout << "Empty image" << endl;
}
else
{
//imshow("orignal image", src);
}
// to gray image
Mat gray, binaryImage;
CVTL::RGB2GRAY(src, gray);
cv::threshold(gray, binaryImage, 30, 1, cv::THRESH_BINARY);
Mat labelImage, interestRegImage,ApiTmpImage,ApiRegImage;
int regionNum = CVTL::regionConnectAnalysis(binaryImage, labelImage);
double oldMethodStart = getTickCount();
CVTL::selectRegion(labelImage, interestRegImage, 4);
double oldMethodEnd = getTickCount();
double newMethodStart = getTickCount();
CVTL::selectRegionOptimize(labelImage, interestRegImage, 4);
double newMethodEnd = getTickCount();
double oldMethod = (oldMethodEnd - oldMethodStart) / getTickFrequency();
double newMethod = (newMethodEnd - newMethodStart) / getTickFrequency();
std::cout << "oldMethod :" << oldMethod << endl;
std::cout << "newMethod :" << newMethod << endl;
std::cout << regionNum << endl;
imshow("interestRegImage", interestRegImage);
waitKey(0);
return 0;
}
原图如下
选取结果
两种方法用时:
可以看出第二种方法在速度上明显快于第一种。