在Matlab中通过插值实现图像轮廓平滑

3

我正在使用这个阈值方法绘制图像轮廓点,但我的轮廓有直线段。我想在每个点处绘制相对于垂直线的角度,因此我真的需要曲线。

我可以使用凸包获得平滑曲线。

轮廓点和期望轮廓的图像

该图像生成如下:

B = bwboundaries(BW3);
outline = B{1,1};
plot(outline(:,2),outline(:,1),'r.','LineWidth',1) 
K = convhull(outline(:,2),outline(:,1)); 
plot(outline(K,2),outline(K,1),'b+--','LineWidth',1)

但是我该如何填补凸包点之间的“空隙”呢?我希望在每个红色点上都有蓝色曲线上的一个点。

我尝试使用interp1来实现:

outline2 = outline;
outline2(:,2)=interp1(outline(K,1),outline(K,2),outline(:,1),'spline');

但是出现了以下错误:

"使用griddedInterpolant时出错,网格向量必须包含唯一点。"

我认为这是因为轮廓形成了一个循环,而不是每个y对应一个唯一的x点。有没有其他方法可以使用样条线填充这些丢失的点呢?

我也愿意尝试其他找到平滑边缘的方法。

感谢您的任何帮助!


这是因为 outline(:,1) 包含重复的值。您是要获取最接近红点的点,还是等距离的点? - Cris Luengo
我想要在对象两侧的曲线上每个y值范围内的一点,并且对于x轴也是如此,以使轮廓封闭。这些点应该位于(或靠近)蓝色曲线上。 - Frederick Steven Wells
2个回答

2

由于您的图像看起来平滑且采样良好,建议您为每个边缘像素找到真实边缘的亚像素位置。这样做可以避免使用凸包,虽然对于特定的图像可能有用,但无法推广到任意形状。

以下是一些代码来完成我所建议的内容。

% A test image in the range 0-1, the true edge is assumed to be at 0.5
img = double(gaussianedgeclip(60-rr));

% Get rough outline
p = bwboundaries(img>0.5);
p = p{1,1};

% Refine outline
n = size(p,1);
q = p;          % output outline
for ii=1:n
   % Find the normal at point p(ii,:)
   if ii==1
      p1 = p(end,:);
   else
      p1 = p(ii-1,:);
   end
   if ii==n
      p2 = p(1,:);
   else
      p2 = p(ii+1,:);
   end
   g = p2-p1;
   g = (g([2,1]).*[-1,1])/norm(g);
   % Find a set of points along a line perpendicular to the outline
   s = p(ii,:) + g.*linspace(-2,2,9)';
         % NOTE: The line above requires newer versions of MATLAB. If it
         % fails, use bsxfun or repmat to compute s.
   v = interp2(img,s(:,2),s(:,1));
   % Find where this 1D sample intersects the 0.5 point,
   % using linear interpolation
   if v(1)<0.5
      j = find(v>0.5,1,'first');
   else
      j = find(v<0.5,1,'first');
   end
   x = (v(j-1)-0.5) / (v(j-1)-v(j));
   q(ii,:) = s(j-1,:) + (s(j,:)-s(j-1,:))*x;
end

% Plot
clf
imshow(img,[])
hold on
plot(p(:,2),p(:,1),'r.','LineWidth',1) 
plot(q(:,2),q(:,1),'b.-','LineWidth',1) 
set(gca,'xlim',[68,132],'ylim',[63,113])

代码输出图像

第一行生成测试图像需要DIPimage,但代码的其余部分只使用了标准MATLAB函数,除了你也在使用的来自图像处理工具箱的bwboundaries

输出的点集q没有以整数x或y进行采样。这个问题要复杂得多。

对于变量名只用一个字母表示,非常抱歉... :)


嗨Cris,感谢你的回答。我尝试了实现它,但在第26行一直出现问题:“使用griddedInterpolant时出错 样本值必须是单个或双精度数组。interp2>makegriddedinterp中的错误(第230行) F = griddedInterpolant(varargin {:});interp2中的错误(第114行) F = makegriddedinterp({X,Y},V,method,extrap); Subpix中的错误(第26行) v = interp2(frame,s(:,2),s(:,1));”。我一开始在interp1中出现错误,现在在interp2中出现错误...似乎我还没有走太远... - Frederick Steven Wells
@FrederickStevenWells:在脚本的开头某处加入frame = double(frame)interp2函数需要浮点数输入数组,你正在传递(我猜)一个整数值数组。 - Cris Luengo

1
使用Marching Squares(https://en.wikipedia.org/wiki/Marching_squares#Isoline)查找初始轮廓。
然后,如果您想要良好的导数估计,请拟合插值Cubic Spline(https://en.wikipedia.org/wiki/Spline_interpolation#Algorithm_to_find_the_interpolating_cubic_spline)。
这里有一个小技巧:似乎您想要像素中心的斜率。 但是从 marching cubes 获得的样条将通过已知边缘上的点而不是通过中心。 您可以:
  • 将中心点投影到样条上最近的点(不幸的是需要解高次多项式);

  • 隐式化三次弧并计算隐式函数的梯度。

如果您的精度要求不严格,则可能只需使用标记像素中的段方向即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接