上研究生时写的代码,为了防止时间久了丢失,将它贴到网上。
头文件:
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include <vector>
using namespace std;
class ConCompLabel
{
private:
//传入参数(同时也是传出参数,最终的标记图)
IplImage* bwImg;
//内部参数
int bwHeight;
int bwWidth;
int boundWidth; //边界宽度
vector<int> labelTabel; //标记表
int labelNum; //当前的最大标记号
IplImage* boundImg; //包含边框的图像
//传出参数
vector<int> compSize; //标记完后,各连接部件的面积
int maxSize; //最大块的面积
int maxLabel; //最大块的标号
IplImage* maxImg; //只包含最大块的图像
CvRect maxCompRect; //最大块的外接矩形
public:
ConCompLabel(IplImage* bwImg0);
~ConCompLabel();
int option();
int getLabelImg() const {return bwImg;}
int getMaxImg() const {return maxImg;}
CvRect getMaxCompRect() const {return maxCompRect;}
private:
int addBound();
int removeBound();
int scan();
int adjast(); //可得到最终的标记图像
int link(int leftLabel, int rightLabel);
int calcCompSize();
int selectMaxImg();
int calcmaxCompRect();
};
cpp文件:
#include "ConCompLabel.h"
ConCompLabel::ConCompLabel(IplImage* bwImg0)
{
bwImg = bwImg0;
boundWidth = 1;
bwHeight = bwImg0->height;
bwWidth = bwImg0->width;
labelNum = 0; //!!务必清零
labelTabel.push_back(labelNum); //将向量的第一个元素的标记变为0
//为了处理边界问题,在源图像周围加一圈
boundImg = cvCreateImage(cvSize(width + 2 * boundWidth,
bwHeight + 2 * boundWidth), IPL_DEPTH_8U, 1);
cvZero(boundImg);
maxImg = cvCreateImage(cvSize(width, bwHeight), IPL_DEPTH_8U, 1);
cvZero(maxImg);
compSize.push_back(0); //没有标记为0的块,因此标记为0的像素总数为0
maxSize = 0;
}
ConCompLabel::~ConCompLabel()
{
cvReleaseImage(&boundImg);
cvReleaseImage(&maxImg);
}
//给图像增加一圈背景像素,以处理边界问题
//传入图像必须为8位灰度的
int ConCompLabel::addBound()
{
for (int i = boundWidth; i < bwHeight + boundWidth; i++)
{
for (int j = boundWidth; j < width + boundWidth; j++)
{
CV_IMAGE_ELEM(boundImg, uchar, i, j) =
CV_IMAGE_ELEM(bwImg, uchar, i - boundWidth, j - boundWidth);
}
}
return 0;
}
//参考文献:Suzuki, Fast connected component labeling, pattern recognition, 42(2009), 1977-1987.
int ConCompLabel::scan()
{
for (int i = boundWidth; i < bwHeight + boundWidth; i++)
{
for (int j = boundWidth; j < width + boundWidth; j++)
{
if (CV_IMAGE_ELEM(boundImg, uchar, i, j))
{
int c1 = CV_IMAGE_ELEM(boundImg, uchar, i, j - 1);
int c2 = CV_IMAGE_ELEM(boundImg, uchar, i - 1, j - 1);
int c3 = CV_IMAGE_ELEM(boundImg, uchar, i - 1, j);
int c4 = CV_IMAGE_ELEM(boundImg, uchar, i - 1, j + 1);
if (c3)
CV_IMAGE_ELEM(boundImg, uchar, i, j) = c3;
else if (c1)
{
CV_IMAGE_ELEM(boundImg, uchar, i, j) = c1;
if (c4)
{
link(c1, c4);
}
}
else if (c2)
{
CV_IMAGE_ELEM(boundImg, uchar, i, j) = c2;
if (c4)
{
link(c2, c4);
}
}
else if (c4)
CV_IMAGE_ELEM(boundImg, uchar, i, j) = c4;
else
{
//在final_label中添加新的标记
CV_IMAGE_ELEM(boundImg, uchar, i, j) = ++labelNum;
labelTabel.push_back(labelNum);
}
}
}//for j
}//for i
return 0;
}
//第二次扫描
int ConCompLabel::adjast()
{
for (int i = boundWidth; i < bwHeight + boundWidth; i++)
{
for (int j = boundWidth; j < width + boundWidth; j++)
{
if (CV_IMAGE_ELEM(boundImg, uchar, i, j))
{
CV_IMAGE_ELEM(boundImg, uchar, i, j) =
labelTabel[CV_IMAGE_ELEM(boundImg, uchar, i, j)];
}//if
}//for j
}//for i
return 0;
}
int ConCompLabel::removeBound()
{
for (int i = boundWidth; i < bwHeight + boundWidth; i++)
{
for (int j = boundWidth; j < width + boundWidth; j++)
{
CV_IMAGE_ELEM(bwImg, uchar, i - boundWidth, j - boundWidth) =
CV_IMAGE_ELEM(boundImg, uchar, i, j);
}
}
return 0;
}
//两种标记之间存在连接时,调整标记表
int ConCompLabel::link(int leftLabel, int rightLabel)
{
int srcLabel = labelTabel[leftLabel];
int srcLabel = labelTabel[rightLabel];
//此时,c2和c4之间存在连接,将final_label中标记为c2的全改为c4
for (int t = 1; t <= labelTabel.size(); t++)
{
if (labelTabel[t] == srcLabel)
labelTabel[t] = srcLabel;
}
return 0;
}
//计算各连接块的面积
int ConCompLabel::calcCompSize()
{
//统计各块的面积
for (int i = 0; i < bwHeight; i++)
for (int j = 0; j < bwWidth; j++)
{
int index = CV_IMAGE_ELEM(bwImg, uchar, i, j);
if (index)
{
//如果标记值在面积向量中尚未出现,则入栈,相应面积值为0
if (index > compSize.size() - 1)
{
for (int t = compSize.size(); t <= index; t++)
{
compSize.push_back(0);
}
}
//面积值加1
compSize[index] ++;
}
}
//搜寻最大面积的块
maxSize = 0;
for (int i = 1; i <= compSize.size(); i++)
{
if (compSize[i] > maxSize)
{
maxSize = compSize[i];
maxLabel = i;
}
}
return 0;
}
//滤除其它块,只保留最大面积块
int ConCompLabel::selectMaxImg()
{
for (int i = 0; i < bwHeight; i++)
for (int j = 0; j < bwWidth; j++)
CV_IMAGE_ELEM(maxImg, uchar, i, j) =
(CV_IMAGE_ELEM(bwImg, uchar, i, j) == maxLabel) ? 255 : 0;
return 0;
}
//计算最大连接块的外接矩形
int ConCompLabel::calcmaxCompRect()
{
int minX = bwWidth - 1;
int maxX = 0;
int minY = bwHeight - 1;
int maxY = 0;
for (int i = 0; i < bwHeight; i++)
{
for (int j = 0; j < bwWidth; j++)
{
if (CV_IMAGE_ELEM(img_max_block, uchar, i, j))
{
minX = j < minX ? j : minX;
maxX = j > maxX ? j : maxX;
minY = i < minY ? i : minY;
maxY = i > maxY ? i : maxY;
}// if
}// for j
}//for i
maxCompRect.x = minX;
maxCompRect.y = minY;
maxCompRect.width = maxX - minX + 1;
maxCompRect.height = maxY - minY + 1;
return 0;
}
int ConCompLabel::option()
{
addBound();
scan();
adjast();
removeBound();
calcCompSize();
selectMaxImg();
calcmaxCompRect();
return 0;
}