也许可以尝试使用距离变换和脊检测这种方法:
cv::Mat input = cv::imread("fitLine.jpg");
cv::Mat gray;
cv::cvtColor(input,gray,CV_BGR2GRAY);
cv::Mat mask = gray>100;
cv::imshow("mask",mask);
cv::Mat dt;
cv::distanceTransform(mask,dt,CV_DIST_L1,CV_DIST_MASK_PRECISE);
cv::imshow("dt", dt/15.0f);
cv::imwrite("fitLineOut.png",255*dt/15.0f);
//care: this part doesn't work for diagonal lines, a ridge detection would be better!!
cv::Mat lines = cv::Mat::zeros(input.rows, input.cols, CV_8UC1);
//only take the maxDist of each row
for(unsigned int y=0; y<dt.rows; ++y)
{
float biggestDist = 0;
cv::Point2i biggestDistLoc(0,0);
for(unsigned int x=0; x<dt.cols; ++x)
{
cv::Point2i current(x,y);
if(dt.at<float>(current) > biggestDist)
{
biggestDist = dt.at<float>(current) ;
biggestDistLoc = current;
}
}
lines.at<unsigned char>(biggestDistLoc) = 255;
}
//and the maxDist of each row
for(unsigned int x=0; x<dt.cols; ++x)
{
float biggestDist = 0;
cv::Point2i biggestDistLoc(0,0);
for(unsigned int y=0; y<dt.rows; ++y)
{
cv::Point2i current(x,y);
if(dt.at<float>(current) > biggestDist)
{
biggestDist = dt.at<float>(current) ;
biggestDistLoc = current;
}
}
lines.at<unsigned char>(biggestDistLoc) = 255;
}
cv::imshow("max", lines);
cv::waitKey(-1);
这个想法是计算距离变换并找到轮廓内的脊。
this is how the distance transformed image looks like:
you can see that there is a local ridge maximum in the middle of the lines.
然后我使用一个非常简单的方法:只需找到每行/列中的最大距离。这是非常草率的,应该改变为真正的脊检测或细化方法!!!!
edit:补充说明:
这个想法是找到轮廓“中间”的所有点。在数学/图形中,中轴位于物体的某种“中间”,它的定义是同时到至少两个轮廓点具有相同最小距离的所有点。
近似中轴的一种方法是计算距离变换。距离变换是一个矩阵,它保存每个像素到下一个对象点(例如对象的轮廓)的距离(请参阅http://en.wikipedia.org/wiki/Distance_transform http://en.wikipedia.org/wiki/Distance_transform也)。这是第一张图片。在那里,您可以看到线条中间的点比靠近边界的点稍微亮一点,这意味着线条上最亮的点可以解释为中轴(近似值),因为如果您移动远离它(与线方向正交),距离变小,因此峰值是到两个边界的距离接近相等的点。
这样,如果您可以在距离变换中找到那些“脊”,那么您就完成了。
脊线检测通常由 Harris 操作员完成(参见http://en.wikipedia.org/wiki/Ridge_detection http://en.wikipedia.org/wiki/Ridge_detection ).
在我发布的快速而肮脏的版本中,我尝试通过仅接受每行和每行中的最大值来检测山脊。这对于大多数水平和垂直的山脊来说是可以的,但对于对角的山脊来说就不行了。所以也许你真的想交换for loops
with a real ridge detection
.