如何计算图像中一组笔画的粗细?

How can I calculate the thickness of a set of strokes in an image?

如何计算附图中每个笔划的粗细?

我对图像中的所有神经进行了分割,并标记了图像中的每个对象。我想知道的是如何计算每根神经的粗细?另外,如果任何神经中有 inflation,我如何突出显示神经的特定部分?

非常感谢您在这方面的帮助。

我想向您指出以下 post:Measuring the average thickness of traces in an image。本质上,图像中只有一个笔划,objective 用于确定此笔划的平均粗细。我们只需为图像中存在的每个笔划重新应用此算法。

基本算法如下(而且很巧妙):

  1. 对于图像中的笔划,将 distance transform 应用到图像,图像将在其中进行变换,以便所有背景像素变为白色,而对象像素变为黑色。因此,我们在图像的 inverse 上应用距离变换。

    距离变换查找图像中的点与最近的非零像素之间的距离。因此,通过将距离变换应用到逆向,对于每个笔划点,这将确定从该特定点到笔划上最近的 边界 点的距离。为什么需要这方面的距离变换,在下一步会讲清楚。

  2. 在距离变换中共享最大距离的笔划上的点定义笔划的中间。您可以将其视为笔画之间的 中途 点。由于浮点运算,在这个最大距离的某个公差范围内收集所有这些距离值。

  3. 找到所有这些距离的平均值或中值,这决定了笔画的宽度....所以如果你想要厚度,只需将结果乘以2.

在继续之前,我假设您可以访问图像处理工具箱,因为距离变换是在作为工具箱一部分的 bwdist 函数中实现的。如果您没有这个工具箱,那么不幸的是我所写的内容将无法使用。这是算法唯一需要的依赖项(当然除了使用 imreadim2bw 之外)。

由于某些原因,当您上传 RGB 格式的图像时,我不得不将您的图像转换为二进制图像,因此我将其制作成单通道二进制图像。因此,您的代码可能如下所示:

%// Read the image from Whosebug and convert to binary
im = im2bw(imread('http://i.stack.imgur.com/DggWW.jpg'));

%// Find all unique contours and assign them a unique ID.  Also determine how many there are
[lbl,N] = bwlabel(im);

%// Initialize an array that determines the thickness of each stroke
thickness = zeros(N,1);

%// Tolerance to collect distances
tol = 1;

%// For each stroke...
for idx = 1 : N

    %// Step #1
    cntr = lbl == idx; %// Get a mask that segments out that stroke only
    d = bwdist(~cntr); %// Find distance transform on inverse

    %// Step #2
    max_dist = max(d(:)); %// Get the maximum distance from stroke to edge
    dists = d(abs(d - max_dist) <= tol); %// Collect those distances within a tolerance

    %// Step #3
    thickness(idx) = 2*mean(dists);  %// Find the average width and multiply by 2 for full width
end

thickness 是一个元素数组,它告诉您图像中每个笔画的宽度或厚度。每个元素 i 告诉您 bwlabel 结果中标签 i 指定的轮廓的厚度。因此,对于 thickness 中的每个元素 i,这记录了在以下位置描绘的笔划的粗细:

cntr = lbl == i;

根据你的图片,我得到以下结果:

thickness =

    3.3555
    3.2494
    3.1545
    3.1773
    3.3282
    3.2607
    3.2710
    3.2772
    3.2948
    3.1607

至于确定神经是否发炎,你需要知道每个笔画的真实厚度是多少,并确定是否有增加。我没有这样的信息,所以我将把它留给你作为练习。我们已经计算了每个笔画的粗细,因此从那里开始,编写一些代码来检测增加并采取相应的行动。


作为额外的奖励,让我们制作一个新的输出图像,我们在其中找到每个笔画的质心,将该笔画放在该输出图像中并打印出其在笔画质心处的粗细。像这样:

imshow(im); hold on;
for idx = 1 : N
    [y,x]= find(lbl == idx);
    cen = mean([x y]);
    text(cen(1), cen(2), ['T = ' num2str(thickness(idx))], 'color', 'red');
end

我们得到:

我的方法使用以下方法解决了这个问题

想法:

对于图片中的每个笔画:

  1. 计算笔划面积
  2. 计算笔划长度
  3. 用面积除以笔划长度计算平均厚度

地区:

计算面积很容易。可以简单地计算像素。

行程长度:

笔划长度比较难。人们也许可以使用图像的骨架 bwmorph(BW,'skel',Inf) 来做到这一点,但是我使用了一种不同的方法。我找到了笔划的周长,并使用一些数学方法找到了一个面积和周长相等的矩形,其边应对应于笔划长度和平均厚度。

计算这样一个笔划的周长,要注意coastline paradox。测量由像素轮廓定义的笔划的实际周长将给出不准确的结果:对角线笔划的周长将被测量为向右 1 像素宽度,向上 1 像素宽度,向右 1 像素宽度,向上 1 像素宽度,。 .. 而不是:sqrt(2) 对角线像素宽度,...

我们可以使用alpha shapes to triangulate our stroke. I use the implementation by Jonas Lundgren on the File Exchange: alphavol来解决这个问题。

最终代码:

image = im2bw(imread('http://i.stack.imgur.com/DggWW.jpg'));    
alphaRadius = 2; %// Could be chosen larger than 2 to get better results, but must not affect the overall area of the triangulation.
%%
[Labeled,N] = bwlabel(image);
thicknesses = zeros(1,N);
lengths = @(Points) sqrt(sum(Points.^2,2));                           

%// Formula for finding an equivalent rectangle:
%// Assume rectangle: Area = a*b, Perimeter = 2*(a+b),
%// solve for a and b, return minimum as thickness
solveForThickness = @(P,A) min(P/4 + (P^2/4 - 4*A)^(1/2)/2, ...
                               P/4 - (P^2/4 - 4*A)^(1/2)/2);
for idx = 1:N
    %// Get current stroke
    stroke = (Labeled == idx);
    [Y,X] = find(stroke);
    %// Get corners of pixels and generate alpha shape
    P = [X-1, Y-1; ...
         X,   Y-1;
         X-1, Y;
         X,   Y];
    [area,S] = alphavol(P,alphaRadius);
    area = nnz(stroke); %// Seems to give better results than triangulated area above
    %// Compute perimeter and thickness
    perimeter = sum(lengths(P(S.bnd(:,1),:)-P(S.bnd(:,2),:)));
    thicknesses(idx) = solveForThickness(perimeter, area);
end

后果:

请记住,此解决方案在很大程度上取决于三角测量与笔划数据的拟合程度。通过选择均匀变化陡度的笔划,可以看到由于海岸线悖论导致的该算法的问题。可能有更好的方法来计算笔画的长度以获得更准确的结果。不过它似乎工作得很好。

使用 alpha 形状的替代方法可以是:

perimeter = getfield(regionprops(stroke,'perimeter'),'Perimeter');

结果也比较准确。