测量图像中痕迹的平均厚度

时间:2010-09-17 13:33:43

标签: algorithm image language-agnostic image-processing computer-vision

问题在于:我有许多由不同厚度的痕迹组成的二进制图像。下面有两张图片来说明问题:

第一张图片 - 尺寸:711 x 643 px

711 x 643 example image

第二张图片 - 尺寸:930 x 951像素

alt text

我需要的是测量图像中迹线的平均厚度(以像素为单位)。事实上,图像中痕迹的平均厚度是一种有点主观的度量。所以,我需要的是一个与迹线半径有一定关系的度量,如下图所示:

alt text

备注

  • 由于测量不需要非常精确,我愿意以精确度换取速度。换句话说,速度是解决这个问题的重要因素。

  • 痕迹中可能存在交叉点。

  • 迹线厚度可能不是恒定的,但平均测量值是可以的(即使最大迹线厚度也可以接受)。

  • 跟踪总是比它宽得多。

7 个答案:

答案 0 :(得分:19)

我建议使用这个算法:

  1. 对图像应用距离变换,以便将所有背景像素设置为0,将所有前景像素设置为距背景的距离
  2. 在距离变换图像中查找局部最大值。这些是线条中间的点。将它们的像素值(即距背景的距离)放入列表
  3. 计算该列表的中位数或平均值

答案 1 :(得分:8)

@ nikie的回答给我留下了深刻的印象,并试了一下......

我简化了算法以获得最大值,而不是平均值,因此避免了局部最大值检测算法。我认为如果中风表现良好就足够了(尽管对于自相交线,它可能不准确)。

Mathematica中的程序是:

m = Import["http://imgur.com/3Zs7m.png"]   (* Get image from web*)
s = Abs[ImageData[m] - 1];                 (* Invert colors to detect background *)
k = DistanceTransform[Image[s]]            (* White Pxs converted to distance to black*)
k // ImageAdjust                           (* Show the image *)
Max[ImageData[k]]                          (* Get the max stroke width *)

生成的结果是

alt text

数值(28.46像素X 2)非常适合我56 px的测量值(尽管你的值是100px:*)

编辑 - 实施完整算法

嗯......有点......而不是搜索局部最大值,找到距离变换的固定点。几乎,但不完全不同于同样的事情:)

m = Import["http://imgur.com/3Zs7m.png"];   (*Get image from web*)
s = Abs[ImageData[m] - 1];         (*Invert colors to detect background*)
k = DistanceTransform[Image[s]];   (*White Pxs converted to distance to black*)
Print["Distance to Background*"]
k // ImageAdjust                   (*Show the image*)
Print["Local Maxima"]
weights = 
    Binarize[FixedPoint[ImageAdjust@DistanceTransform[Image[#], .4] &,s]]  
Print["Stroke Width =", 
     2 Mean[Select[Flatten[ImageData[k]] Flatten[ImageData[weights]], # != 0 &]]]

alt text

正如您所看到的,结果与前一个结果非常相似,使用简化算法获得。

答案 2 :(得分:4)

来自Here。一个简单的方法!

3.1 估算笔宽度

可以从前景的区域A和周长L来容易地估计笔的厚度

T = A/(L/2)

本质上,我们将前景重新塑造成一个矩形并测量最长边的长度。笔的更强的建模,例如,作为产生圆形末端的盘,可以允许更高的精度,但是光栅化误差会损害重要性。

虽然精确度不是主要问题,但我们确实需要考虑偏差和奇点。

因此,我们应该使用考虑“圆度”的函数来计算面积A和周长L. 在MATLAB中

A = bwarea(.)  
L = bwarea(bwperim(.; 8))

由于我手头没有MATLAB,我在Mathematica做了一个小程序:

m = Binarize[Import["http://imgur.com/3Zs7m.png"]] (* Get Image *)
k = Binarize[MorphologicalPerimeter[m]]            (* Get Perimeter *)
p = N[2 Count[ImageData[m], Except[1], 2]/ 
    Count[ImageData[k], Except[0], 2]]             (* Calculate *)

输出为36 Px ...

周边图像如下

alt text

HTH!

答案 3 :(得分:2)

问题问题已经过了3年:) 按照@nikie的程序,这是一个笔画宽度的matlab实现。

 clc;
 clear;
 close all;


I = imread('3Zs7m.png');
X = im2bw(I,0.8);

subplottight(2,2,1);
imshow(X);

Dist=bwdist(X);

subplottight(2,2,2);
imshow(Dist,[]);

RegionMax=imregionalmax(Dist);

[x, y] = find(RegionMax ~= 0);
subplottight(2,2,3);
imshow(RegionMax);

List(1:size(x))=0;
for i = 1:size(x) 
List(i)=Dist(x(i),y(i));
end

fprintf('Stroke Width = %u \n',mean(List));

答案 4 :(得分:1)

假设迹线具有恒定的厚度,比它宽得多,不是太强烈弯曲并且没有交叉点/交叉点,我建议边缘检测算法也确定边缘的方向,然后是上升/具有一些三角法和最小化算法的跌倒检测器。这使您在曲线的相对直线部分获得最小厚度。

我猜这个错误高达25%。

首先使用边缘检测器,它为我们提供边缘所在的信息以及它具有的方向(45°或PI / 4步)。这是通过使用4个不同的3x3矩阵(Example)进行过滤来完成的 通常我会说它足以水平扫描图像,但你也可以垂直或对角扫描 假设逐行(水平)扫描,一旦我们找到边缘,我们检查它是否是上升(从背景到痕迹颜色)或下降(到背景)。如果边缘的方向与扫描方向成直角,请跳过它 如果您发现一个上升,一个下降且方向正确且两者之间没有任何干扰,请测量从上升到下降的距离。如果方向是对角线,则乘以2的平方根。将此度量与坐标数据一起存储。

然后,算法必须沿着边缘(当前无法找到网络资源)搜索相邻(通过其坐标)测量。如果存在局部最小值,每边可能有4到5个大小单位的填充(要使用的值 - 更大:信息越少,越小:噪声越大),此度量就有资格作为候选者。这是为了确保不考虑路径的末端或弯曲过多的部分。

最小值是测量值。合理性检查:如果迹线不是太纠结,那么该区域应该有很多值。

如果还有其他问题,请发表评论。 : - )

答案 5 :(得分:1)

这是一个适用于任何计算机语言而无需特殊功能的答案......

基本思路:尝试将圆圈装入图像的黑色区域。如果可以,请尝试更大的圈子。

算法:

  • 设置图像背景= 0和跟踪= 1
  • 初始化数组结果[]
  • 设置minimalExpectedWidth
  • 设置w = minimalExpectedWidth
    • set counter = 0
    • 创建一个零大小的矩阵w x w
    • 在该矩阵的直径w的圆内,放一个
    • 计算圆的面积(= PI * w)
    • 循环遍历图像的所有像素
      • 优化:如果当前像素是背景颜色 - >继续循环
      • 将矩阵与每个像素处的图像相乘(例如,用该矩阵过滤图像) (你可以使用当前的x和y位置以及从0到w的双循环)
      • 取每次乘法结果的总和
      • 如果总和等于计算的圆的面积,则递增计数器
    • 存储在结果[w - minimalExpectedWidth]
    • 将w增加一个
    • 优化:包括来自此处的算法
  • 而计数器大于零

现在结果数组包含每个测试宽度的匹配数 用图表来看看它。
对于宽度为1,这将等于迹线颜色的像素数。对于更大的宽度值,更少的圆形区域将适合迹线。结果数组将逐渐减少,直到突然下降。这是因为具有该宽度的圆形区域的滤波器矩阵现在仅适合交叉点 在下降之前是跟踪的宽度。如果宽度不是恒定的,那么下降就不会那么突然。

我这里没有MATLAB进行测试,并且不确定检测到这种突然下降的函数,但是我们确实知道减少是连续的,所以我采取了最大的二阶导数这样的(从零开始)结果数组

算法:

  • 设置最大值= 0
  • set widthFound = 0
  • 如上所述设置minimalExpectedWidth
  • 设置prevvalue = result [0]
  • set index = 1
  • 设置prevFirstDerivative = result [1] - prevvalue
  • 循环,直到index为更大的结果长度
    • firstDerivative = result [index] - prevvalue
    • set secondDerivative = firstDerivative - prevFirstDerivative
    • if secondDerivative>最大值或第二度值<最大* -1
      • maximum = secondDerivative
      • widthFound = index + minimalExpectedWidth
    • prevFirstDerivative = firstDerivative
    • prevvalue =结果[index]
    • 将索引增加一个
  • return widthFound

现在,widthFound是跟踪宽度,相对于宽度+ 1,找到了更多匹配。

我知道其他一些答案部分涵盖了这一点,但我的描述非常简单,您无需学习图像处理。

答案 6 :(得分:0)

我有一个有趣的解决方案:

  1. 进行边缘检测,用于边缘像素提取。
  2. 进行物理模拟 - 将边缘像素视为带正电粒子。
  3. 现在在中风区域放置了一些带正电荷的粒子。
  4. 计算用于确定这些自由粒子运动的电力方程式。
  5. 模拟粒子运动一段时间,直到粒子达到位置平衡。 (因为他们将在一段时间后从两个前缘击退,他们将留在中间线)
  6. 现在行程厚度/ 2将为average distance from edge particle to nearest free particle.