在图像中寻找斑马状图案(从照片中检测结构光条纹中心线)

信息处理 图像处理 峰值检测 图像分割
2022-01-11 06:29:33

我正在从事一个项目,该项目将边缘投射到一个主题上,并拍摄了一张照片。任务是找到条纹的中心线,这些中心线在数学上表示条纹平面和对象表面之间的 3D 相交曲线。

这张照片是 PNG (RGB),之前的尝试使用灰度化和差异阈值处理来获得黑白的“斑马状”照片,从中很容易找到每个条纹的每个像素列的中点。问题是,通过阈值化以及离散像素列的平均高度,我们会遇到一些精度损失和量化,这根本不是我们想要的。

通过查看图像,我的印象是,如果通过某种统计扫描方法直接从非阈值图像(RGB 或灰度)中检测到中心线,则中心线可能更连续(更多点)和更平滑(未量化) (一些泛滥/迭代卷积,无论如何)。

下面是一个实际的示例图像:

在此处输入图像描述

任何建议将不胜感激!

4个回答

我建议以下步骤:

  1. 找到一个阈值将前景与背景分开。
  2. 对于二值图像中的每个斑点(一条斑马条纹),对于每个x,找到方向上的加权中心(按像素强度)y
  3. 可能,平滑这些y值,以消除噪音。
  4. (x,y)通过拟合某种曲线来连接点。这篇文章可能会对你有所帮助。在我看来,你也可以拟合一个高级多项式,尽管它更糟。

这是一个显示步骤 1,2 和 4 的 Matlab 代码。我跳过了自动阈值选择。相反,我选择了手册th=40

这些是通过找到每列的加权平均值找到的曲线: 在此处输入图像描述

这些是拟合多项式后的曲线: 在此处输入图像描述

这是代码:

function Zebra()
    im = imread('http://i.stack.imgur.com/m0sy7.png');
    im = uint8(mean(im,3));

    th = 40;
    imBinary = im>th;
    imBinary = imclose(imBinary,strel('disk',2));
    % figure;imshow(imBinary);
    labels = logical(imBinary);
    props =regionprops(labels,im,'Image','Area','BoundingBox');

    figure(1);imshow(im .* uint8(imBinary));
    figure(2);imshow(im .* uint8(imBinary));

    for i=1:numel(props)
        %Ignore small ones
        if props(i).Area < 10
            continue
        end
        %Find weighted centroids
        boundingBox = props(i).BoundingBox;
        ul = boundingBox(1:2)+0.5;
        wh = boundingBox(3:4);
        clipped = im( ul(2): (ul(2)+wh(2)-1), ul(1): (ul(1)+wh(1)-1) );
        imClip = double(props(i).Image) .* double(clipped);
        rows = transpose( 1:size(imClip,1) );
        %Weighted calculation
        weightedRows  = sum(bsxfun(@times, imClip, rows),1) ./ sum(imClip,1);
        %Calculate x,y
        x = ( 1:numel(weightedRows) ) + ul(1) - 1;
        y = ( weightedRows ) + ul(2) - 1;
        figure(1);
        hold on;plot(x,y,'b','LineWidth',2);
        try %#ok<TRYNC>
            figure(2);
            [xo,yo] = FitCurveByPolynom(x,y);
            hold on;plot(xo,yo,'g','LineWidth',2);
        end
        linkaxes( cell2mat(get(get(0,'Children'),'Children')) )
    end        
end

function [xo,yo] = FitCurveByPolynom(x,y)
   p = polyfit(x,y,15); 
   yo = polyval(p,x);
   xo = x;
end

我不会使用 RGB 图像。彩色图像通常是通过在相机传感器上放置“拜耳滤镜”来制作的,这通常会降低您可以达到的分辨率。

如果您使用灰度图像,我认为您描述的步骤(二值化“斑马”图像,找到中线)是一个好的开始。作为最后一步,我会

  • 取你找到的中线的每个点
  • 取上下“斑马线”中像素的灰度值
  • 使用最小均方将抛物线拟合到这些灰度值
  • 这条抛物线的顶点是对中线位置的改进估计

这是通过将您的问题建模为“路径优化问题”来解决您的问题的替代解决方案。虽然它比简单的二值化然后曲线拟合解决方案更复杂,但它在实践中更加稳健。

从非常高的层次来看,我们应该将这张图片视为一个图形,其中

  1. 每个图像像素都是该图上的一个节点

  2. 每个节点都连接到其他一些节点,称为邻居,这种连接定义通常被称为该图的拓扑。

  3. 每个节点都有一个权重(特征、成本、能量或任何你想称之为的),反映了这个节点处于我们正在寻找的最优中心线的可能性。

只要我们可以对这种可能性进行建模,那么您寻找“边缘中心线”的问题就变成了在图上找到局部最优路径的问题,这可以通过动态规划(例如维特比算法)有效地解决。

以下是采用这种方法的一些优点:

  1. 您的所有结果都将是连续的(与可能将一条中心线分成几块的阈值方法不同)

  2. 构建这样的图有很多自由度,可以选择不同的特征,以及图拓扑。

  3. 从路径优化的角度来看,您的结果是最佳的

  4. 您的解决方案将对噪声更加稳健,因为只要噪声在所有像素之间均匀分布,这些最佳路径就会保持稳定。

这是上述想法的简短演示。由于我不使用任何先验知识来指定可能的起始节点和结束节点,因此我只是对每个可能的起始节点进行解码。 解码维特比路径

对于模糊结尾,这是由于我们正在为每个可能的结尾节点寻找最佳路径这一事实引起的。结果,尽管对于位于黑暗区域的某些节点,突出显示的路径仍然是其局部最优路径。

对于模糊路径,您可以在找到它之后对其进行平滑处理,或者使用一些平滑特征来代替原始强度。

可以通过更改起始节点和结束节点来恢复部分路径。

修剪这些不想要的局部最优路径并不难。因为我们有维特比解码后所有路径的可能性,并且您可以使用各种先验知识(例如,我们看到对于共享相同源的那些我们只需要一个最佳路径是真的。)

更多细节,你可以参考论文。

 Wu, Y.; Zha, S.; Cao, H.; Liu, D., & Natarajan, P.  (2014, February). A Markov Chain Line Segmentation Method for Text Recognition. In IS&T/SPIE 26th Annual Symposium on Electronic Imaging (DRR), pp. 90210C-90210C.

这是用于制作上图的一小段 python 代码。


import cv2
import numpy as np
from matplotlib import pyplot
# define your image path
image_path = ;
# read in an image
img = cv2.imread( image_path, 0 );
rgb = cv2.imread( image_path, -1 );

# some feature to reflect how likely a node is in an optimal path
img = cv2.equalizeHist( img ); # equalization
img = img - img.mean(); # substract DC
img_pmax = img.max(); # get brightest intensity
img_nmin = img.min(); # get darkest intensity
# express our preknowledge
img[ img > 0 ] *= +1.0  / img_pmax; 
img[ img = 1 :
    prev_idx = vt_path[ -1 ].astype('int');
    vt_path.append( path_buffer[ prev_idx, time ] );
    time -= 1;
vt_path.reverse();    
vt_path = np.asarray( vt_path ).T;

# plot found optimal paths for every 7 of them
pyplot.imshow( rgb, 'jet' ),
for row in range( 0, h, 7 ) :
    pyplot.hold(True), pyplot.plot( vt_path[row,:], c=np.random.rand(3,1), lw = 2 );
pyplot.xlim( ( 0, w ) );
pyplot.ylim( ( h, 0 ) );

认为我应该发布我的答案,因为它与其他方法有点不同。我在 Matlab 中试过这个。

  • 对所有通道求和并创建图像,因此所有通道的权重相同
  • 对此图像执行形态闭合和高斯滤波
  • 对于结果图像的每一列,找到局部最大值并构建图像
  • 找到这张图片的连通分量

我在这里看到的一个缺点是这种方法对于条纹的某些方向效果不佳。在这种情况下,我们必须纠正其方向并应用此程序。

这是 Matlab 代码:

im = imread('m0sy7.png');
imsum = sum(im, 3); % sum all channels
h = fspecial('gaussian', 3);
im2 = imclose(imsum, ones(3)); % close
im2 = imfilter(im2, h); % smooth
% for each column, find regional max
mx = zeros(size(im2));
for c = 1:size(im2, 2)
    mx(:, c) = imregionalmax(im2(:, c));
end
% find connected components
ccomp = bwlabel(mx);

例如,如果你取图像的中间列,它的轮廓应该是这样的:(蓝色是轮廓。绿色是局部最大值) 中间剖面和局部最大值

包含所有列的局部最大值的图像如下所示: 在此处输入图像描述

这是连接的组件(尽管有些条纹被破坏了,但它们中的大多数得到了一个连续的区域):

在此处输入图像描述