<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Algorithms in C#: Connected Component Labeling</title><link>http://blogs.msdn.com/kirillosenkov/archive/2008/05/07/algorithms-in-c-connected-component-labeling.aspx</link><description>I have to admit, I'm not very good at interviews. For some reason my mind isn't trained well for sorting linked lists or balancing binary trees on the whiteboard. I have no problem designing 600+ type hierarchies and building complex object-oriented frameworks,</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>re: Algorithms in C#: Connected Component Labeling</title><link>http://blogs.msdn.com/kirillosenkov/archive/2008/05/07/algorithms-in-c-connected-component-labeling.aspx#8498920</link><pubDate>Tue, 13 May 2008 08:27:34 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8498920</guid><dc:creator>Kirill Osenkov</dc:creator><description>&lt;p&gt;Brian, a developer on the F# team, has posted the F# solution which uses the Union-Find algorithm: &lt;/p&gt;
&lt;p&gt;&lt;a rel="nofollow" target="_new" href="http://lorgonblog.spaces.live.com/blog/cns"&gt;http://lorgonblog.spaces.live.com/blog/cns&lt;/a&gt;!701679AD17B6D310!220.entry&lt;/p&gt;
&lt;p&gt;It's much faster than my algorithm :) And looks shorter. Highly recommended.&lt;/p&gt;
</description></item><item><title>re: Algorithms in C#: Connected Component Labeling</title><link>http://blogs.msdn.com/kirillosenkov/archive/2008/05/07/algorithms-in-c-connected-component-labeling.aspx#8527395</link><pubDate>Wed, 21 May 2008 13:16:41 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8527395</guid><dc:creator>Morgan Cheng</dc:creator><description>&lt;p&gt;I happens to investigate this algorithm recently.&lt;/p&gt;
&lt;p&gt;I have to adimit that the track row instead of point is a ingenious idea, but there is a lot collections are created and then released. So, I tried to implement it in other ways to validate the performance.&lt;/p&gt;
&lt;p&gt;The result is interesting:&lt;/p&gt;
&lt;p&gt;For most of the time the pixel-by-pixel algorithm is much faster than span-by-span algorithm. Pixel-by-pixel is not defeated when color blob is sparse. That is, &amp;quot;White percentage&amp;quot; bar is to very left or very right.&lt;/p&gt;
</description></item><item><title>re: Algorithms in C#: Connected Component Labeling</title><link>http://blogs.msdn.com/kirillosenkov/archive/2008/05/07/algorithms-in-c-connected-component-labeling.aspx#8530757</link><pubDate>Thu, 22 May 2008 04:00:43 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8530757</guid><dc:creator>Kirill Osenkov</dc:creator><description>&lt;p&gt;Interesting! Thanks Morgan. I'll have to implement the classic pixel-by-pixel algorithm next time to measure the performance difference.&lt;/p&gt;
</description></item><item><title>re: Algorithms in C#: Connected Component Labeling</title><link>http://blogs.msdn.com/kirillosenkov/archive/2008/05/07/algorithms-in-c-connected-component-labeling.aspx#8531008</link><pubDate>Thu, 22 May 2008 05:35:11 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8531008</guid><dc:creator>Morgan Cheng</dc:creator><description>&lt;p&gt;I'm in rush yesterday and have no time to analyze the reason. Now, I believe that the reason is that the randomly generated graph is not appropriate for span-by-span detection. Since it is random, black and white pixels are even distributed. So, there is not long span there; each span has only few pixels. In worst case, each span has only one pixel. As a result, the power of span is not utilized.&lt;/p&gt;
&lt;p&gt;I use the two different way on real image. It turns out that span-by-span way is better than pixel-by-pixel way, since in real photo, same color is generally in blob. In average, the time cost ratio is 3:1. So, the span-by-span algorithm is really helpful! thanks.&lt;/p&gt;
&lt;p&gt;The 3-generation way like .net GC is really brilliant idea! But I implement span-by-span in traditional way. Each span is assigned a blobID . For each span, if there is no intersection with up row, a new blob id is assigned; if there is only one intersection, the up span blob ID is reused; if there is more than one intersection, use the smallest blob id, and a boundTo mapping of id-to-id is updated. After the parsing, we can get the directBoundTo from boundTo, then each blob has a unique blob ID.&lt;/p&gt;
&lt;p&gt;Below is just some sample code. The performance is better than 3-gen way. Perhaps mainly due to less function call and collection used.&lt;/p&gt;
&lt;p&gt;var boundTo = new Dictionary&amp;lt;int, int&amp;gt; ();&lt;/p&gt;
&lt;p&gt;int blobID = 0;&lt;/p&gt;
&lt;p&gt;for (int y = 0; y &amp;lt; height; ++y)&lt;/p&gt;
&lt;p&gt;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;var rowSet = new StrikeList&amp;lt;Color&amp;gt;();&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;allStrikes.Add(rowSet);&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Strike&amp;lt;Color&amp;gt; strike = null;&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Color prevPixel = Color.Empty; &lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;for (int x = 0; x &amp;lt; width; ++x)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Color currPixel = field[x, y];&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if (currPixel != prevPixel)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if (strike != null)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;strike.EndX = x - 1;&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;rowSet.Add(strike);&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;strike = null;&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;prevPixel = currPixel;&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if (currPixel != Color.Empty)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;strike = new Strike&amp;lt;Color&amp;gt; { StartX = x, Color = currPixel, Y = y };&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if (strike != null)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;strike.EndX = width - 1;&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;rowSet.Add(strike);&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;StrikeList&amp;lt;Color&amp;gt; prevRowSet = (y &amp;gt; 0) ? allStrikes[y - 1] : new StrikeList&amp;lt;Color&amp;gt;();&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;foreach (var strike in rowSet)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;List&amp;lt;Strike&amp;lt;Color&amp;gt;&amp;gt; insected = prevRowSet.Where(s =&amp;gt; s.IntersectWith(strike)).ToList();&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if (insected.Count == 0)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;strike.BlobID = (++blobID);&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;boundTo[blobID] = blobID;&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;else if (insected.Count == 1)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;strike.BlobID = insected[0].BlobID;&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;else&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;var minBlob = insected[0];&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;for (int i = 0; i &amp;lt; insected.Count; ++i)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if (insected[i].BlobID &amp;lt; minBlob.BlobID)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;minBlob = insected[i];&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;strike.BlobID = minBlob.BlobID;&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;foreach (var s in insected)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if (s != minBlob)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;boundTo[s.BlobID] = minBlob.BlobID;&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;var directBoundTo = new Dictionary&amp;lt;int, int&amp;gt;();&lt;/p&gt;
&lt;p&gt;foreach (var key in boundTo.Keys)&lt;/p&gt;
&lt;p&gt;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;int target = boundTo[key];&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;while (boundTo[target] != target)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;{&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;target = boundTo[target];&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;directBoundTo[key] = target;&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;</description></item><item><title>re: Algorithms in C#: Connected Component Labeling</title><link>http://blogs.msdn.com/kirillosenkov/archive/2008/05/07/algorithms-in-c-connected-component-labeling.aspx#8612578</link><pubDate>Wed, 18 Jun 2008 01:17:13 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8612578</guid><dc:creator>Kirill Osenkov</dc:creator><description>&lt;p&gt;Morgan, thanks so much for sharing this, this is really interesting. It's great that you've found a way to improve performance and I think you're right - span tracking isn't really shining on random images.&lt;/p&gt;
&lt;p&gt;I like your idea, it saves up memory because you don't have to initialize throw away collections every time.&lt;/p&gt;
</description></item></channel></rss>