Matlab:如何在图像中弯曲线条

5
我手头有一张图像(png格式),椭圆形的边界线(代表细胞核)是直线,不太实用。我该如何从图像中提取这些线并使其弯曲,前提是它们仍然围绕着细胞核。
以下是该图像:
enter image description here 弯曲后: enter image description here 编辑:如何将答案2中的“膨胀和过滤”部分翻译成Matlab语言?我想不出来。

你是在说黑色的线条吗? - Hannes Ovrén
在这种情况下,“bent”是什么意思?你能否选择图像中的一行并画出它在“弯曲”后应该看起来的样子? - Hannes Ovrén
3
视您需要的数量而定,但老实说,对于这种情况,最好的幻像似乎是从像您的示例一样的真实图像推导出来的一个阈值化/简化图像。 - John Colby
@John 我认为可以通过模拟从核心椭圆开始的某种随机生长来实现,但我仍然无法让它正常工作。 - Dr. belisarius
3个回答

7
您输入的内容是沃罗诺伊图。您可以使用其他距离函数重新计算它,而不是欧几里德距离。以下是Mathematica中使用曼哈顿距离的示例(i3是没有线条的输入图像):
ColorCombine[{Image[
   WatershedComponents[
    DistanceTransform[Binarize@i3, 
      DistanceFunction -> ManhattanDistance] ]], i3, i3}]

在此输入图片描述

编辑

我正在使用另一种算法(初步结果)。您觉得如何?

在此输入图片描述


OP想要更逼真的细胞,即不那么像 Voronoi 图。看看示例照片,你会发现一些细胞承受更多的压力或其他因素而鼓起来并挤压其他细胞。 - John Colby
2
@John,你觉得我的新图画怎么样?我还在完善一些细节! - Dr. belisarius
那真的很棒。我喜欢它! - John Colby
@John,我已经完成了算法,但现在没有时间发布它。整个过程相当复杂,因为所有简单的解决方案最终都会变成对称配置。需要进行几个随机化步骤才能获得(更或者少一些)“自然”的外观。我明天会发布我的“弗兰肯斯坦”。 - Dr. belisarius
@belisarius 请问你可以更详细地解释一下你正在使用的算法吗? - Elsie
显示剩余2条评论

7

好的,这里有一种方法涉及到几个随机化步骤,能够获得“自然”非对称外观。

我发布了实际的Mathematica代码,以防万一有人需要将其翻译为Matlab。

(* A preparatory step: get your image and clean it*)
i = Import@"http://i.stack.imgur.com/YENhB.png";
i1 = Image@Replace[ImageData[i], {0., 0., 0.} -> {1, 1, 1}, {2}];
i2 = ImageSubtract[i1, i];
i3 = Inpaint[i, i2]

enter image description here

(*Now reduce to a skeleton to get a somewhat random starting point.  
The actual algorithm for this dilation does not matter, as far as we 
get a random area slightly larger than the original elipses *)
id = Dilation[SkeletonTransform[
             Dilation[SkeletonTransform@ColorNegate@Binarize@i3, 3]], 1] 

enter image description here

(*Now the real random dilation loop*)
(*Init vars*)
p = Array[1 &, 70]; j = 1;
(*Store in w an image with a different color for each cluster, so we 
can find edges between them*)
w = (w1 = 
      WatershedComponents[
       GradientFilter[Binarize[id, .1], 1]]) /. {4 -> 0} // Colorize;
(*and loop ...*)
For[i = 1, i < 70, i++,
 (*Select edges in w and dilate them with a random 3x3 kernel*)
 ed = Dilation[EdgeDetect[w, 1], RandomInteger[{0, 1}, {3, 3}]];
 (*The following is the core*)
 p[[j++]] = w =
   ImageFilter[  (* We apply a filter to the edges*)
    (Switch[
          Length[#1],  (*Count the colors in a 3x3 neighborhood of each pixel*)
          0, {{{0, 0, 0}, 0}},          (*If no colors, return bkg*)
          1, #1,                        (*If one color, return it*)
          _, {{{0, 0, 0}, 0}}])[[1, 1]] (*If more than one color, return bkg*)&@
      Cases[Tally[Flatten[#1, 1]], 
       Except[{{0.`, 0.`, 0.`}, _}]] & (*But Don't count bkg pixels*),
    w, 1,
    Masking -> ed,       (*apply only to edges*)
    Interleaving -> True (*apply to all color chanels at once*)]
 ]

结果如下:

结果是:

enter image description here

编辑

对于以Mathematica为导向的读者来说,最后一个循环的功能代码可能更容易(且更短):

NestList[
 ImageFilter[  
   If[Length[#1] ==  1, #1[[1, 1]], {0, 0, 0}] &@
     Cases[Tally[Flatten[#1, 1]], Except[{0.` {1, 1, 1}, _}]] & , #, 1,
   Masking      -> Dilation[EdgeDetect[#, 1], RandomInteger[{0, 1}, {3, 3}]],  
   Interleaving -> True ] &,
 WatershedComponents@GradientFilter[Binarize[id,.1],1]/.{4-> 0}//Colorize, 
5]

你上面使用的算法,如果我理解正确的话,是将不同的颜色分配给分水岭区域,一旦膨胀相遇,它就会返回黑色背景中的边缘。在Mathematica中它运行得很好。现在我正在尝试在matlab中应用这种方法,希望我能成功。谢谢。 - Elsie
@Ivy 是的。要小心随机化部分,否则你最终会得到一个美丽的正方形苏格兰呢补缀。祝好运! - Dr. belisarius
谢谢你的信任,但这是一个难以翻译的问题(我自己还在学习Mathematica)...无论如何,我会尽快查看并回复。顺便说一句,非常好的解决方案+1。 - Amro
@Amro,你非常值得我的赞扬!无论如何,如果你正在学习Mma,请记得在SO中查看Mma标签的问题和答案。那里有很多知识渊博的用户可以学习。 - Dr. belisarius
@Amro 完全同意,但 Mma 社区更喜欢这种方式。 - Dr. belisarius
显示剩余3条评论

6

这是我想到的翻译,它不是@belisarius代码的直接翻译,但应该足够接近。

%# read image (indexed image)
[I,map] = imread('http://i.stack.imgur.com/YENhB.png');

%# extract the blobs (binary image)
BW = (I==1);

%# skeletonization + dilation
BW = bwmorph(BW, 'skel', Inf);
BW = imdilate(BW, strel('square',2*1+1));

%# connected components
L = bwlabel(BW);
imshow(label2rgb(L))

%# filter 15x15 neighborhood
for i=1:13
    L = nlfilter(L, [15 15], @myFilterFunc);
    imshow( label2rgb(L) )
end

%# result
L(I==1) = 0;                %# put blobs back
L(edge(L,'canny')) = 0;     %# edges
imshow( label2rgb(L,@jet,[0 0 0]) )

myFilterFunc.m

function p = myFilterFunc(x)
    if range(x(:)) == 0
        p = x(1);                %# if one color, return it
    else
        p = mode(x(x~=0));       %# else, return the most frequent color
    end
end

结果如下:

结果:

截图

以下是该过程的动画:

动画


+1 很好。在我的答案中使用了掩码来提供额外的随机化步骤。无论如何,我认为这已经足够作为一个开始。恭喜! - Dr. belisarius
@Amro:邻近区域花费时间...无论如何,那真的很酷! - Elsie

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