如何确定线条的宽度?

时间:2014-02-27 22:25:09

标签: opencv image-processing computer-vision

我需要检测这些线的宽度:

enter image description here

这些线是平行的并且有一些噪音。

目前,我所做的是:

1.使用细化处理中心(ZhangSuen)

ZhanSuenThinning(binImage, thin);

2.计算距离变换

cv::distanceTransform(binImage, distImg, CV_DIST_L2, CV_DIST_MASK_5);

3.累计中心周围的半距离

double halfWidth = 0.0;
int count = 0;
for(int a = 0; a < thinImg.cols; a++)
    for(int b = 0; b < thinImg.rows; b++)
        if(thinImg.ptr<uchar>(b, a)[0] > 0)
        {
            halfWidth += distImg.ptr<float>(b, a)[0];
            count ++;
        }

4.最后,获取实际宽度

width = halfWidth / count * 2;

结果,并不是很好,在1-2像素左右是错误的。在更大的图像上,结果更糟,有什么建议吗?

5 个答案:

答案 0 :(得分:9)

您可以采用条形码阅读器算法,这是更快的方法。

image

扫描水平和垂直线条。 让X表示与黑线的水平交点的长度,Y表示垂直交点的长度(如果有一些噪音,可以计算几个X和Y的中值)。

X * Y / 2 = area
X²+Y² = hypotenuse²
hypotenuse * width / 2 = area

所以:width = 2 * area / hypotenuse

编辑:您也可以使用PCA轻松找到角度。

答案 1 :(得分:4)

你需要的是为你的图像中的每个轮廓找到RotatedRect,这里是OpenCV tutorial如何做到这一点。然后从旋转的矩形中获取“大小”的值,在这里您将获得轮廓的高度和宽度,高度和宽度可以互换以用于轮廓的不同对齐。在上图中,高度变为宽度,宽度变为高度。

Contour-->RotatedRect 
              | 
              '-->  Size2f size
                            |
                            |-->width
                            '-->height

找到轮廓后再做

RotatedRect minRect = minAreaRect( Mat(contours[i]) );
Size2f contourSize=minRect.size  //  width and height of the rectangle

每个轮廓的旋转矩形

enter image description here

这是C ++代码

Mat src=imread("line.png",1);
Mat thr,gray;
blur(src,src,Size(3,3));
cvtColor(src,gray,CV_BGR2GRAY);
Canny(gray,thr,50, 190, 3, false );
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( thr.clone(),contours,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE,Point(0,0));
vector<RotatedRect> minRect( contours.size() );

for( int i = 0; i < contours.size(); i++ )
  minRect[i] = minAreaRect( Mat(contours[i]) );

for( int i = 0; i< contours.size(); i++ )
 {
    cout<<"  Size ="<<minRect[i].size<<endl; //The width may interchange according to contour alignment
    Size2f s=minRect[i].size;
    // rotated rectangle
    Point2f rect_points[4]; minRect[i].points( rect_points );
    for( int j = 0; j < 4; j++ )
      line( src, rect_points[j], rect_points[(j+1)%4], Scalar(0,0,255), 1, 8 );
}

imshow("src",src);
imshow("Canny",thr);

答案 2 :(得分:2)

  1. 霍夫线适合找到每一行
  2. 从每条线上的每个像素拟合,沿垂直方向扫描以获得到边缘的距离。使用样条拟合或类似的子像素方法查找边缘。
  3. 根据您的需要/愿望,取中距离或平均距离。为了消除异常值的问题,在计算平均值或中位数之前,将距离丢弃到第10百分位数以下和第90百分位数以上。您还可以使用统计信息报告大小:线宽W,标准差S。
  4. 尽管可以使用连通分量算法来查找线条,但它不会像样条拟合那样找到“真实”边缘。

答案 3 :(得分:1)

一个简单明了的建议:

  • 计算黑色像素的总数。

  • 检测每条线的长度。 (可能使用CVHoughLinesP,或者只是每条细化线周围的边界框的对角线)

  • 将黑色像素数除以所有线长的总和,这样就可以得到平均线宽。

我不确定这是否比现有方法更准确。每行的不规则末端部分可能会抛出它。

您可以尝试的一件事可以提高该案例的准确性:

  • 测量线的平均角度
  • 旋转图像以使线条水平对齐
  • 裁剪您的形状的矩形子部分,因此所有线条的长度相同 (你可以通过形态学闭合来获得形状的轮廓,然后找到一个完全包含在形状中的矩形。确保矩形的水平边缘位于线之间)
  • 然后再次计算黑色像素的数量(计算由于将图像旋转为整个像素的x%而导致的灰色像素)
  • 除以(rectangle_width * number_of_lines_in_rectangle)

答案 4 :(得分:0)

您所显示的图像有噪音/模糊,因此黑色像素的数量可能无法反映线条属性;例如,黑色像素可以部分归因于椒盐噪声。你可以通过形态侵蚀摆脱它,但这也会影响你的线条。

更好的方法是提取连通分量,删除可能来自噪声或小斑点的小分量,然后计算像素数并将其除以行数。这种方法还可以帮助您分析图像中对象的形状,并除去噪声或线条以外的任何伪像。

当您有一些接近线条边框的灰色像素时,不同的真实字词情况。您可以使用阈值来丢弃它们,也可以使用一些权重<1来计算它们。这将补偿图像中的模糊。顺便说一下,图像的旋转可能会增加模糊,因为它通常通过插值和平滑来完成。