边界提取:根据之前的博客就是用原图减去腐蚀后的图像;
边界跟踪:按照某种扫描规则找到目标边界上的像素直到回到原点;
本程序中,我采用从左到右从上到下的顺序查找边界,如下图所示,分别从左下、下、右下右、右上、上、左上、左方向搜索边界点,当搜索到原点时,边界搜索结束。
当图像中有多个连通域时,显然这种方法就不适用,所以改进的方法是:每搜索完一个边界,将改连通域设置为背景颜色(如:0),然后继续搜索
#include
#include
using namespace std;
using namespace cv;
void erode(const Mat _src, Mat& dst, int se[3][3]);
void dilate(const Mat _src, Mat& dst, int se[3][3]);
/**************************************************
功能:边界跟踪
参数:src-二值图像
***************************************************/
void traceBoundary(Mat src, Mat& dst)
{
//起始边界点和当前边界点
Point ptStart;
Point ptCur;
//搜索方向数组{左下,下,右下,右,右上,上,左上,左}
int Direction[8][2] = { { -1, 1 }, { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 }, { 0, -1 }, { -1, -1 }, { -1, 0 } };
int nCurDirect = 0;//当前探查的方向
//搜索开始,区别ptCur==ptStart的两种情况(一种开始,一种结束)
bool bAtStartPt;
//算法不处理边界上的点,将图像的四周设置为白
//for (int i = 0; i < src.rows; i++)
//{
// dst.at(i, 0) = 255;
// dst.at(i, src.rows - 1) = 255;
//}
//for (int j = 0; j < src.cols; j++)
//{
// dst.at(0, j) = 255;
// dst.at(src.rows - 1, j) = 255;
//}
int xPos, yPos;
//逐行扫描
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
if (src.at(i, j) > 0)
{
ptStart.x = j;
ptStart.y = i;
ptCur = ptStart;
bAtStartPt = true;
while ((ptCur.x != ptStart.x) || (ptCur.y != ptStart.y) || bAtStartPt)
{
bAtStartPt = false;
//下一个探查位置
xPos = ptCur.x + Direction[nCurDirect][0];
yPos = ptCur.y + Direction[nCurDirect][1];
int nSearchTimes = 1;
while (src.at(yPos, xPos) ==0)
{
nCurDirect++;//逆时针旋转45度
if (nCurDirect >= 8)
nCurDirect -= 8;
xPos = ptCur.x + Direction[nCurDirect][0];
yPos = ptCur.y + Direction[nCurDirect][1];
//8领域中都没有边界点,说明是孤立点
if (++nSearchTimes >= 8)
{
xPos = ptCur.x;
yPos = ptCur.y;
break;
}
}
//找到下一个边界点
ptCur.x = xPos;
ptCur.y = yPos;
//在新像上标记边界
dst.at(ptCur.y, ptCur.x) = 255;
/***********
此处可以定义vector存储提取的边界点
************/
//将当前探查方向顺时针回转90度作为下一次的探查初始方向
nCurDirect -= 2;
if (nCurDirect < 0)
{
nCurDirect += 8;
}
}
return;
}
//当存在多个边界时,在此处添加相应代码,并删除return(每跟踪完一个边界,删除相应的区域)
}
}
}
int main()
{
//读取二值图像(此步可以忽略)
Mat src = imread("test1.jpg", 0);
Mat src_binary;
threshold(src, src_binary, 250, 255, THRESH_BINARY);
imshow("原始图像", src_binary);
//创建模板
int se[3][3] = { { -1, 1, -1 }, { 1, 1, 1 }, { 1, 1, 1 } };
//边界提取(原图-腐蚀后的图像)
Mat dstImg;
erode(src_binary, dstImg, se);
dstImg = src_binary - dstImg;
imshow("边界提取后的图像", dstImg);
//边界跟踪
Mat BoundaryImg = Mat::zeros(src_binary.size(), src_binary.type());
traceBoundary(dstImg, BoundaryImg);
imshow("边界图像", BoundaryImg);
waitKey(0);
return 0;
}
边界提取的结果:
边界跟踪后的结果: