我在一个项目中工作,该项目将条纹投射在主体上,并拍摄了照片。我们的任务是找到条纹的中心线,这些中心线在数学上代表条纹平面和对象表面之间的3D交点曲线。

照片是PNG(RGB),以前尝试过使用灰度,然后使用差异阈值获得黑白“斑马纹”摄影,从中可以轻松找到每个条纹的每个像素列的中点。问题在于,通过阈值化以及通过获取离散像素列的平均高度,我们会遇到一些精度损失和量化问题,这是完全不需要的。

我的印象是,在图像上,如果通过某种统计扫描方法(某些泛洪/迭代)直接从非阈值图像(RGB或灰度)检测到中心线,则中心线可能更连续(更多点)并且更平滑(未量化)

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



任何建议将不胜感激!

/>

评论

这很有趣。但是,顺便说一下,我正在做一些使用色带检测3d对象的研究。由于使用色带,因此很容易从投影机中找到每个色带的对应关系。因此使用三角学可以计算3d信息。如果颜色相同,如何找到对应关系?我猜你的项目也是关于3d重建的吗?

@johnyoung:请不要添加评论作为答案。我知道您需要信誉才能发表评论,但是请避免当前的做法。我建议您问自己(相关)的问题或回答他人的问题以增加您的代表。

抱歉,还有一个问题而不是给出答案,在相移方法中,我们计算投影图像中每个像素的相位,但是在这里为什么我们需要找出条纹的中心线,可能是我的问题很愚蠢但我不知道不,所以请给我打电话的确切原因。您可以在回答后删除我的问题

这些是不同的方法。我通过投影一系列白色条纹来建模一系列几何平面(每个条纹在3D空间中形成一个“平面”)。因此,我需要找到条纹的中心线,因为平面没有厚度。当然可以执行相移分析,但是有一个问题:我的投影是二值的(黑白条纹交替),强度没有正弦变化,因此我无法执行相移(并且目前不需要) )。

#1 楼

我建议执行以下步骤:


找到一个阈值以将前景与背景分开。
对于二进制图像中的每个斑点(一个斑马条纹),对于每个x,找到y方向上的加权中心(按像素强度)。
可以平滑y的值,以消除噪声。
通过拟合某种曲线连接(x,y)点。本文可能会为您提供帮助。我认为,即使更糟,也可以拟合高级多项式。

这是一个Matlab代码,显示了步骤1,2和4。我跳过了自动阈值选择。相反,我选择了手动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


评论


$ \ begingroup $
我发现这很有趣。我使用Python,但是无论如何我都必须研究所有这些的原理。作为独立评论,我倾向于不执行经典图像处理(直接在量化图像容器(例如uint8数组)上执行),而是在应用操作之前将所有内容作为浮点数组加载到内存中。另外,我对图像下半部分的结果感到惊讶,蓝线没有沿着预期的边缘中线行进(...)。现在谢谢,我将在收到一些结果后尽快提出反馈!
$ \ endgroup $
– heltonbiker
2012年10月15日在18:04

$ \ begingroup $
@heltonbiker,请检查更新的答案。您对浮点数是正确的,当我转换为double时就使用了它。关于下半部分的结果,我需要检查一下,这可能是软件错误
$ \ endgroup $
–安德烈(Andrey Rubshtein)
2012年10月15日18:07



$ \ begingroup $
@heltonbiker,完成了。这确实是与基于1的索引相关的错误。
$ \ endgroup $
–安德烈(Andrey Rubshtein)
2012年10月15日18:26

$ \ begingroup $
优秀!的确如此。使用这种技术,并且就我的目的而言,平滑甚至不但不需要,而且会很有害。非常感谢您的关注!
$ \ endgroup $
– heltonbiker
2012年10月15日19:15

#2 楼

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

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


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


评论


$ \ begingroup $
好主意。我计划在每个像素列的峰值处使用某种抛物线或样条曲线,但我仍在怀疑是否应该检查像素列还是沿该行检查像素“区域” ...还要等一下更多答案。现在谢谢!
$ \ endgroup $
– heltonbiker
2012年10月15日16:18

$ \ begingroup $
@heltonbiker-作为快速测试,请仅使用绿色通道。在彩色传感器上,通常绿色像素的数量是红色的两倍,而红色和蓝色的像素更少
$ \ endgroup $
–马丁·贝克特(Martin Beckett)
2012年10月21日4:03



$ \ begingroup $
@MartinBeckett感谢您的关注,我已经分析了每个渠道,实际上绿色的渠道似乎比红色的渠道要解决得多。但是,绘制每个通道的垂直横截面强度值时,“条纹图案”似乎在通道之间变化不大,并且我目前在转换为灰度时将它们均匀混合。即使如此,我仍然计划研究通道之间的最佳线性组合以获得最佳对比度结果,或者获取已经为灰度的图像。再次感谢!
$ \ endgroup $
– heltonbiker
2012年10月21日在22:55

#3 楼

通过将问题建模为“路径优化问题”,这是您的问题的另一种解决方案。尽管它比简单的二值化然后弯曲的解决方案复杂,但在实践中却更强大。

从非常高的层次来看,我们应该将此图像视为一个图,其中


每个图像像素都是该图上的一个节点
节点连接到其他一些节点(称为邻居),并且此连接定义通常称为该图的拓扑。
每个节点都有权重(功能,成本,能源或任何您想要称呼的权重) ),反映出该节点位于我们正在寻找的最佳中心线上的可能性。

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

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


您的所有结果都是连续的(不同于阈值方法可能将一条中心线分解成碎片)
可以很自由地构建这样的图形,可以选择不同的特征和图形拓扑。
从路径优化的角度来看,您的结果是最佳的。
您的解决方案将对噪声更加鲁棒,因为只要噪声在所有像素之间平均分配,这些最佳路径就保持稳定。

以下是上述想法的简短演示。由于我不使用任何先验知识来指定可能的起始节点和终止节点,因此我只对每个可能的起始节点进行解码。


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

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

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

修剪这些不希望的局部最优路径并不困难。因为我们在维特比解码之后拥有所有路径的可能性,并且您可能会使用各种先验知识(例如,我们看到,对于共享同一源的人,我们只需要一条最优路径。)

更多详细信息,您可以参考本文。

 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 ) );


评论


$ \ begingroup $
这是一种非常有趣的方法。我承认“图形”的主题一直困扰着我,直到最近(在同一项目上)我只能使用图形解决另一个问题。在“理解”之后,我意识到了这些最​​短路径算法的强大功能。您的想法非常有趣,如果有需要/机会,我会重新实现这一想法。非常感谢你。
$ \ endgroup $
– heltonbiker
2014年11月11日11:26

$ \ begingroup $
关于您当前的结果,根据我的经验,在构建图形之前,最好先使用高斯和/或中值滤波器对图像进行平滑处理。这样可以使线条更平滑(更正确)。同样,一种可能的技巧是扩展邻域以允许在两个或更多像素(直到给定限制,例如8或10个像素)上“直接跳跃”。当然应该选择合适的成本函数,但是我认为这很容易调整。
$ \ endgroup $
– heltonbiker
2014年11月11日,11:29

$ \ begingroup $
哦,是的。我只是选择了一些东西,您肯定可以使用其他拓扑和能量函数。实际上,此框架也是可训练的。特别是,您从原始强度开始,解码以获得最佳路径,仅以高置信度拾取那些最佳节点,并以此方式获得“标记数据”。使用这小部分自动标记的数据,您可以学习许多有用的东西。
$ \ endgroup $
–陷阱
2014年11月11日19:04

#4 楼

我认为我应该发布我的答案,因为它与其他方法有些不同。我在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);


例如,如果您采取图片的中间列,其轮廓应如下所示:
(蓝色为轮廓,绿色为局部最大值)


包含图像的图像所有列的局部最大值看起来像这样:


这里是相连的组件(尽管一些条纹被折断了,但大多数都变成了连续区域):



评论


$ \ begingroup $
这实际上是我们现在正在做的,唯一的区别是如何为每个像素列找到局部最大值:我们使用抛物线插值法来找到抛物线穿过具有最大值的像素及其顶点的确切顶点上下邻居。这使得s可以在“像素之间”,从而更好地表示线条的微妙平滑度。感谢您的回答!
$ \ endgroup $
– heltonbiker
2014年11月12日上午11:33