OpenCV 扫描标记法求连通区域面积
2017-08-25 20:04
204 查看
扫描标记的时候 没有选择用Two-Pass(两遍扫描法),而是用Seed-Filling(种子填充法)。
扫描标记中最需要注意点:
1.首先说明我的图片是黑色底,白色目标物。
2.icvprCcaBySeedFill函数扫描全图一遍以后,不同的连通区域就已经分配了一个唯一且不同的标记号。
3.接下来icvprLabelColor函数也是把全图扫描一遍,然后依次判断每一个像素点的值,并都赋给pixelValue
,如果这个值为1,就说明是1号连通区,为2,就说明是2号连通区。
4.为了求面积,在判断像素点的值的时候,加入了count变量,作为计数器。发现一个1号连通区的像素点,count值就加1,以此得出1号连通区的像素点数,其他连通区以此类推。
5.如果你的图片是白色底,黑色的目标物,那么在(data[j]
== 0), if (_lableImg.at<int>(curX, curY-1) == 0) 和下面3个4连通的判断中,一定要把0改成1,而且还要把threshold(binImage,
binImage, 50,255, CV_THRESH_BINARY_INV) ; 中的255改成1,不然调不出来。
6.代码是照抄其他博客的,不知道这算不算原创。不过count++这个是我自己想的
哈哈。亲测可用。
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <map>
#include <stack>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
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 Scalar(b,g,r) ;
}
void icvprLabelColor(Mat& _labelImg, Mat& _colorLabelImg)
{
int count=0;
if (_labelImg.empty() ||
_labelImg.type() != CV_32SC1)
{
return ;
}
map<int, Scalar> colors ;
int rows = _labelImg.rows ;
int cols = _labelImg.cols ;
_colorLabelImg.release() ;
_colorLabelImg.create(rows, cols, CV_8UC3) ;
_colorLabelImg = 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 == 3) //pixelValue是标记号
{
count++;//数像素点
if (colors.count(pixelValue) <= 0)
{
colors[pixelValue] = icvprGetRandomColor() ;
}
Scalar color = colors[pixelValue] ;
*data_dst++ = color[0] ;
*data_dst++ = color[1] ;
*data_dst++ = color[2] ;
}
else
{
data_dst++ ;
data_dst++ ;
data_dst++ ;
}
}
}
printf("%d",count);
}
void icvprCcaBySeedFill(Mat& _binImg,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] == 0)
{
stack<pair<int,int>> neighborPixels ;
neighborPixels.push(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
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) == 0)
{// left pixel
neighborPixels.push(pair<int,int>(curX, curY-1)) ;
}
if (_lableImg.at<int>(curX, curY+1) == 0)
{// right pixel
neighborPixels.push(pair<int,int>(curX, curY+1)) ;
}
if (_lableImg.at<int>(curX-1, curY) == 0)
{// up pixel
neighborPixels.push(pair<int,int>(curX-1, curY)) ;
}
if (_lableImg.at<int>(curX+1, curY) == 0)
{// down pixel
neighborPixels.push(pair<int,int>(curX+1, curY)) ;
}
}
}
}
}
}
int main(int argc, char** argv)
{
Mat binImage = imread("D:\\VS2012\\Work\\test7\\test7\\9.jpg", 0) ; //你自己改地址
imshow("原图", binImage) ;
threshold(binImage, binImage, 50,255, CV_THRESH_BINARY_INV) ;
Mat labelImg ;
icvprCcaBySeedFill(binImage, labelImg) ;
Mat colorLabelImg ;
icvprLabelColor(labelImg, colorLabelImg) ;
imshow("区域标记", colorLabelImg) ;
waitKey(0) ;
return 0 ;
}
扫描标记中最需要注意点:
1.首先说明我的图片是黑色底,白色目标物。
2.icvprCcaBySeedFill函数扫描全图一遍以后,不同的连通区域就已经分配了一个唯一且不同的标记号。
3.接下来icvprLabelColor函数也是把全图扫描一遍,然后依次判断每一个像素点的值,并都赋给pixelValue
,如果这个值为1,就说明是1号连通区,为2,就说明是2号连通区。
4.为了求面积,在判断像素点的值的时候,加入了count变量,作为计数器。发现一个1号连通区的像素点,count值就加1,以此得出1号连通区的像素点数,其他连通区以此类推。
5.如果你的图片是白色底,黑色的目标物,那么在(data[j]
== 0), if (_lableImg.at<int>(curX, curY-1) == 0) 和下面3个4连通的判断中,一定要把0改成1,而且还要把threshold(binImage,
binImage, 50,255, CV_THRESH_BINARY_INV) ; 中的255改成1,不然调不出来。
6.代码是照抄其他博客的,不知道这算不算原创。不过count++这个是我自己想的
哈哈。亲测可用。
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <map>
#include <stack>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
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 Scalar(b,g,r) ;
}
void icvprLabelColor(Mat& _labelImg, Mat& _colorLabelImg)
{
int count=0;
if (_labelImg.empty() ||
_labelImg.type() != CV_32SC1)
{
return ;
}
map<int, Scalar> colors ;
int rows = _labelImg.rows ;
int cols = _labelImg.cols ;
_colorLabelImg.release() ;
_colorLabelImg.create(rows, cols, CV_8UC3) ;
_colorLabelImg = 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 == 3) //pixelValue是标记号
{
count++;//数像素点
if (colors.count(pixelValue) <= 0)
{
colors[pixelValue] = icvprGetRandomColor() ;
}
Scalar color = colors[pixelValue] ;
*data_dst++ = color[0] ;
*data_dst++ = color[1] ;
*data_dst++ = color[2] ;
}
else
{
data_dst++ ;
data_dst++ ;
data_dst++ ;
}
}
}
printf("%d",count);
}
void icvprCcaBySeedFill(Mat& _binImg,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] == 0)
{
stack<pair<int,int>> neighborPixels ;
neighborPixels.push(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
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) == 0)
{// left pixel
neighborPixels.push(pair<int,int>(curX, curY-1)) ;
}
if (_lableImg.at<int>(curX, curY+1) == 0)
{// right pixel
neighborPixels.push(pair<int,int>(curX, curY+1)) ;
}
if (_lableImg.at<int>(curX-1, curY) == 0)
{// up pixel
neighborPixels.push(pair<int,int>(curX-1, curY)) ;
}
if (_lableImg.at<int>(curX+1, curY) == 0)
{// down pixel
neighborPixels.push(pair<int,int>(curX+1, curY)) ;
}
}
}
}
}
}
int main(int argc, char** argv)
{
Mat binImage = imread("D:\\VS2012\\Work\\test7\\test7\\9.jpg", 0) ; //你自己改地址
imshow("原图", binImage) ;
threshold(binImage, binImage, 50,255, CV_THRESH_BINARY_INV) ;
Mat labelImg ;
icvprCcaBySeedFill(binImage, labelImg) ;
Mat colorLabelImg ;
icvprLabelColor(labelImg, colorLabelImg) ;
imshow("区域标记", colorLabelImg) ;
waitKey(0) ;
return 0 ;
}
相关文章推荐
- OpenCV:二值图像连通区域分析与标记算法实现
- 连通区域标记:c++版的bwlabel实现(基于opencv)
- 单次扫描完成二值图连通区域标记
- OpenCV_连通区域分析与标记(Connected Component Analysis-Labeling)
- OpenCV 基于轮廓提取的二值图像分析与连通区域标记算法
- 使用opencv编写函数 删除二值图像中面积较小的连通区域
- OpenCV:二值图像连通区域分析与标记算法实现
- OpenCV_连通区域分析与标记
- OpenCV_连通区域分析与标记(Connected Component Analysis-Labeling)
- OpenCV_连通区域分析与标记(Connected Component Analysis-Labeling
- OpenCV:二值图像连通区域分析与标记算法实现
- 二值图像连通区域标记(OpenCV版)
- 单次扫描完成二值图连通区域标记
- C++ 二值图像连通区域标记
- opencv 图片最大连通区域识别
- opencv统计图像不同区域面积
- opencv 分水岭 连通区域
- 学习OpenCV——肤色检测:椭圆模型 (&最大连通区域)
- bwlabel(BW,n) matlab中标记二值图像中白色(1)的连通区域