对于给定离散轨迹 拟合其中存在的直线方程的分析
问题是这样的,有一组扇形位图,求其中每一个扇形的圆心,半径,圆心角
位图类似以下:这是一副一般扫描仪输出的结果
首先是opencv 能帮我们做得,提取图像边缘 效果如图:
void
getEdge(
const
IplImage
*
pImg,IplImage
*
pCannyImg,
int
pos){
IplImage * pGray = cvCreateImage(cvGetSize(pImg),IPL_DEPTH_8U, 1 );
cvCvtColor(pImg,pGray,CV_BGR2GRAY);
cvSmooth(pGray,pCannyImg,CV_BLUR, 3 , 3 , 0 , 0 );
cvNot(pGray,pCannyImg);
cvCanny(pGray,pCannyImg,pos,pos * 3 , 3 );
cvRelease( & pGray);
}
IplImage * pGray = cvCreateImage(cvGetSize(pImg),IPL_DEPTH_8U, 1 );
cvCvtColor(pImg,pGray,CV_BGR2GRAY);
cvSmooth(pGray,pCannyImg,CV_BLUR, 3 , 3 , 0 , 0 );
cvNot(pGray,pCannyImg);
cvCanny(pGray,pCannyImg,pos,pos * 3 , 3 );
cvRelease( & pGray);
}
然后就要对边缘分割来得到一个个扇形,因为这些扇形都是整齐排列的,所以不需要什么封闭轨迹检查,直接根据点的坐标的连续性就可以判定了。不妨设横竖相差大于3个像素点为不同扇形的点。
const
int
hgap
=
3
;
const int vgap = 3 ;
for ( int row = 0 ;row < edge -> height; ++ row){
for ( int col = 0 ;col < edge -> width; ++ col){
const uchar * value = ( const uchar * )edge -> imageData + row * edge -> widthStep + col;
if ( * value == 255 ){
// printf("%d,%d = %d\n",row,col,*value);
points.push_back(std::make_pair(col,row));
}
}
}
Points::const_iterator iter;
Point prev = std::make_pair( 0 , 0 );
for (iter = points.begin();iter != points.end(); ++ iter){
if (iter -> second - prev.second > vgap){
Area area;
areas.push_front(area);
}
areas[ 0 ].push_back( * iter);
prev =* iter;
}
prev = std::make_pair( 0 , 0 );
for (Areas::iterator iter = areas.begin();iter != areas.end(); ++ iter){
std::sort(iter -> begin(),iter -> end());
for (Area::const_iterator iter2 = iter -> begin();iter2 != iter -> end(); ++ iter2){
if ((iter2 -> first - prev.first > hgap) || (iter2 -> first - prev.first < 0 )){
Area area;
result.push_front(area);
}
result[ 0 ].push_back( * iter2);
prev =* iter2;
}
}
const int vgap = 3 ;
for ( int row = 0 ;row < edge -> height; ++ row){
for ( int col = 0 ;col < edge -> width; ++ col){
const uchar * value = ( const uchar * )edge -> imageData + row * edge -> widthStep + col;
if ( * value == 255 ){
// printf("%d,%d = %d\n",row,col,*value);
points.push_back(std::make_pair(col,row));
}
}
}
Points::const_iterator iter;
Point prev = std::make_pair( 0 , 0 );
for (iter = points.begin();iter != points.end(); ++ iter){
if (iter -> second - prev.second > vgap){
Area area;
areas.push_front(area);
}
areas[ 0 ].push_back( * iter);
prev =* iter;
}
prev = std::make_pair( 0 , 0 );
for (Areas::iterator iter = areas.begin();iter != areas.end(); ++ iter){
std::sort(iter -> begin(),iter -> end());
for (Area::const_iterator iter2 = iter -> begin();iter2 != iter -> end(); ++ iter2){
if ((iter2 -> first - prev.first > hgap) || (iter2 -> first - prev.first < 0 )){
Area area;
result.push_front(area);
}
result[ 0 ].push_back( * iter2);
prev =* iter2;
}
}
这样我们就得到了一组扇形的边缘轨迹。因为这些轨迹是无序排列的,所以我们需要求出这个扇形的边缘链码,才能对轨迹进行分析。
又由于扫描得到的图片有明显失真,所以用一般的8领域并不能准确得到链码,我们需要更大的领域矩阵。
求N阶领域函数:
Points getNearPoints(
const
Point
&
point,
int
gap
=
1
){
std::set < Point > s_points;
Points points;
int x(point.first),y(point.second);
for ( int i = gap;i >=- gap; -- i){
for ( int j = gap;j >=- gap; -- j){
s_points.insert(std::make_pair(x + i,y + j));
}
}
for (std::set < Point > ::const_iterator iter = s_points.begin();iter != s_points.end(); ++ iter){
if ( * iter != point)
points.push_back( * iter);
}
return points;
}
这里有一点算法优化,因为可以保证:扇形2条半径中右边的一条必定斜率为0,所以求出右边半径的2个断点很简单。因为斜率为0,即此区间上的微分为0,也即差分为0。所以半径的右断点和圆心为轨迹上最长的差分为0区间的2个端点。
std::set < Point > s_points;
Points points;
int x(point.first),y(point.second);
for ( int i = gap;i >=- gap; -- i){
for ( int j = gap;j >=- gap; -- j){
s_points.insert(std::make_pair(x + i,y + j));
}
}
for (std::set < Point > ::const_iterator iter = s_points.begin();iter != s_points.end(); ++ iter){
if ( * iter != point)
points.push_back( * iter);
}
return points;
}
有了圆心点,再根据链码 向下寻找,得到左边半径的轨迹:
由于直线的二阶导数为0 左端点即为以上轨迹中二阶差分为0的最长区间的左端点
有了2条半径的端点,不难求得此扇形的圆心角。
由于圆形角代表了每一个扇形红色的分度值,所以可以将原图修复如下: