OpenCV:曲线的检测与提取-0
1、基本思想:
给出一个带有曲线像素宽度为1的二值图像;
①遍历图像像素,找到第一个白色的点;
②在此点的八邻域内找到下一个白色的点;
③将找到的点的灰度值置为0;保存找到点的坐标;
④八邻域内没有找到白色点,则表示一端搜索完成;
原图如下:

2、【注】:
①第一个找到的点不一定是曲线的端点,
因此应该分别向两边寻找相邻的点;
②在搜寻相邻像素点时,
会首先搜寻此点与上一个点相邻像素相对的位置;
这样可以减少寻找的次数,而且当有相交的曲线时,能连接到我们一般认为的曲线。

3、程序如下:
//寻找二值图像上的第一个点
//maskImg二值图像,标记
//outPoint输出查找的点
bool findFirstPoint(Mat &maskImg, Point &outPoint)
{
bool success = false;
for (int i = 0; i < maskImg.rows; i++)
{
unsigned char *pData = (unsigned char*)(maskImg.data+i*maskImg.step);
for (int j = 0; j < maskImg.cols; j++)
{
if (pData[j] == 255)//找到第一个白色的点
{
success = true;
outPoint.x = j;
outPoint.y = i;
pData[j] = 0;//将此点像素设为0
break;
}
}
if (success)
break;
}
return success;
}

4、//八邻域中寻找曲线上某个点的下一个点
//neiPoint八邻域点
//currPt当前点
bool findNextPoint(vector<Point> &neiPt, Mat &image, Point currPt, int currFlag, Point& outPt, int &outFlag)
{
int i = currFlag;//0,6
int count = 1;
bool success = false;
while (count <= 7)
{
Point tpPt = currPt + neiPt[i];//邻域像素点
if (tpPt.x > 0 && tpPt.y > 0 && tpPt.x < image.cols && tpPt.y < image.rows)//在图像内部
{
if( ((unsigned char*)(image.data+tpPt.y*image.step))[tpPt.x]==255 )
{
outPt = tpPt;
outFlag = i;
success = true;
((unsigned char*)(image.data+tpPt.y*image.step))[tpPt.x]=0;
break;//跳出循环
}
}
if (count % 2)//奇数
{
i += count;
if (i > 7)
{
i -= 8;
}
}
else
{
i += -count;
if (i < 0)
{
i += 8;
}
}
count++;
}
return success;
}

5、//寻找曲线 :
void findLines(Mat &binaryImg, vector<deque<Point>> &outLines)
{
//八邻域
vector<Point> neighborPtVec;
neighborPtVec.push_back(Point(-1,-1));
neighborPtVec.push_back(Point(0,-1));
neighborPtVec.push_back(Point(1,-1));
neighborPtVec.push_back(Point(1,0));
neighborPtVec.push_back(Point(1,1));
neighborPtVec.push_back(Point(0,1));
neighborPtVec.push_back(Point(-1,1));
neighborPtVec.push_back(Point(-1,0));
Point firstPt;
while (findFirstPoint(binaryImg, firstPt))
{
deque<Point> line;//点的队列
line.push_back(firstPt);//存储第一个点
//由于第一个点不一定是线段的起始位置,
//因此两个方向都要查找
Point currPt = firstPt;//当前点
int currFlag = 0;//标志
Point nextPt;//下一个点
int nextFlag;//下一点的标志
while (findNextPoint(neighborPtVec, binaryImg, currPt, currFlag, nextPt, nextFlag))//一端
{
line.push_back(nextPt);//压入队列
currPt = nextPt;
currFlag = nextFlag;
}
//找另一端
currPt = firstPt;
currFlag = 0;
while (findNextPoint(neighborPtVec, binaryImg, currPt, currFlag, nextPt, nextFlag))
{
line.push_front(nextPt);
currPt = nextPt;
currFlag = nextFlag;//邻域与中心像素的位置
}
if (line.size() > 10)
{
outLines.push_back(line);
}
}
}

6、//主函数main:
//用deque<Point> 描述曲线
//随机取色画曲线
Scalar random_color(RNG& _rng)
{
int icolor = (unsigned)_rng;
return Scalar(icolor & 0xFF, (icolor >> 8) & 0xFF, (icolor >> 16) & 0xFF);
}
int main()
{
Mat binaryImg = imread("1.jpg",0);
vector<deque<Point>> lines;
findLines(binaryImg, lines);
cout << lines.size() << endl;
//draw lines
Mat draw_img;
cvtColor(binaryImg,draw_img,CV_GRAY2BGR);
RNG rng(123);
Scalar color;
for (int i = 0; i < lines.size(); i++)
{
color = random_color(rng);
for (int j = 0; j < lines[i].size(); j++)
{
draw_img.at<Vec3b>(lines[i][j]) = Vec3b(color[0], color[1], color[2]);
}
}
imshow("draw_img", draw_img);
imwrite("draw_img.jpg", draw_img);
waitKey(0);
return 0;
}

