<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Steve on Image Processing</title>
	
	<link>http://blogs.mathworks.com/steve</link>
	<description>Steve Eddins manages the Image &amp; Geospatial development team at The MathWorks and coauthored Digital Image Processing Using MATLAB. He writes here about image processing concepts, algorithm implementations, and MATLAB.</description>
	<lastBuildDate>Tue, 07 Feb 2012 00:29:45 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/SteveOnImageProcessing" /><feedburner:info uri="steveonimageprocessing" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>New comment policy</title>
		<link>http://feedproxy.google.com/~r/SteveOnImageProcessing/~3/hV0yFQAwA5E/</link>
		<comments>http://blogs.mathworks.com/steve/2012/02/06/new-comment-policy/#comments</comments>
		<pubDate>Tue, 07 Feb 2012 00:28:31 +0000</pubDate>
		<dc:creator>Steve Eddins</dc:creator>
				<category><![CDATA[Blog policies]]></category>

		<guid isPermaLink="false">http://blogs.mathworks.com/steve/?p=460</guid>
		<description><![CDATA[I want to let readers know that I'm experimenting with a new comment policy. I started this blog at the beginning of 2006. At first, I left comments open and only filtered out obvious spam. Soon, though, I found that I couldn't handle the volume of comments. Also, most comments were off-topic, which I thought [...]]]></description>
			<content:encoded><![CDATA[   <p>I want to let readers know that I'm experimenting with a new comment policy.</p>
   <p>I started this blog at the beginning of 2006. At first, I left comments open and only filtered out obvious spam. Soon, though,
      I found that I couldn't handle the volume of comments. Also, most comments were off-topic, which I thought detracted from
      the main content of the posts. So I began moderating all comments, and I only allowed through comments that were relevant
      to the post's topic.
   </p>
   <p>And that's the way it's been for the past five years or so. But lately the time required for handling and responding to comments
      has become a problem for me. It is taking away time and energy that I would prefer to put into posting new content. Because
      of this I have occasionally considered turning off comments completely, but I've been reluctant to do that because I regularly
      learn valuable things from reader comments, and also because readers sometimes let me know about corrections I should make.
   </p>
   <p>So starting this week I'm trying a compromise. I'm closing comments on any post older than 60 days. I believe that most of
      the interesting discussions and needed corrections come in well before that time.
   </p>

<p>
Keep in mind that this blog isn't a general help forum or an alternative channel for product support. I encourage everyone seeking specific help with a MATLAB concept or with getting your code running to use resources such as
      <a href="http://www.mathworks.com/matlabcentral/answers/">MATLAB Answers</a> or the <a href="http://www.mathworks.com/matlabcentral/newsreader/">MATLAB newsgroup</a>.
</p>
<p>
For product support questions, another starting point is the <a href="http://www.mathworks.com/support">Support section</a> of mathworks.com. There's really a lot of good information there.
</p>
<p>
I also get a pretty high volume of direct e-mail because my e-mail address is relatively easy to find. I do try to answer specific questions about my MATLAB Central File Exchange submissions that come in through my <a href="http://www.mathworks.com/matlabcentral/fileexchange/authors/22204">Author page</a> there. Otherwise, I mostly can't answer the direct e-mail that I receive.
</p>
   <p>Thanks for reading ... and for commenting!</p><img src="http://feeds.feedburner.com/~r/SteveOnImageProcessing/~4/hV0yFQAwA5E" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blogs.mathworks.com/steve/2012/02/06/new-comment-policy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blogs.mathworks.com/steve/2012/02/06/new-comment-policy/</feedburner:origLink></item>
		<item>
		<title>Jobs and such</title>
		<link>http://feedproxy.google.com/~r/SteveOnImageProcessing/~3/2UWiHPxCISc/</link>
		<comments>http://blogs.mathworks.com/steve/2012/01/31/jobs-and-such/#comments</comments>
		<pubDate>Tue, 31 Jan 2012 18:59:38 +0000</pubDate>
		<dc:creator>Steve Eddins</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blogs.mathworks.com/steve/?p=448</guid>
		<description><![CDATA[Last week I happened to come across a summary I wrote after completing my first month at MathWorks. This was quite a while a go ... December 1993! I was hired to take over development of the Image Processing Toolbox, which had just shipped version 1.0. From the summary, I see that I spent a [...]]]></description>
			<content:encoded><![CDATA[<p>Last week I happened to come across a summary I wrote after completing my first month at MathWorks. This was quite a while a go ... December 1993!
</p>
<p>
I was hired to take over development of the Image Processing Toolbox, which had just shipped version 1.0. From the summary, I see that I spent a lot of time that first month studying the toolbox, looking at the way product demos and GUIs were programmed (back then, anyway), and writing a new product demo (on quadtree decomposition).
</p>
<p>
A fair amount of time that first month was spent getting up and running on my computer, an HP workstation running some flavor of Unix. I read a few months worth of internal newsgroup postings in order to learn more about my new colleagues and company. I drafted a new section on JPEG image compression for a marketing document called MATLAB Expo, and I searched for a cool-looking sample image to use in an ad.  I attended a meeting of the MathWorks Social Mission Team.
</p>
<p>
I studied specs for MATLAB 5.0, which we thought would ship soon but actually took three more years.
</p>
<p>
And I learned the company's bug tracking system in order to report my very first bug found.
</p>
<p>
Hey ... speaking of new jobs, we just completed our annual planning process, and <b>in 2012 we plan to hire bunches of people</b> at MathWorks!
</p>
<p>
So please, take a look at the <a href="http://www.mathworks.com/company/jobs/opportunities/">Careers</a> section of our web site to see if an opening interests you. And submit your resume now! Managers are going crazy trying to fill the openings.
</p><img src="http://feeds.feedburner.com/~r/SteveOnImageProcessing/~4/2UWiHPxCISc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blogs.mathworks.com/steve/2012/01/31/jobs-and-such/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blogs.mathworks.com/steve/2012/01/31/jobs-and-such/</feedburner:origLink></item>
		<item>
		<title>Generating Hilbert curves</title>
		<link>http://feedproxy.google.com/~r/SteveOnImageProcessing/~3/2cKFqoqEmig/</link>
		<comments>http://blogs.mathworks.com/steve/2012/01/25/generating-hilbert-curves/#comments</comments>
		<pubDate>Thu, 26 Jan 2012 04:40:47 +0000</pubDate>
		<dc:creator>Steve Eddins</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blogs.mathworks.com/steve/?p=444</guid>
		<description><![CDATA[This week I came across some files I wrote about 16 years ago to compute Hilbert curves. A Hilbert curve is a type of fractal curve; here is a sample: I can't remember why I was working on this. Possibly I was anticipating that 16 years in the future, during an unusually mild New England [...]]]></description>
			<content:encoded><![CDATA[<div xmlns:mwsh="http://www.mathworks.com/namespace/mcode/v1/syntaxhighlight.dtd" class="content">
   <p>This week I came across some files I wrote about 16 years ago to compute Hilbert curves. A Hilbert curve is a type of fractal
      curve; here is a sample:
   </p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2012/hilbert-curve-sample.png"> </p>
   <p>I can't remember why I was working on this. Possibly I was anticipating that 16 years in the future, during an unusually mild
      New England winter, I would be looking for a blog topic.
   </p>
   <p>Anyway, there are several interesting ways to code up a Hilbert curve generator. My old code for generating the Hilbert curve
      followed the J. G. Griffiths, "Table-driven Algorithms for Generating Space-Filling Curves," Computer-Aided Design, v.17,
      pp. 37-41, 1985.  Here's the basic procedure:
   </p>
   <p>First, initialize four curves, \( A_0 \), \( B_0 \), \( C_0 \), and \( D_0 \), to be empty (no points).</p>
   <p>Then, build up the Hilbert curve iteratively as follows:</p>
   <p>\[ A_{n+1} = [B_n, N, A_n, E, A_n, S, C_n] \]</p>
   <p>\[ B_{n+1} = [A_n, E, B_n, N, B_n, W, D_n] \]</p>
   <p>\[ C_{n+1} = [D_n, W, C_n, S, C_n, E, A_n] \]</p>
   <p>\[ D_{n+1} = [C_n, S, D_n, W, D_n, N, B_n] \]</p>
   <p>where \( N \) represents a unit step up, \( E \) is a unit step to the right, \( S \), is a unit step down, and \( W \) is
      a unit step left.
   </p>
   <p>One way to code this procedure is to incrementally build up a set of vectors that define the step from one point on the path
      to the next, and then to use a cumulative summation at the end to turn the steps into x-y coordinates. Here's how you might
      do it.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">A = zeros(0,2);
B = zeros(0,2);
C = zeros(0,2);
D = zeros(0,2);

north = [ 0  1];
east  = [ 1  0];
south = [ 0 -1];
west  = [-1  0];

order = 3;
<span style="color: #0000FF">for</span> n = 1:order
  AA = [B ; north ; A ; east  ; A ; south ; C];
  BB = [A ; east  ; B ; north ; B ; west  ; D];
  CC = [D ; west  ; C ; south ; C ; east  ; A];
  DD = [C ; south ; D ; west  ; D ; north ; B];

  A = AA;
  B = BB;
  C = CC;
  D = DD;
<span style="color: #0000FF">end</span>

A = [0 0; cumsum(A)];

plot(A(:,1), A(:,2), <span style="color: #A020F0">'clipping'</span>, <span style="color: #A020F0">'off'</span>)
axis <span style="color: #A020F0">equal</span>, axis <span style="color: #A020F0">off</span></pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2012/fractal_curves_01.png"> <p>I was curious to see what might be on the MATLAB Central File Exchange, so I <a href="http://www.mathworks.com/matlabcentral/fileexchange/?search_submit=fileexchange&amp;query=hilbert+curve&amp;term=hilbert+curve">searched for "hilbert curve"</a> and found several interesting contributions. There are a couple of 3-D Hilbert curve generators, and several different ways
      of coding up a 2-D Hilbert curve generator. I was particularly interested in the <a href="http://www.mathworks.com/matlabcentral/fileexchange/27577-fractal-curves">Fractal Curves contribution</a> by <a href="http://www.mathworks.com/matlabcentral/fileexchange/authors/27012">Jonas Lundgren</a>.
   </p>
   <p>Jonas shows a much more compact implementation of the ideas above using complex arithmetic. It looks like this:</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">a = 1 + 1i;
b = 1 - 1i;

<span style="color: #228B22">% Generate point sequence</span>
z = 0;
order = 5;
<span style="color: #0000FF">for</span> k = 1:order
    w = 1i*conj(z);
    z = [w-a; z-b; z+a; b-w]/2;
<span style="color: #0000FF">end</span>

plot(z, <span style="color: #A020F0">'clipping'</span>, <span style="color: #A020F0">'off'</span>)
axis <span style="color: #A020F0">equal</span>, axis <span style="color: #A020F0">off</span></pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2012/fractal_curves_02.png"> <p>Jonas' contribution includes several other curve generators. Here's one called "dragon".</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">z = dragon(12);
plot(z), axis <span style="color: #A020F0">equal</span></pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2012/fractal_curves_03.png"> <p>Here's a zoom-in view of a portion of the dragon.</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">axis([-.1 0.2 0.4 0.7])</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2012/fractal_curves_04.png"> <p>What do you think? Does anyone know of an image processing application for shape-filling curves? Please leave a comment.</p><script language="JavaScript">
<!--

    function grabCode_0f7b19584bc4487a97853a51146f5ef8() {
        // Remember the title so we can use it in the new page
        title = document.title;

        // Break up these strings so that their presence
        // in the Javascript doesn't mess up the search for
        // the MATLAB code.
        t1='0f7b19584bc4487a97853a51146f5ef8 ' + '##### ' + 'SOURCE BEGIN' + ' #####';
        t2='##### ' + 'SOURCE END' + ' #####' + ' 0f7b19584bc4487a97853a51146f5ef8';
    
        b=document.getElementsByTagName('body')[0];
        i1=b.innerHTML.indexOf(t1)+t1.length;
        i2=b.innerHTML.indexOf(t2);
 
        code_string = b.innerHTML.substring(i1, i2);
        code_string = code_string.replace(/REPLACE_WITH_DASH_DASH/g,'--');

        // Use /x3C/g instead of the less-than character to avoid errors 
        // in the XML parser.
        // Use '\x26#60;' instead of '<' so that the XML parser
        // doesn't go ahead and substitute the less-than character. 
        code_string = code_string.replace(/\x3C/g, '\x26#60;');

        author = 'Steve Eddins';
        copyright = 'Copyright 2012 The MathWorks, Inc.';

        w = window.open();
        d = w.document;
        d.write('<pre>\n');
        d.write(code_string);

        // Add author and copyright lines at the bottom if specified.
        if ((author.length > 0) || (copyright.length > 0)) {
            d.writeln('');
            d.writeln('%%');
            if (author.length > 0) {
                d.writeln('% _' + author + '_');
            }
            if (copyright.length > 0) {
                d.writeln('% _' + copyright + '_');
            }
        }

        d.write('</pre>\n');
      
      d.title = title + ' (MATLAB code)';
      d.close();
      }   
      
-->
</script><p style="text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray"><br /><a href="javascript:grabCode_0f7b19584bc4487a97853a51146f5ef8()"><span style="font-size: x-small;        font-style: italic;">Get 
            the MATLAB code 
            <noscript>(requires JavaScript)</noscript></span></a><br /><br />
      Published with MATLAB&reg; 7.13<br /></p>
</div>
<!--
0f7b19584bc4487a97853a51146f5ef8 ##### SOURCE BEGIN #####
%%
% This week I came across some files I wrote about 16 years ago to compute
% Hilbert curves. A Hilbert curve is a type of fractal curve; here is a
% sample:
%
% <<http://blogs.mathworks.com/images/steve/2012/hilbert-curve-sample.png>>
%
% I can't remember why I was working on this. Possibly I was anticipating
% that 16 years in the future, during an unusually mild New England winter,
% I would be looking for a blog topic.
%
% Anyway, there are several interesting ways to code up a Hilbert curve
% generator. My old code for generating the Hilbert curve followed the J.
% G. Griffiths, "Table-driven Algorithms for Generating Space-Filling
% Curves," Computer-Aided Design, v.17, pp. 37-41, 1985.  Here's the basic
% procedure:
%
% First, initialize four curves, \( A_0 \), \( B_0 \), \( C_0 \), and \(
% D_0 \), to be empty (no points).
%
% Then, build up the Hilbert curve iteratively as follows:
%
% \[ A_{n+1} = [B_n, N, A_n, E, A_n, S, C_n] \]
%
% \[ B_{n+1} = [A_n, E, B_n, N, B_n, W, D_n] \]
%
% \[ C_{n+1} = [D_n, W, C_n, S, C_n, E, A_n] \]
%
% \[ D_{n+1} = [C_n, S, D_n, W, D_n, N, B_n] \]
%
% where \( N \) represents a unit step up, \( E \) is a unit step to the
% right, \( S \), is a unit step down, and \( W \) is a unit step left.
%
% One way to code this procedure is to incrementally build up a set of
% vectors that define the step from one point on the path to the next, and
% then to use a cumulative summation at the end to turn the steps into x-y
% coordinates. Here's how you might do it.

A = zeros(0,2);
B = zeros(0,2);
C = zeros(0,2);
D = zeros(0,2);

north = [ 0  1];
east  = [ 1  0];
south = [ 0 -1];
west  = [-1  0];

order = 3;
for n = 1:order
  AA = [B ; north ; A ; east  ; A ; south ; C];
  BB = [A ; east  ; B ; north ; B ; west  ; D];
  CC = [D ; west  ; C ; south ; C ; east  ; A];
  DD = [C ; south ; D ; west  ; D ; north ; B];
  
  A = AA;
  B = BB;
  C = CC;
  D = DD;
end

A = [0 0; cumsum(A)];

plot(A(:,1), A(:,2), 'clipping', 'off')
axis equal, axis off

%%
% I was curious to see what might be on the MATLAB Central File Exchange,
% so I
% <http://www.mathworks.com/matlabcentral/fileexchange/?search_submit=fileexchange&#038;query=hilbert+curve&#038;term=hilbert+curve
% searched for "hilbert curve"> and found several interesting
% contributions. There are a couple of 3-D Hilbert curve generators, and
% several different ways of coding up a 2-D Hilbert curve generator. I was
% particularly interested in the
% <http://www.mathworks.com/matlabcentral/fileexchange/27577-fractal-curves
% Fractal Curves contribution> by
% <http://www.mathworks.com/matlabcentral/fileexchange/authors/27012 Jonas
% Lundgren>.
%
% Jonas shows a much more compact implementation of the ideas above using
% complex arithmetic. It looks like this:

a = 1 + 1i;
b = 1 - 1i;

% Generate point sequence
z = 0;
order = 5;
for k = 1:order
    w = 1i*conj(z);
    z = [w-a; z-b; z+a; b-w]/2;
end

plot(z, 'clipping', 'off')
axis equal, axis off

%%
% Jonas' contribution includes several other curve generators. Here's one
% called "dragon".

z = dragon(12);
plot(z), axis equal

%%
% Here's a zoom-in view of a portion of the dragon.

axis([-.1 0.2 0.4 0.7])

%%
% What do you think? Does anyone know of an image processing application
% for shape-filling curves? Please leave a comment.
##### SOURCE END ##### 0f7b19584bc4487a97853a51146f5ef8
--><img src="http://feeds.feedburner.com/~r/SteveOnImageProcessing/~4/2cKFqoqEmig" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blogs.mathworks.com/steve/2012/01/25/generating-hilbert-curves/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://blogs.mathworks.com/steve/2012/01/25/generating-hilbert-curves/</feedburner:origLink></item>
		<item>
		<title>Five years ago: August, September, and October 2006</title>
		<link>http://feedproxy.google.com/~r/SteveOnImageProcessing/~3/FoV5SU6TFtc/</link>
		<comments>http://blogs.mathworks.com/steve/2012/01/13/five-years-ago-august-september-and-october-2006/#comments</comments>
		<pubDate>Fri, 13 Jan 2012 20:50:33 +0000</pubDate>
		<dc:creator>Steve Eddins</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blogs.mathworks.com/steve/?p=439</guid>
		<description><![CDATA[Much of the information I posted in this blog years ago is still useful today. Image processing theory hasn't been completely been overturned since then, and I'm still talking about MATLAB after all. For the benefit of readers who have joined the party more recently, I will occasionally recap the useful bits that I posted [...]]]></description>
			<content:encoded><![CDATA[<div xmlns:mwsh="http://www.mathworks.com/namespace/mcode/v1/syntaxhighlight.dtd" class="content">
   <p>Much of the information I posted in this blog years ago is still useful today. Image processing theory hasn't been completely
      been overturned since then, and I'm still talking about MATLAB after all. For the benefit of readers who have joined the party
      more recently, I will occasionally recap the useful bits that I posted about five years ago.
   </p>
   <p>In August, September, and October of 2006 I finally finished (mostly) a long series of posts covering <a href="http://blogs.mathworks.com/steve/category/spatial-transforms/">spatial transforms</a>. I posed a <a href="http://blogs.mathworks.com/steve/2006/08/08/reader-challenge-custom-spatial-transformations/">reader challenge</a> to come up with a particularly interesting and creative custom spatial transformation. My favorite <a href="http://blogs.mathworks.com/steve/2006/08/22/responses-to-reader-challenge/">submission</a> was this one:
   </p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/80/reader_challenge_03.jpg"> </p>
   <p>I told the story of how we <a href="http://blogs.mathworks.com/steve/2006/08/11/discrete-cosine-transforms-jpeg-and-software-compatibility/">guessed wrong</a> in the early 1990s about which of the several variants of the discrete cosine transform we should use in the Image Processing
      Toolbox.
   </p>
   <p>I <a href="http://blogs.mathworks.com/steve/2006/09/20/unusual-red-eye-reduction-technique/">poked fun</a> at advertising techniques for digital cameras.
   </p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/85/red_eye_reduction_cropped.jpg"> </p>
   <p>I taught about several image processing concepts, including <a href="http://blogs.mathworks.com/steve/2006/10/19/hough-transform-coordinate-system/">Hough transforms</a>, <a href="http://blogs.mathworks.com/steve/2006/10/04/separable-convolution/">separable convolution</a>, and <a href="http://blogs.mathworks.com/steve/2006/10/23/nonflat-grayscale-dilation-and-erosion/">nonflat grayscale dilation and erosion</a>.
   </p>
   <p>And I finally ended ten years of public silence about the <a href="http://blogs.mathworks.com/steve/2006/10/17/the-story-behind-the-matlab-default-image/">MATLAB default image</a>.
   </p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/87/default_image_story_03.jpg"> </p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/87/default_image_story_04.jpg"> </p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/87/default_image_story_12.jpg"> </p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/87/default_image_story_13.jpg"> </p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/87/default_image_story_14.jpg"> </p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/87/default_image_story_07.jpg"> </p>
   <p>Be sure to check out the <a href="http://blogs.mathworks.com/steve/archives/">blog archives</a> for more oldies but goodies.
   </p><script language="JavaScript">
<!--

    function grabCode_0833acd73e354c1b9da78458dd874d6b() {
        // Remember the title so we can use it in the new page
        title = document.title;

        // Break up these strings so that their presence
        // in the Javascript doesn't mess up the search for
        // the MATLAB code.
        t1='0833acd73e354c1b9da78458dd874d6b ' + '##### ' + 'SOURCE BEGIN' + ' #####';
        t2='##### ' + 'SOURCE END' + ' #####' + ' 0833acd73e354c1b9da78458dd874d6b';
    
        b=document.getElementsByTagName('body')[0];
        i1=b.innerHTML.indexOf(t1)+t1.length;
        i2=b.innerHTML.indexOf(t2);
 
        code_string = b.innerHTML.substring(i1, i2);
        code_string = code_string.replace(/REPLACE_WITH_DASH_DASH/g,'--');

        // Use /x3C/g instead of the less-than character to avoid errors 
        // in the XML parser.
        // Use '\x26#60;' instead of '<' so that the XML parser
        // doesn't go ahead and substitute the less-than character. 
        code_string = code_string.replace(/\x3C/g, '\x26#60;');

        author = 'Steve Eddins';
        copyright = 'Copyright 2012 The MathWorks, Inc.';

        w = window.open();
        d = w.document;
        d.write('<pre>\n');
        d.write(code_string);

        // Add author and copyright lines at the bottom if specified.
        if ((author.length > 0) || (copyright.length > 0)) {
            d.writeln('');
            d.writeln('%%');
            if (author.length > 0) {
                d.writeln('% _' + author + '_');
            }
            if (copyright.length > 0) {
                d.writeln('% _' + copyright + '_');
            }
        }

        d.write('</pre>\n');
      
      d.title = title + ' (MATLAB code)';
      d.close();
      }   
      
-->
</script><p style="text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray"><br /><a href="javascript:grabCode_0833acd73e354c1b9da78458dd874d6b()"><span style="font-size: x-small;        font-style: italic;">Get 
            the MATLAB code 
            <noscript>(requires JavaScript)</noscript></span></a><br /><br />
      Published with MATLAB&reg; 7.13<br /></p>
</div>
<!--
0833acd73e354c1b9da78458dd874d6b ##### SOURCE BEGIN #####
%%
% Much of the information I posted in this blog years ago is still useful
% today. Image processing theory hasn't been completely been overturned
% since then, and I'm still talking about MATLAB after all. For the benefit
% of readers who have joined the party more recently, I will occasionally
% recap the useful bits that I posted about five years ago.
%
% In August, September, and October of 2006 I finally finished (mostly) a
% long series of posts covering
% <http://blogs.mathworks.com/steve/category/spatial-transforms/ spatial
% transforms>. I posed a
% <http://blogs.mathworks.com/steve/2006/08/08/reader-challenge-custom-spatial-transformations/
% reader challenge> to come up with a particularly interesting and
% creative custom spatial transformation. My favorite
% <http://blogs.mathworks.com/steve/2006/08/22/responses-to-reader-challenge/
% submission> was this one:
%
% <<http://blogs.mathworks.com/images/steve/80/reader_challenge_03.jpg>>
%
% I told the story of how we
% <http://blogs.mathworks.com/steve/2006/08/11/discrete-cosine-transforms-jpeg-and-software-compatibility/
% guessed wrong> in the early 1990s about which of the several variants of
% the discrete cosine transform we should use in the Image Processing
% Toolbox.
%
% I
% <http://blogs.mathworks.com/steve/2006/09/20/unusual-red-eye-reduction-technique/
% poked fun> at advertising techniques for digital cameras.
%
% <<http://blogs.mathworks.com/images/steve/85/red_eye_reduction_cropped.jpg>>
%
% I taught about several image processing concepts, including
% <http://blogs.mathworks.com/steve/2006/10/19/hough-transform-coordinate-system/
% Hough transforms>,
% <http://blogs.mathworks.com/steve/2006/10/04/separable-convolution/ separable
% convolution>, and
% <http://blogs.mathworks.com/steve/2006/10/23/nonflat-grayscale-dilation-and-erosion/
% nonflat grayscale dilation and erosion>.
%
% And I finally ended ten years of public silence about the
% <http://blogs.mathworks.com/steve/2006/10/17/the-story-behind-the-matlab-default-image/
% MATLAB default image>.
%
% <<http://blogs.mathworks.com/images/steve/87/default_image_story_03.jpg>>
%
% <<http://blogs.mathworks.com/images/steve/87/default_image_story_04.jpg>>
%
% <<http://blogs.mathworks.com/images/steve/87/default_image_story_12.jpg>>
%
% <<http://blogs.mathworks.com/images/steve/87/default_image_story_13.jpg>>
%
% <<http://blogs.mathworks.com/images/steve/87/default_image_story_14.jpg>>
%
% <<http://blogs.mathworks.com/images/steve/87/default_image_story_07.jpg>>
%
% Be sure to check out the <http://blogs.mathworks.com/steve/archives/ blog
% archives> for more oldies but goodies.

##### SOURCE END ##### 0833acd73e354c1b9da78458dd874d6b
--><img src="http://feeds.feedburner.com/~r/SteveOnImageProcessing/~4/FoV5SU6TFtc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blogs.mathworks.com/steve/2012/01/13/five-years-ago-august-september-and-october-2006/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blogs.mathworks.com/steve/2012/01/13/five-years-ago-august-september-and-october-2006/</feedburner:origLink></item>
		<item>
		<title>Modifying centroid locations in an image – an application of linear indexing</title>
		<link>http://feedproxy.google.com/~r/SteveOnImageProcessing/~3/PYkqk3UB0D8/</link>
		<comments>http://blogs.mathworks.com/steve/2011/12/28/modifying-centroid-locations-in-an-image-an-application-of-linear-indexing/#comments</comments>
		<pubDate>Wed, 28 Dec 2011 12:00:45 +0000</pubDate>
		<dc:creator>Steve Eddins</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blogs.mathworks.com/steve/?p=434</guid>
		<description><![CDATA[Blog reader Mike posed the following question recently: If you have a bunch of point locations (for example, object centroids), how you make a binary image containing just those points? For example, consider this image:bw = imread('text.png'); imshow(bw, 'InitialMagnification', 200) How can we make an image like this, where the dots are located at the [...]]]></description>
			<content:encoded><![CDATA[<div xmlns:mwsh="http://www.mathworks.com/namespace/mcode/v1/syntaxhighlight.dtd" class="content">
   <p>Blog reader Mike posed the following question <a href="http://blogs.mathworks.com/steve/2006/06/10/determining-point-position-in-mri-phantom/#comment-24640">recently</a>:
   </p>
   <p>If you have a bunch of point locations (for example, object centroids), how you make a binary image containing just those
      points?
   </p>
   <p>For example, consider this image:</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">bw = imread(<span style="color: #A020F0">'text.png'</span>);
imshow(bw, <span style="color: #A020F0">'InitialMagnification'</span>, 200)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/centroids_on_image_01.png"> <p>How can we make an image like this, where the dots are located at the centroids of the objects?</p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/centroids_on_image_02.png"> </p>
   <p>Solving this problem is a nice application of linear indexing, something I wrote about in this blog a <a href="http://blogs.mathworks.com/steve/2008/02/08/linear-indexing/">long time ago</a>. Let's see how it can work for us here.
   </p>
   <p>First, let's find the centroids using <tt>regionprops</tt>:
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">s = regionprops(bw, <span style="color: #A020F0">'Centroid'</span>);</pre><p><tt>s</tt> is a struct array. Since we just asked for one measurement, the centroid, each element of s is a struct containing just one
      field, <tt>'Centroid'</tt>.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">s(1)</pre><pre style="font-style:oblique">ans = 
    Centroid: [11 13.5000]
</pre><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">s(2)</pre><pre style="font-style:oblique">ans = 
    Centroid: [7.6829 38.1707]
</pre><p>The length of <tt>s</tt> is the number of objects in the image.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">num_objects = length(s)</pre><pre style="font-style:oblique">num_objects =
    88
</pre><p>Next, we gather all the individual centroid locations into x and y vectors. To accomplish this I use the <a href="http://www.mathworks.com/help/releases/R2011b/techdoc/matlab_prog/br04bw6-38.html#bs6e2p_">comma-separated list syntax for struct arrays</a>.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">centroids = cat(1, s.Centroid);
x = centroids(:,1);
y = centroids(:,2);</pre><p>If the comma-separated list syntax makes your brain hurt, you can use a loop instead:</p><pre>  centroids = zeros(length(s), 2);
  for k = 1:length(s)
      centroids(k,:) = s(k).Centroid;
  end</pre><p>Now let's round the centroid locations to get row and column subscripts.</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">r = round(y);
c = round(x);</pre><p>Here's where linear indexing comes into play. In order to assign to a bunch of scattered locations like this, you want to
      use a single subscript. That's what we call linear indexing. You can use the function <tt>sub2ind</tt> to convert a set of subscripts to linear indices.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">ind = sub2ind(size(bw), r, c);</pre><p>And finally we can use the linear indices to assign a value to a bunch of image pixel locations all at once.</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">bw2 = false(size(bw));
bw2(ind) = true;

imshow(bw2, <span style="color: #A020F0">'InitialMagnification'</span>, 200)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/centroids_on_image_02.png"> <p>See my <a href="http://blogs.mathworks.com/steve/2008/02/08/linear-indexing/">08-Feb-2008 blog post</a> for more about linear indexing.
   </p><script language="JavaScript">
<!--

    function grabCode_a30b78c4c45049fcb395b0d7d31e1d1c() {
        // Remember the title so we can use it in the new page
        title = document.title;

        // Break up these strings so that their presence
        // in the Javascript doesn't mess up the search for
        // the MATLAB code.
        t1='a30b78c4c45049fcb395b0d7d31e1d1c ' + '##### ' + 'SOURCE BEGIN' + ' #####';
        t2='##### ' + 'SOURCE END' + ' #####' + ' a30b78c4c45049fcb395b0d7d31e1d1c';
    
        b=document.getElementsByTagName('body')[0];
        i1=b.innerHTML.indexOf(t1)+t1.length;
        i2=b.innerHTML.indexOf(t2);
 
        code_string = b.innerHTML.substring(i1, i2);
        code_string = code_string.replace(/REPLACE_WITH_DASH_DASH/g,'--');

        // Use /x3C/g instead of the less-than character to avoid errors 
        // in the XML parser.
        // Use '\x26#60;' instead of '<' so that the XML parser
        // doesn't go ahead and substitute the less-than character. 
        code_string = code_string.replace(/\x3C/g, '\x26#60;');

        author = 'Steve Eddins';
        copyright = 'Copyright 2011 The MathWorks, Inc.';

        w = window.open();
        d = w.document;
        d.write('<pre>\n');
        d.write(code_string);

        // Add author and copyright lines at the bottom if specified.
        if ((author.length > 0) || (copyright.length > 0)) {
            d.writeln('');
            d.writeln('%%');
            if (author.length > 0) {
                d.writeln('% _' + author + '_');
            }
            if (copyright.length > 0) {
                d.writeln('% _' + copyright + '_');
            }
        }

        d.write('</pre>\n');
      
      d.title = title + ' (MATLAB code)';
      d.close();
      }   
      
-->
</script><p style="text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray"><br /><a href="javascript:grabCode_a30b78c4c45049fcb395b0d7d31e1d1c()"><span style="font-size: x-small;        font-style: italic;">Get 
            the MATLAB code 
            <noscript>(requires JavaScript)</noscript></span></a><br /><br />
      Published with MATLAB&reg; 7.13<br /></p>
</div>
<!--
a30b78c4c45049fcb395b0d7d31e1d1c ##### SOURCE BEGIN #####
%%
% Blog reader Mike posed the following question
% <http://blogs.mathworks.com/steve/2006/06/10/determining-point-position-in-mri-phantom/#comment-24640
% recently>:
%
% If you have a bunch of point locations (for example, object centroids),
% how you make a binary image containing just those points?
%
% For example, consider this image:

bw = imread('text.png');
imshow(bw, 'InitialMagnification', 200)

%%
% How can we make an image like this, where the dots are located at the
% centroids of the objects?
%
% <<http://blogs.mathworks.com/images/steve/2011/centroids_on_image_02.png>>
%
% Solving this problem is a nice application of linear indexing, something
% I wrote about in this blog a
% <http://blogs.mathworks.com/steve/2008/02/08/linear-indexing/ long time
% ago>. Let's see how it can work for us here.
%
% First, let's find the centroids using |regionprops|:

s = regionprops(bw, 'Centroid');

%%
% |s| is a struct array. Since we just asked for one measurement, the
% centroid, each element of s is a struct containing just one field,
% |'Centroid'|.

s(1)

%%

s(2)

%%
% The length of |s| is the number of objects in the image.

num_objects = length(s)

%%
% Next, we gather all the individual centroid locations into x and y
% vectors. To accomplish this I use the
% <http://www.mathworks.com/help/releases/R2011b/techdoc/matlab_prog/br04bw6-38.html#bs6e2p_
% comma-separated list syntax for struct arrays>.

centroids = cat(1, s.Centroid);
x = centroids(:,1);
y = centroids(:,2);

%%
% If the comma-separated list syntax makes your brain hurt, you can use a
% loop instead:
%
%    centroids = zeros(length(s), 2);
%    for k = 1:length(s)
%        centroids(k,:) = s(k).Centroid;
%    end

%%
% Now let's round the centroid locations to get row and column subscripts.

r = round(y);
c = round(x);

%%
% Here's where linear indexing comes into play. In order to assign to a
% bunch of scattered locations like this, you want to use a single
% subscript. That's what we call linear indexing. You can use the function
% |sub2ind| to convert a set of subscripts to linear indices.

ind = sub2ind(size(bw), r, c);

%%
% And finally we can use the linear indices to assign a value to a bunch of
% image pixel locations all at once.

bw2 = false(size(bw));
bw2(ind) = true;

imshow(bw2, 'InitialMagnification', 200)

%%
% See my <http://blogs.mathworks.com/steve/2008/02/08/linear-indexing/
% 08-Feb-2008 blog post> for more about linear indexing.



##### SOURCE END ##### a30b78c4c45049fcb395b0d7d31e1d1c
--><img src="http://feeds.feedburner.com/~r/SteveOnImageProcessing/~4/PYkqk3UB0D8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blogs.mathworks.com/steve/2011/12/28/modifying-centroid-locations-in-an-image-an-application-of-linear-indexing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blogs.mathworks.com/steve/2011/12/28/modifying-centroid-locations-in-an-image-an-application-of-linear-indexing/</feedburner:origLink></item>
		<item>
		<title>Batch processing files in another folder</title>
		<link>http://feedproxy.google.com/~r/SteveOnImageProcessing/~3/09la88JUQzY/</link>
		<comments>http://blogs.mathworks.com/steve/2011/12/23/batch-processing-files-in-another-folder/#comments</comments>
		<pubDate>Fri, 23 Dec 2011 12:00:31 +0000</pubDate>
		<dc:creator>Steve Eddins</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blogs.mathworks.com/steve/?p=430</guid>
		<description><![CDATA[Blog reader Asadullah posted the following question last week on my old post about batch processing: I am trying to process some images by following the MATLAB demo. After getting the names of files when I try to see any of the files then it gives the error. The detail is as follows: &#62;&#62; fileFolder [...]]]></description>
			<content:encoded><![CDATA[<div xmlns:mwsh="http://www.mathworks.com/namespace/mcode/v1/syntaxhighlight.dtd" class="content">
   <p>Blog reader Asadullah posted the following question last week on my old post about <a href="http://blogs.mathworks.com/steve/2006/06/06/batch-processing/">batch processing</a>:
   </p>
   <p><i>I am trying to process some images by following the MATLAB demo. After getting the names of files when I try to see any of
         the files then it gives the error. The detail is as follows:</i></p><pre> &gt;&gt; fileFolder = fullfile(matlabroot,'work','original');
 &gt;&gt; dirOutput = dir(fullfile(fileFolder,'AT3_1m4_*.jpg'));
 &gt;&gt; fileNames = {dirOutput.name}'</pre><pre> fileNames =</pre><pre>     'AT3_1m4_001.jpg'
     'AT3_1m4_002.jpg'
     'AT3_1m4_003.jpg'
     'AT3_1m4_004.jpg'
     'AT3_1m4_005.jpg'
     'AT3_1m4_006.jpg'
     'AT3_1m4_007.jpg'</pre><pre>     [...]</pre><pre>     'AT3_1m4_116.jpg'
     'AT3_1m4_117.jpg'
     'AT3_1m4_118.jpg'
     'AT3_1m4_119.jpg'
     'AT3_1m4_120.jpg'</pre><pre> &gt;&gt; I=imread(fileNames{1});
 ??? Error using ==&gt; imread
 File "AT3_1m4_001.jpg" does not exist.</pre><p><i>I could not understand where is the problem? Please help me to solve this.</i></p>
   <p>Several people have asked me this same question, so I thought I should post the answer so everyone can see it instead of replying
      in a comment.
   </p>
   <p>First, though, I want to strongly recommend against placing your own work files (data, code, etc.) underneath the MATLAB program
      folder, as the Asadullah's code shows in this line:
   </p><pre>   fileFolder = fullfile(matlabroot,'work','original');</pre><p>The function <tt>matlabroot</tt> returns the location of the MATLAB program folder. On a typical Windows computer, for example, this location might be something
      like:
   </p><pre>   C:\Program Files\MATLAB\R2011b</pre><p>Windows doesn't consider this to be an area where a user will store their own files, and most backup programs will not normally
      back up files that are stored here. Instead, store your own work elsewhere. On Windows, a good choice is somewhere under "My
      Documents".
   </p>
   <p>OK, now back to the question at hand. The problem is that the string being passed to <tt>imread</tt> contains only the filename, such as 'AT3_1m4_116.jpg', and not the full folder location. That's not enough information for
      <tt>imread</tt> to be able to find the file.
   </p>
   <p>You have to add the full folder location to the image filename before passing it to <tt>imread</tt>. In Asadullah's example, the corrected code would look something like this:
   </p><pre>   I = imread(fullfile(fileFolder,fileNames{1}));</pre><p>Hope that helps! May your days be filled with happily processing large numbers of images.</p><script language="JavaScript">
<!--

    function grabCode_548a2ec3f09f49ed99b5217ee89c3bdd() {
        // Remember the title so we can use it in the new page
        title = document.title;

        // Break up these strings so that their presence
        // in the Javascript doesn't mess up the search for
        // the MATLAB code.
        t1='548a2ec3f09f49ed99b5217ee89c3bdd ' + '##### ' + 'SOURCE BEGIN' + ' #####';
        t2='##### ' + 'SOURCE END' + ' #####' + ' 548a2ec3f09f49ed99b5217ee89c3bdd';
    
        b=document.getElementsByTagName('body')[0];
        i1=b.innerHTML.indexOf(t1)+t1.length;
        i2=b.innerHTML.indexOf(t2);
 
        code_string = b.innerHTML.substring(i1, i2);
        code_string = code_string.replace(/REPLACE_WITH_DASH_DASH/g,'--');

        // Use /x3C/g instead of the less-than character to avoid errors 
        // in the XML parser.
        // Use '\x26#60;' instead of '<' so that the XML parser
        // doesn't go ahead and substitute the less-than character. 
        code_string = code_string.replace(/\x3C/g, '\x26#60;');

        author = 'Steve Eddins';
        copyright = 'Copyright 2011 The MathWorks, Inc.';

        w = window.open();
        d = w.document;
        d.write('<pre>\n');
        d.write(code_string);

        // Add author and copyright lines at the bottom if specified.
        if ((author.length > 0) || (copyright.length > 0)) {
            d.writeln('');
            d.writeln('%%');
            if (author.length > 0) {
                d.writeln('% _' + author + '_');
            }
            if (copyright.length > 0) {
                d.writeln('% _' + copyright + '_');
            }
        }

        d.write('</pre>\n');
      
      d.title = title + ' (MATLAB code)';
      d.close();
      }   
      
-->
</script><p style="text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray"><br /><a href="javascript:grabCode_548a2ec3f09f49ed99b5217ee89c3bdd()"><span style="font-size: x-small;        font-style: italic;">Get 
            the MATLAB code 
            <noscript>(requires JavaScript)</noscript></span></a><br /><br />
      Published with MATLAB&reg; 7.13<br /></p>
</div>
<!--
548a2ec3f09f49ed99b5217ee89c3bdd ##### SOURCE BEGIN #####
%%
% Blog reader Asadullah posted the following question last week on my old
% post about <http://blogs.mathworks.com/steve/2006/06/06/batch-processing/
% batch processing>:
%
% _I am trying to process some images by following the MATLAB demo. After
% getting the names of files when I try to see any of the files then it
% gives the error. The detail is as follows:_
%
%   >> fileFolder = fullfile(matlabroot,'work','original');
%   >> dirOutput = dir(fullfile(fileFolder,'AT3_1m4_*.jpg'));
%   >> fileNames = {dirOutput.name}'
%   
%   fileNames = 
%   
%       'AT3_1m4_001.jpg'
%       'AT3_1m4_002.jpg'
%       'AT3_1m4_003.jpg'
%       'AT3_1m4_004.jpg'
%       'AT3_1m4_005.jpg'
%       'AT3_1m4_006.jpg'
%       'AT3_1m4_007.jpg'
%   
%       [...]
%   
%       'AT3_1m4_116.jpg'
%       'AT3_1m4_117.jpg'
%       'AT3_1m4_118.jpg'
%       'AT3_1m4_119.jpg'
%       'AT3_1m4_120.jpg'
%   
%   >> I=imread(fileNames{1});
%   ??? Error using ==> imread
%   File "AT3_1m4_001.jpg" does not exist.
%   
%
% _I could not understand where is the problem? Please help me to solve
% this._
%
% Several people have asked me this same question, so I thought I should
% post the answer so everyone can see it instead of replying in a comment.
%
% First, though, I want to strongly recommend against placing your own work
% files (data, code, etc.) underneath the MATLAB program folder, as
% the Asadullah's code shows in this line:
%
%     fileFolder = fullfile(matlabroot,'work','original');
%     
%
% The function |matlabroot| returns the location of the MATLAB program
% folder. On a typical Windows computer, for example, this location might
% be something like:
%
%     C:\Program Files\MATLAB\R2011b
%     
%
% Windows doesn't consider this to be an area where a user will store their
% own files, and most backup programs will not normally back up files that
% are stored here. Instead, store your own work elsewhere. On Windows, a
% good choice is somewhere under "My Documents".
%
% OK, now back to the question at hand. The problem is that the string
% being passed to |imread| contains only the filename, such as
% 'AT3_1m4_116.jpg', and not the full folder location. That's not enough
% information for |imread| to be able to find the file.
%
% You have to add the full folder location to the image filename before
% passing it to |imread|. In Asadullah's example, the corrected code would
% look something like this:
%
%     I = imread(fullfile(fileFolder,fileNames{1}));
%     
%
% Hope that helps! May your days be filled with happily processing large
% numbers of images.

##### SOURCE END ##### 548a2ec3f09f49ed99b5217ee89c3bdd
--><img src="http://feeds.feedburner.com/~r/SteveOnImageProcessing/~4/09la88JUQzY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blogs.mathworks.com/steve/2011/12/23/batch-processing-files-in-another-folder/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blogs.mathworks.com/steve/2011/12/23/batch-processing-files-in-another-folder/</feedburner:origLink></item>
		<item>
		<title>Exploring shortest paths – wrapping up</title>
		<link>http://feedproxy.google.com/~r/SteveOnImageProcessing/~3/PK3tyjwCCe4/</link>
		<comments>http://blogs.mathworks.com/steve/2011/12/21/exploring-shortest-paths-wrapping-up/#comments</comments>
		<pubDate>Wed, 21 Dec 2011 13:20:18 +0000</pubDate>
		<dc:creator>Steve Eddins</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blogs.mathworks.com/steve/?p=419</guid>
		<description><![CDATA[For the past several weeks I've been writing about shortest-path problems in image processing: finding the shortest path between two points in an image, with and without constraints. Applications included the practical (path finding in a skeleton image) and fun (maze solving). Along the way, I've described: the basic idea of finding shortest paths by [...]]]></description>
			<content:encoded><![CDATA[<div xmlns:mwsh="http://www.mathworks.com/namespace/mcode/v1/syntaxhighlight.dtd" class="content">
   <p>For the past several weeks I've been writing about shortest-path problems in image processing: finding the shortest path between
      two points in an image, with and without constraints.
   </p>
   <p>Applications included the practical (path finding in a skeleton image) and fun (maze solving).</p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_e_10.png"> </p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_e_06.png"> </p>
   <p>Along the way, I've described:</p>
   <div>
      <ul>
         <li>the basic idea of finding shortest paths by adding two distance transforms together (<a href="http://blogs.mathworks.com/steve/2011/11/01/exploring-shortest-paths-part-1/">part 1</a>)
         </li>
         <li>the nonuniqueness of the shortest paths (<a href="http://blogs.mathworks.com/steve/2011/11/26/exploring-shortest-paths-part-2/">part 2</a>)
         </li>
         <li>handling floating-point round-off effects (<a href="http://blogs.mathworks.com/steve/2011/12/02/exploring-shortest-paths-part-3/">part 3</a>)
         </li>
         <li>using thinning to pick out a single path (<a href="http://blogs.mathworks.com/steve/2011/12/06/exploring-shortest-paths-part-4/">part 4</a>)
         </li>
         <li>using <tt>bwdistgeodesic</tt> to find shortest paths subject to constraint (<a href="http://blogs.mathworks.com/steve/2011/12/13/exploring-shortest-paths-part-5/">part 5</a>)
         </li>
      </ul>
   </div>
   <p>I hope you found the series interesting and useful. If you have particular applications for these techniques in your own work,
      I invite you to share them with us by posting a comment.
   </p><script language="JavaScript">
<!--

    function grabCode_c4fd7b6597054f7c8fcd25002b48b782() {
        // Remember the title so we can use it in the new page
        title = document.title;

        // Break up these strings so that their presence
        // in the Javascript doesn't mess up the search for
        // the MATLAB code.
        t1='c4fd7b6597054f7c8fcd25002b48b782 ' + '##### ' + 'SOURCE BEGIN' + ' #####';
        t2='##### ' + 'SOURCE END' + ' #####' + ' c4fd7b6597054f7c8fcd25002b48b782';
    
        b=document.getElementsByTagName('body')[0];
        i1=b.innerHTML.indexOf(t1)+t1.length;
        i2=b.innerHTML.indexOf(t2);
 
        code_string = b.innerHTML.substring(i1, i2);
        code_string = code_string.replace(/REPLACE_WITH_DASH_DASH/g,'--');

        // Use /x3C/g instead of the less-than character to avoid errors 
        // in the XML parser.
        // Use '\x26#60;' instead of '<' so that the XML parser
        // doesn't go ahead and substitute the less-than character. 
        code_string = code_string.replace(/\x3C/g, '\x26#60;');

        author = 'Steve Eddins';
        copyright = 'Copyright 2011 The MathWorks, Inc.';

        w = window.open();
        d = w.document;
        d.write('<pre>\n');
        d.write(code_string);

        // Add author and copyright lines at the bottom if specified.
        if ((author.length > 0) || (copyright.length > 0)) {
            d.writeln('');
            d.writeln('%%');
            if (author.length > 0) {
                d.writeln('% _' + author + '_');
            }
            if (copyright.length > 0) {
                d.writeln('% _' + copyright + '_');
            }
        }

        d.write('</pre>\n');
      
      d.title = title + ' (MATLAB code)';
      d.close();
      }   
      
-->
</script><p style="text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray"><br /><a href="javascript:grabCode_c4fd7b6597054f7c8fcd25002b48b782()"><span style="font-size: x-small;        font-style: italic;">Get 
            the MATLAB code 
            <noscript>(requires JavaScript)</noscript></span></a><br /><br />
      Published with MATLAB&reg; 7.13<br /></p>
</div>
<!--
c4fd7b6597054f7c8fcd25002b48b782 ##### SOURCE BEGIN #####
%%
% For the past several weeks I've been writing about shortest-path problems
% in image processing: finding the shortest path between two points in an
% image, with and without constraints.
%
% Applications included the practical (path finding in a skeleton image)
% and fun (maze solving).
%
% <<http://blogs.mathworks.com/images/steve/2011/shortest_path_e_10.png>>
%
% <<http://blogs.mathworks.com/images/steve/2011/shortest_path_e_06.png>>
%
% Along the way, I've described:
%
% * the basic idea of finding shortest paths by adding two distance
% transforms together
% (<http://blogs.mathworks.com/steve/2011/11/01/exploring-shortest-paths-part-1/
% part 1>)
% * the nonuniqueness of the shortest paths
% (<http://blogs.mathworks.com/steve/2011/11/26/exploring-shortest-paths-part-2/
% part 2>)
% * handling floating-point round-off effects
% (<http://blogs.mathworks.com/steve/2011/12/02/exploring-shortest-paths-part-3/
% part 3>)
% * using thinning to pick out a single path
% (<http://blogs.mathworks.com/steve/2011/12/06/exploring-shortest-paths-part-4/
% part 4>)
% * using |bwdistgeodesic| to find shortest paths subject to constraint
% (<http://blogs.mathworks.com/steve/2011/12/13/exploring-shortest-paths-part-5/
% part 5>)
%
% I hope you found the series interesting and useful. If you have
% particular applications for these techniques in your own work, I
% invite you to share them with us by posting a comment.
##### SOURCE END ##### c4fd7b6597054f7c8fcd25002b48b782
--><img src="http://feeds.feedburner.com/~r/SteveOnImageProcessing/~4/PK3tyjwCCe4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blogs.mathworks.com/steve/2011/12/21/exploring-shortest-paths-wrapping-up/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blogs.mathworks.com/steve/2011/12/21/exploring-shortest-paths-wrapping-up/</feedburner:origLink></item>
		<item>
		<title>Exploring shortest paths – part 5</title>
		<link>http://feedproxy.google.com/~r/SteveOnImageProcessing/~3/0DMRYHoF7Go/</link>
		<comments>http://blogs.mathworks.com/steve/2011/12/13/exploring-shortest-paths-part-5/#comments</comments>
		<pubDate>Tue, 13 Dec 2011 07:00:45 +0000</pubDate>
		<dc:creator>Steve Eddins</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blogs.mathworks.com/steve/?p=399</guid>
		<description><![CDATA[In this post in the Exploring shortest paths series, I make things a little more complicated (and interesting) by adding constraints to the shortest path problem. Last time, I showed this example of finding the shortest paths between the "T" and the sideways "s" in this image: url = 'http://blogs.mathworks.com/images/steve/2011/two-letters-cropped.png'; bw = imread(url);And this was [...]]]></description>
			<content:encoded><![CDATA[<div xmlns:mwsh="http://www.mathworks.com/namespace/mcode/v1/syntaxhighlight.dtd" class="content">
   <p>In this post in the <i>Exploring shortest paths</i> series, I make things a little more complicated (and interesting) by adding constraints to the shortest path problem. Last
      time, I showed this example of finding the shortest paths between the "T" and the sideways "s" in this image:
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">url = <span style="color: #A020F0">'http://blogs.mathworks.com/images/steve/2011/two-letters-cropped.png'</span>;
bw = imread(url);</pre><p>And this was the result:</p>
   <p><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_d_05.png"> </p>
   <p>What if we complicate the problem by adding other objects to the image and looking for the shortest path that avoids these
      other objects?
   </p>
   <p>For example, what if we want to find a shortest path between the "T" and the sideways "s" without going through any of other
      letters between them in the image below?
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">url = <span style="color: #A020F0">'http://blogs.mathworks.com/images/steve/2011/text-cropped.png'</span>;
text = imread(url);
imshow(text, <span style="color: #A020F0">'InitialMagnification'</span>, <span style="color: #A020F0">'fit'</span>)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_e_01.png"> <p>We can do this by using <tt>bwdistgeodesic</tt> instead of <tt>bwdist</tt> in our algorithm. <tt>bwdistgeodesic</tt> is a new function in the R2011b release of the Image Processing Toolbox. In addition to taking a binary image input that
      <tt>bwdist</tt> takes, it takes another argument that specifies which pixels the paths are allowed to traverse.
   </p>
   <p>Let's use our text image to make a mask of allowed path pixels.</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">mask = ~text | bw;
imshow(mask, <span style="color: #A020F0">'InitialMagnification'</span>, <span style="color: #A020F0">'fit'</span>)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_e_02.png"> <p>Next, we make our two binary object images as before.</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">L = bwlabel(bw);
bw1 = (L == 1);
bw2 = (L == 2);</pre><p>Next, instead of using <tt>bwdist</tt>, we use <tt>bwdistgeodesic</tt>.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">D1 = bwdistgeodesic(mask, bw1, <span style="color: #A020F0">'quasi-euclidean'</span>);
D2 = bwdistgeodesic(mask, bw2, <span style="color: #A020F0">'quasi-euclidean'</span>);

D = D1 + D2;
D = round(D * 8) / 8;

D(isnan(D)) = inf;
paths = imregionalmin(D);

paths_thinned_many = bwmorph(paths, <span style="color: #A020F0">'thin'</span>, inf);
P = false(size(bw));
P = imoverlay(P, ~mask, [1 0 0]);
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, paths_thinned_many, [1 1 0]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, <span style="color: #A020F0">'InitialMagnification'</span>, <span style="color: #A020F0">'fit'</span>)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_e_03.png"> <p>In the image above, the white characters are the starting and ending points for the path. The path is not allowed to pass
      through the red characters. The gray pixels are all the pixels on any shortest path, and the yellow pixels lie along one particular
      shortest path.
   </p>
   <p>Let's have a little fun by using this technique to solve a maze. Here's a test maze (created by <a href="http://www.billsgames.com/mazegenerator/">The Maze Generator</a>).
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">url = <span style="color: #A020F0">'http://blogs.mathworks.com/images/steve/2011/maze-51x51.png'</span>;
I = imread(url);
imshow(I)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_e_04.png"> <p>Make an image of walls by thresholding the maze image. Dilate the walls (make them a little thicker) in order to keep the
      solution path a little bit away from them.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">walls = imdilate(I &lt; 128, ones(7,7));
imshow(walls)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_e_05.png"> <p>The function <tt>bwgeodesic</tt> will take seed locations (as column and row coordinates) in addition to binary images. Below I specify the seed locations
      as the two entry points into the maze.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">D1 = bwdistgeodesic(~walls, 1, 497, <span style="color: #A020F0">'quasi-euclidean'</span>);
D2 = bwdistgeodesic(~walls, 533, 517, <span style="color: #A020F0">'quasi-euclidean'</span>);</pre><p>The rest of the procedure is the same as above.</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">D = D1 + D2;
D = round(D * 8) / 8;

D(isnan(D)) = inf;
paths = imregionalmin(D);

solution_path = bwmorph(paths, <span style="color: #A020F0">'thin'</span>, inf);
thick_solution_path = imdilate(solution_path, ones(3,3));
P = imoverlay(I, thick_solution_path, [1 0 0]);
imshow(P, <span style="color: #A020F0">'InitialMagnification'</span>, <span style="color: #A020F0">'fit'</span>)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_e_06.png"> <p>(If you're interested in other maze-solving techniques using image processing, take a look at the File Exchange contribution
      <a href="http://www.mathworks.com/matlabcentral/fileexchange/27175-mazesolution/content/Maze_Solution/maze_solution.m">maze_solution</a> by <a href="http://www.mathworks.com/matlabcentral/fileexchange/authors/31862">Image Analyst</a>.)
   </p>
   <p>In a <a href="http://blogs.mathworks.com/steve/2011/11/01/exploring-shortest-paths-part-1/#comment-24596">comment on part 1 of this series</a>, Sepideh asked this question:
   </p>
   <p>"I have a binary image which contains one pixel width skeleton of an image. I have got the skeleton using the bwmorph function.
      As you may know basically in this binary image, I have ones on the skeleton and zeros anywhere else. how can I find the shortest
      &#8216;quasi-euclidean&#8217; distance between two nonzero elements which are on the skeleton? The path should be on the skeleton itself
      too."
   </p>
   <p>The techniques described above can be applied to this path-along-a-skeleton problem. Let's try an example.</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">bw = imread(<span style="color: #A020F0">'circles.png'</span>);
imshow(bw, <span style="color: #A020F0">'InitialMagnification'</span>, 200)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_e_07.png"> <pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">skel = bwmorph(bw, <span style="color: #A020F0">'skel'</span>, Inf);
imshow(skel, <span style="color: #A020F0">'InitialMagnification'</span>, 200)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_e_08.png"> <p>Let's find the skeleton-constrained path distance between these two points.</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">r1 = 38;
c1 = 53;

r2 = 208;
c2 = 147;

hold <span style="color: #A020F0">on</span>
plot(c1, r1, <span style="color: #A020F0">'g*'</span>, <span style="color: #A020F0">'MarkerSize'</span>, 15)
plot(c2, r2, <span style="color: #A020F0">'g*'</span>, <span style="color: #A020F0">'MarkerSize'</span>, 15)
hold <span style="color: #A020F0">off</span></pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_e_09.png"> <pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">D1 = bwdistgeodesic(skel, c1, r1, <span style="color: #A020F0">'quasi-euclidean'</span>);
D2 = bwdistgeodesic(skel, c2, r2, <span style="color: #A020F0">'quasi-euclidean'</span>);

D = D1 + D2;
D = round(D * 8) / 8;

D(isnan(D)) = inf;
skeleton_path = imregionalmin(D);
P = imoverlay(skel, imdilate(skeleton_path, ones(3,3)), [1 0 0]);
imshow(P, <span style="color: #A020F0">'InitialMagnification'</span>, 200)
hold <span style="color: #A020F0">on</span>
plot(c1, r1, <span style="color: #A020F0">'g*'</span>, <span style="color: #A020F0">'MarkerSize'</span>, 15)
plot(c2, r2, <span style="color: #A020F0">'g*'</span>, <span style="color: #A020F0">'MarkerSize'</span>, 15)
hold <span style="color: #A020F0">off</span></pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_e_10.png"> <p>Finally, the skeleton path length is given by the value of <tt>D</tt> at any point along the path.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">path_length = D(skeleton_path);
path_length = path_length(1)</pre><pre style="font-style:oblique">
path_length =

  239.2500

</pre>
<h4>All the posts in this series</h4>
      <ul>
         <li>the basic idea of finding shortest paths by adding two distance transforms together (<a href="http://blogs.mathworks.com/steve/2011/11/01/exploring-shortest-paths-part-1/">part 1</a>)
         </li>
         <li>the nonuniqueness of the shortest paths (<a href="http://blogs.mathworks.com/steve/2011/11/26/exploring-shortest-paths-part-2/">part 2</a>)
         </li>
         <li>handling floating-point round-off effects (<a href="http://blogs.mathworks.com/steve/2011/12/02/exploring-shortest-paths-part-3/">part 3</a>)
         </li>
         <li>using thinning to pick out a single path (<a href="http://blogs.mathworks.com/steve/2011/12/06/exploring-shortest-paths-part-4/">part 4</a>)
         </li>
         <li>using <tt>bwdistgeodesic</tt> to find shortest paths subject to constraint (<a href="http://blogs.mathworks.com/steve/2011/12/13/exploring-shortest-paths-part-5/">part 5</a>)
         </li>
      </ul>
<script language="JavaScript">
<!--

    function grabCode_6c3df5ae559f4e679ee76128ce614074() {
        // Remember the title so we can use it in the new page
        title = document.title;

        // Break up these strings so that their presence
        // in the Javascript doesn't mess up the search for
        // the MATLAB code.
        t1='6c3df5ae559f4e679ee76128ce614074 ' + '##### ' + 'SOURCE BEGIN' + ' #####';
        t2='##### ' + 'SOURCE END' + ' #####' + ' 6c3df5ae559f4e679ee76128ce614074';
    
        b=document.getElementsByTagName('body')[0];
        i1=b.innerHTML.indexOf(t1)+t1.length;
        i2=b.innerHTML.indexOf(t2);
 
        code_string = b.innerHTML.substring(i1, i2);
        code_string = code_string.replace(/REPLACE_WITH_DASH_DASH/g,'--');

        // Use /x3C/g instead of the less-than character to avoid errors 
        // in the XML parser.
        // Use '\x26#60;' instead of '<' so that the XML parser
        // doesn't go ahead and substitute the less-than character. 
        code_string = code_string.replace(/\x3C/g, '\x26#60;');

        author = 'Steve Eddins';
        copyright = 'Copyright 2011 The MathWorks, Inc.';

        w = window.open();
        d = w.document;
        d.write('<pre>\n');
        d.write(code_string);

        // Add author and copyright lines at the bottom if specified.
        if ((author.length > 0) || (copyright.length > 0)) {
            d.writeln('');
            d.writeln('%%');
            if (author.length > 0) {
                d.writeln('% _' + author + '_');
            }
            if (copyright.length > 0) {
                d.writeln('% _' + copyright + '_');
            }
        }

        d.write('</pre>\n');
      
      d.title = title + ' (MATLAB code)';
      d.close();
      }   
      
-->
</script><p style="text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray"><br /><a href="javascript:grabCode_6c3df5ae559f4e679ee76128ce614074()"><span style="font-size: x-small;        font-style: italic;">Get 
            the MATLAB code 
            <noscript>(requires JavaScript)</noscript></span></a><br /><br />
      Published with MATLAB&reg; 7.13<br /></p>
</div>
<!--
6c3df5ae559f4e679ee76128ce614074 ##### SOURCE BEGIN #####
%%
% In this post in the _Exploring shortest paths_ series, I make things a
% little more complicated (and interesting) by adding constraints to the
% shortest path problem. Last time, I showed this example of finding the
% shortest paths between the "T" and the sideways "s" in this image:

url = 'http://blogs.mathworks.com/images/steve/2011/two-letters-cropped.png';
bw = imread(url);

%%
% And this was the result:
%
% <<http://blogs.mathworks.com/images/steve/2011/shortest_path_d_05.png>>
%
% What if we complicate the problem by adding other objects to the image
% and looking for the shortest path that avoids these other objects?
%
% For example, what if we want to find a shortest path between the "T" and
% the sideways "s" without going through any of other letters between them
% in the image below?

url = 'http://blogs.mathworks.com/images/steve/2011/text-cropped.png';
text = imread(url);
imshow(text, 'InitialMagnification', 'fit')

%%
% We can do this by using |bwdistgeodesic| instead of |bwdist| in our
% algorithm. |bwdistgeodesic| is a new function in the R2011b release of
% the Image Processing Toolbox. In addition to taking a binary image input
% that |bwdist| takes, it takes another argument that specifies which
% pixels the paths are allowed to traverse.
%
% Let's use our text image to make a mask of allowed path pixels.

mask = ~text | bw;
imshow(mask, 'InitialMagnification', 'fit')

%%
% Next, we make our two binary object images as before.

L = bwlabel(bw);
bw1 = (L == 1);
bw2 = (L == 2);

%%
% Next, instead of using |bwdist|, we use |bwdistgeodesic|.
D1 = bwdistgeodesic(mask, bw1, 'quasi-euclidean');
D2 = bwdistgeodesic(mask, bw2, 'quasi-euclidean');

D = D1 + D2;
D = round(D * 8) / 8;

D(isnan(D)) = inf;
paths = imregionalmin(D);

paths_thinned_many = bwmorph(paths, 'thin', inf);
P = false(size(bw));
P = imoverlay(P, ~mask, [1 0 0]);
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, paths_thinned_many, [1 1 0]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, 'InitialMagnification', 'fit')

%%
% In the image above, the white characters are the starting and ending
% points for the path. The path is not allowed to pass through the red
% characters. The gray pixels are all the pixels on any shortest path, and
% the yellow pixels lie along one particular shortest path.
%
% Let's have a little fun by using this technique to solve a maze. Here's a
% test maze (created by <http://www.billsgames.com/mazegenerator/ The Maze
% Generator>).

%%
url = 'http://blogs.mathworks.com/images/steve/2011/maze-51x51.png';
I = imread(url);
imshow(I)

%%
% Make an image of walls by thresholding the maze image. Dilate the walls
% (make them a little thicker) in order to keep the solution path a little
% bit away from them.

walls = imdilate(I < 128, ones(7,7));
imshow(walls)

%%
% The function |bwgeodesic| will take seed locations (as column and row
% coordinates) in addition to binary images. Below I specify the seed
% locations as the two entry points into the maze. 

D1 = bwdistgeodesic(~walls, 1, 497, 'quasi-euclidean');
D2 = bwdistgeodesic(~walls, 533, 517, 'quasi-euclidean');

%%
% The rest of the procedure is the same as above.

D = D1 + D2;
D = round(D * 8) / 8;

D(isnan(D)) = inf;
paths = imregionalmin(D);

solution_path = bwmorph(paths, 'thin', inf);
thick_solution_path = imdilate(solution_path, ones(3,3));
P = imoverlay(I, thick_solution_path, [1 0 0]);
imshow(P, 'InitialMagnification', 'fit')

%%
% (If you're interested in other maze-solving techniques using image
% processing, take a look at the File Exchange contribution
% <http://www.mathworks.com/matlabcentral/fileexchange/27175-mazesolution/content/Maze_Solution/maze_solution.m
% maze_solution> by
% <http://www.mathworks.com/matlabcentral/fileexchange/authors/31862 Image
% Analyst>.)
%
% In a
% <http://blogs.mathworks.com/steve/2011/11/01/exploring-shortest-paths-part-1/#comment-24596
% comment on part 1 of this series>, Sepideh asked this question:
%
% "I have a binary image which contains one pixel width skeleton of an
% image. I have got the skeleton using the bwmorph function. As you may
% know basically in this binary image, I have ones on the skeleton and
% zeros anywhere else. how can I find the shortest â€˜quasi-euclideanâ€™
% distance between two nonzero elements which are on the skeleton? The path
% should be on the skeleton itself too."
%
% The techniques described above can be applied to this
% path-along-a-skeleton problem. Let's try an example.

bw = imread('circles.png');
imshow(bw, 'InitialMagnification', 200)

%%
skel = bwmorph(bw, 'skel', Inf);
imshow(skel, 'InitialMagnification', 200)

%%
% Let's find the skeleton-constrained path distance between these two
% points.

r1 = 38;
c1 = 53;

r2 = 208;
c2 = 147;

hold on
plot(c1, r1, 'g*', 'MarkerSize', 15)
plot(c2, r2, 'g*', 'MarkerSize', 15)
hold off

%%
D1 = bwdistgeodesic(skel, c1, r1, 'quasi-euclidean');
D2 = bwdistgeodesic(skel, c2, r2, 'quasi-euclidean');

D = D1 + D2;
D = round(D * 8) / 8;

D(isnan(D)) = inf;
skeleton_path = imregionalmin(D);
P = imoverlay(skel, imdilate(skeleton_path, ones(3,3)), [1 0 0]);
imshow(P, 'InitialMagnification', 200)
hold on
plot(c1, r1, 'g*', 'MarkerSize', 15)
plot(c2, r2, 'g*', 'MarkerSize', 15)
hold off

%%
% Finally, the skeleton path length is given by the value of |D| at any
% point along the path.
path_length = D(skeleton_path);
path_length = path_length(1)

##### SOURCE END ##### 6c3df5ae559f4e679ee76128ce614074
--><img src="http://feeds.feedburner.com/~r/SteveOnImageProcessing/~4/0DMRYHoF7Go" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blogs.mathworks.com/steve/2011/12/13/exploring-shortest-paths-part-5/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://blogs.mathworks.com/steve/2011/12/13/exploring-shortest-paths-part-5/</feedburner:origLink></item>
		<item>
		<title>Exploring shortest paths – part 4</title>
		<link>http://feedproxy.google.com/~r/SteveOnImageProcessing/~3/8SUNTfUMGGY/</link>
		<comments>http://blogs.mathworks.com/steve/2011/12/06/exploring-shortest-paths-part-4/#comments</comments>
		<pubDate>Tue, 06 Dec 2011 07:00:11 +0000</pubDate>
		<dc:creator>Steve Eddins</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blogs.mathworks.com/steve/?p=398</guid>
		<description><![CDATA[In my previous posts on Exploring shortest paths (November 1, November 26, and December 3), I have noted several times that there isn't a unique shortest path (in general) between one object and another in a binary image. To illustrate, here's a recap of the 'quasi-euclidean' example from last time. bw = logical([ ... 0 [...]]]></description>
			<content:encoded><![CDATA[<div xmlns:mwsh="http://www.mathworks.com/namespace/mcode/v1/syntaxhighlight.dtd" class="content">
   <p>In my previous posts on <i>Exploring shortest paths</i> (<a href="http://blogs.mathworks.com/steve/2011/11/01/exploring-shortest-paths-part-1/">November 1</a>, <a href="http://blogs.mathworks.com/steve/2011/11/26/exploring-shortest-paths-part-2/">November 26</a>, and <a href="http://blogs.mathworks.com/steve/2011/12/03/exploring-shortest-paths-part-3/">December 3</a>), I have noted several times that there isn't a unique shortest path (in general) between one object and another in a binary
      image. To illustrate, here's a recap of the 'quasi-euclidean' example from last time.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">bw = logical([ <span style="color: #0000FF">...</span>
    0 0 0 0 0 0 0
    0 1 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 1 0
    0 0 0 0 0 0 0  ]);
L = bwlabel(bw);
bw1 = (L == 1);
bw2 = (L == 2);

D1 = bwdist(bw1, <span style="color: #A020F0">'quasi-euclidean'</span>);
D2 = bwdist(bw2, <span style="color: #A020F0">'quasi-euclidean'</span>);
D = D1 + D2;
D = round(D * 32) / 32;

paths = imregionalmin(D);
P = false(size(bw));
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, <span style="color: #A020F0">'InitialMagnification'</span>, <span style="color: #A020F0">'fit'</span>)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_d_01.png"> <p>The gray pixels shown above <b>all</b> belong to one or more of the shortest paths between the two objects. So how do we pick one path?
   </p>
   <p>The first idea I had was to thin the paths "blob" using the 'thin' option of <tt>bwmorph</tt>.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">paths_thinned_once = bwmorph(paths, <span style="color: #A020F0">'thin'</span>);
P = false(size(bw));
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, paths_thinned_once, [1 1 0]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, <span style="color: #A020F0">'InitialMagnification'</span>, <span style="color: #A020F0">'fit'</span>)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_d_02.png"> <p>That seems promising. What if we keep thinning until convergence? You do that by passing <tt>inf</tt> as the repetition value to <tt>bwmorph</tt>.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">paths_thinned_many = bwmorph(paths, <span style="color: #A020F0">'thin'</span>, inf);
P = false(size(bw));
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, paths_thinned_many, [1 1 0]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, <span style="color: #A020F0">'InitialMagnification'</span>, <span style="color: #A020F0">'fit'</span>)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_d_03.png"> <p>That looks great.</p>
   <p>Let's try the whole sequence with a longer path.</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">url = <span style="color: #A020F0">'http://blogs.mathworks.com/images/steve/2011/two-letters-cropped.png'</span>;
bw = imread(url);
imshow(bw, <span style="color: #A020F0">'InitialMagnification'</span>, <span style="color: #A020F0">'fit'</span>)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_d_04.png"> <pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">L = bwlabel(bw);
bw1 = (L == 1);
bw2 = (L == 2);

D1 = bwdist(bw1, <span style="color: #A020F0">'quasi-euclidean'</span>);
D2 = bwdist(bw2, <span style="color: #A020F0">'quasi-euclidean'</span>);
D = D1 + D2;
D = round(D * 32) / 32;

paths = imregionalmin(D);

paths_thinned_many = bwmorph(paths, <span style="color: #A020F0">'thin'</span>, inf);
P = false(size(bw));
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, paths_thinned_many, [1 1 0]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, <span style="color: #A020F0">'InitialMagnification'</span>, <span style="color: #A020F0">'fit'</span>)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_d_05.png"> <p>As before, the pixels belonging to any shortest path are shown in gray. The pixels in yellow belong to one particular path.</p>
   <p>Next time I'll explain how to find shortest paths subject to constraints.</p>
<h4>All the posts in this series</h4>
      <ul>
         <li>the basic idea of finding shortest paths by adding two distance transforms together (<a href="http://blogs.mathworks.com/steve/2011/11/01/exploring-shortest-paths-part-1/">part 1</a>)
         </li>
         <li>the nonuniqueness of the shortest paths (<a href="http://blogs.mathworks.com/steve/2011/11/26/exploring-shortest-paths-part-2/">part 2</a>)
         </li>
         <li>handling floating-point round-off effects (<a href="http://blogs.mathworks.com/steve/2011/12/02/exploring-shortest-paths-part-3/">part 3</a>)
         </li>
         <li>using thinning to pick out a single path (<a href="http://blogs.mathworks.com/steve/2011/12/06/exploring-shortest-paths-part-4/">part 4</a>)
         </li>
         <li>using <tt>bwdistgeodesic</tt> to find shortest paths subject to constraint (<a href="http://blogs.mathworks.com/steve/2011/12/13/exploring-shortest-paths-part-5/">part 5</a>)
         </li>
      </ul>
<script language="JavaScript">
<!--

    function grabCode_0529a645ec9649a7a6ac7ed899be8d75() {
        // Remember the title so we can use it in the new page
        title = document.title;

        // Break up these strings so that their presence
        // in the Javascript doesn't mess up the search for
        // the MATLAB code.
        t1='0529a645ec9649a7a6ac7ed899be8d75 ' + '##### ' + 'SOURCE BEGIN' + ' #####';
        t2='##### ' + 'SOURCE END' + ' #####' + ' 0529a645ec9649a7a6ac7ed899be8d75';
    
        b=document.getElementsByTagName('body')[0];
        i1=b.innerHTML.indexOf(t1)+t1.length;
        i2=b.innerHTML.indexOf(t2);
 
        code_string = b.innerHTML.substring(i1, i2);
        code_string = code_string.replace(/REPLACE_WITH_DASH_DASH/g,'--');

        // Use /x3C/g instead of the less-than character to avoid errors 
        // in the XML parser.
        // Use '\x26#60;' instead of '<' so that the XML parser
        // doesn't go ahead and substitute the less-than character. 
        code_string = code_string.replace(/\x3C/g, '\x26#60;');

        author = 'Steve Eddins';
        copyright = 'Copyright 2011 The MathWorks, Inc.';

        w = window.open();
        d = w.document;
        d.write('<pre>\n');
        d.write(code_string);

        // Add author and copyright lines at the bottom if specified.
        if ((author.length > 0) || (copyright.length > 0)) {
            d.writeln('');
            d.writeln('%%');
            if (author.length > 0) {
                d.writeln('% _' + author + '_');
            }
            if (copyright.length > 0) {
                d.writeln('% _' + copyright + '_');
            }
        }

        d.write('</pre>\n');
      
      d.title = title + ' (MATLAB code)';
      d.close();
      }   
      
-->
</script><p style="text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray"><br /><a href="javascript:grabCode_0529a645ec9649a7a6ac7ed899be8d75()"><span style="font-size: x-small;        font-style: italic;">Get 
            the MATLAB code 
            <noscript>(requires JavaScript)</noscript></span></a><br /><br />
      Published with MATLAB&reg; 7.13<br /></p>
</div>
<!--
0529a645ec9649a7a6ac7ed899be8d75 ##### SOURCE BEGIN #####
%%
% In my previous posts on _Exploring shortest paths_
% (<http://blogs.mathworks.com/steve/2011/11/01/exploring-shortest-paths-part-1/
% November 1>,
% <http://blogs.mathworks.com/steve/2011/11/26/exploring-shortest-paths-part-2/
% November 26>, and
% <http://blogs.mathworks.com/steve/2011/12/03/exploring-shortest-paths-part-3/
% December 3>), I have noted several times that there isn't a unique
% shortest path (in general) between one object and another in a binary
% image. To illustrate, here's a recap of the 'quasi-euclidean' example
% from last time.

bw = logical([ ...
    0 0 0 0 0 0 0
    0 1 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 1 0 
    0 0 0 0 0 0 0  ]);
L = bwlabel(bw);
bw1 = (L == 1);
bw2 = (L == 2);

D1 = bwdist(bw1, 'quasi-euclidean');
D2 = bwdist(bw2, 'quasi-euclidean');
D = D1 + D2;
D = round(D * 32) / 32;

paths = imregionalmin(D);
P = false(size(bw));
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, 'InitialMagnification', 'fit')

%%
% The gray pixels shown above *all* belong to one or more of the shortest
% paths between the two objects. So how do we pick one path?
%
% The first idea I had was to thin the paths "blob" using the 'thin' option
% of |bwmorph|.

paths_thinned_once = bwmorph(paths, 'thin');
P = false(size(bw));
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, paths_thinned_once, [1 1 0]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, 'InitialMagnification', 'fit')

%%
% That seems promising. What if we keep thinning until convergence? You do
% that by passing |inf| as the repetition value to |bwmorph|.

paths_thinned_many = bwmorph(paths, 'thin', inf);
P = false(size(bw));
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, paths_thinned_many, [1 1 0]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, 'InitialMagnification', 'fit')

%%
% That looks great. 
%
% Let's try the whole sequence with a longer path.
url = 'http://blogs.mathworks.com/images/steve/2011/two-letters-cropped.png';
bw = imread(url);
imshow(bw, 'InitialMagnification', 'fit')

%%
L = bwlabel(bw);
bw1 = (L == 1);
bw2 = (L == 2);

D1 = bwdist(bw1, 'quasi-euclidean');
D2 = bwdist(bw2, 'quasi-euclidean');
D = D1 + D2;
D = round(D * 32) / 32;

paths = imregionalmin(D);

paths_thinned_many = bwmorph(paths, 'thin', inf);
P = false(size(bw));
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, paths_thinned_many, [1 1 0]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, 'InitialMagnification', 'fit')

%%
% As before, the pixels belonging to any shortest path are shown in gray.
% The pixels in yellow belong to one particular path.
%
% Next time I'll explain how to find shortest paths subject to constraints.

##### SOURCE END ##### 0529a645ec9649a7a6ac7ed899be8d75
--><img src="http://feeds.feedburner.com/~r/SteveOnImageProcessing/~4/8SUNTfUMGGY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blogs.mathworks.com/steve/2011/12/06/exploring-shortest-paths-part-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blogs.mathworks.com/steve/2011/12/06/exploring-shortest-paths-part-4/</feedburner:origLink></item>
		<item>
		<title>Exploring shortest paths – part 3</title>
		<link>http://feedproxy.google.com/~r/SteveOnImageProcessing/~3/4DmSYgrsRwA/</link>
		<comments>http://blogs.mathworks.com/steve/2011/12/02/exploring-shortest-paths-part-3/#comments</comments>
		<pubDate>Fri, 02 Dec 2011 18:00:55 +0000</pubDate>
		<dc:creator>Steve Eddins</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blogs.mathworks.com/steve/?p=397</guid>
		<description><![CDATA[In part 2 of Exploring shortest paths, I noted a problem with using the 'quasi-euclidean' distance transform to find the shortest paths between two objects in a binary image. Specifically, our algorithm resulted in a big, unfilled gap between the two objects. bw = logical([ ... 0 0 0 0 0 0 0 0 1 [...]]]></description>
			<content:encoded><![CDATA[<div xmlns:mwsh="http://www.mathworks.com/namespace/mcode/v1/syntaxhighlight.dtd" class="content">
   <p>In <a href="http://blogs.mathworks.com/steve/2011/11/26/exploring-shortest-paths-part-2/">part 2 of <i>Exploring shortest paths</i></a>, I noted a problem with using the 'quasi-euclidean' distance transform to find the shortest paths between two objects in
      a binary image. Specifically, our algorithm resulted in a big, unfilled gap between the two objects.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">bw = logical([ <span style="color: #0000FF">...</span>
    0 0 0 0 0 0 0
    0 1 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 1 0
    0 0 0 0 0 0 0  ]);
L = bwlabel(bw);
bw1 = (L == 1);
bw2 = (L == 2);

D1 = bwdist(bw1, <span style="color: #A020F0">'quasi-euclidean'</span>);
D2 = bwdist(bw2, <span style="color: #A020F0">'quasi-euclidean'</span>);
D = D1 + D2;

paths = imregionalmin(D);
P = false(size(bw));
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, <span style="color: #A020F0">'InitialMagnification'</span>, <span style="color: #A020F0">'fit'</span>)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_c_01.png"> <p>The pixels marked in gray should be the set of pixels that lie along a shortest path from the two objects in the original
      image. You can plainly, see, however, that there's a large gap.
   </p>
   <p>To see why, let's examine more closely the values of <tt>D</tt>.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">D</pre><pre style="font-style:oblique">
D =

   12.4853   11.6569   11.6569   12.2426   12.8284   13.4142   14.8284
   11.0711    9.6569   10.2426   10.8284   11.4142   12.0000   13.4142
   10.4853    9.6569    9.6569   10.2426   10.8284   11.4142   12.8284
   10.4853    9.6569    9.6569    9.6569   10.2426   10.8284   12.2426
   10.4853    9.6569    9.6569    9.6569    9.6569   10.2426   11.6569
   11.0711    9.6569    9.6569    9.6569    9.6569    9.6569   11.0711
   11.6569   10.2426    9.6569    9.6569    9.6569    9.6569   10.4853
   12.2426   10.8284   10.2426    9.6569    9.6569    9.6569   10.4853
   12.8284   11.4142   10.8284   10.2426    9.6569    9.6569   10.4853
   13.4142   12.0000   11.4142   10.8284   10.2426    9.6569   11.0711
   14.8284   13.4142   12.8284   12.2426   11.6569   11.6569   12.4853

</pre><p>There appears to be an unbroken set of pixels between the two objects with value 9.6569. Actually, it turns out that the pixels
      do not all have the same value. For example, <tt>D(3,3)</tt> appear to have the same value <tt>D(4,4)</tt> but are actually slightly different.
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">D(3,3)</pre><pre style="font-style:oblique">
ans =

    9.6569

</pre><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">D(4,4)</pre><pre style="font-style:oblique">
ans =

    9.6569

</pre><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">D(3,3) - D(4,4)</pre><pre style="font-style:oblique">
ans =

 -9.5367e-007

</pre><p>The difference between the two is the corresponding single-precision relative floating-point precision:</p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">eps(D(3,3))</pre><pre style="font-style:oblique">
ans =

  9.5367e-007

</pre><p>This floating-point round-off error comes into play because of the way that multiples of sqrt(2) are being added in different
      orders.
   </p>
   <p>So we need to adjust our algorithm to account for floating-point round-off error. One way to do that is to round the distance
      transform values to some lower precision. For example, the code below rounds the distance transform to be a multiple of (1/32).
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">D = round(D*32)/32;</pre><p>Now <tt>D(3,3)</tt> and <tt>D(4,4)</tt> are equal:
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">D(3,3) - D(4,4)</pre><pre style="font-style:oblique">
ans =

     0

</pre><p>And <tt>imregionalmin</tt> works as expected to extract the set of pixels belonging to shortest paths. (I'm using <a href="http://www.mathworks.com/matlabcentral/fileexchange/10502-image-overlay">imoverlay</a> from the MATLAB Central File Exchange.)
   </p><pre style="background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)">paths = imregionalmin(D);
P = false(size(bw));
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, <span style="color: #A020F0">'InitialMagnification'</span>, <span style="color: #A020F0">'fit'</span>)</pre><img vspace="5" hspace="5" src="http://blogs.mathworks.com/images/steve/2011/shortest_path_c_02.png"> <p>Here's our revised algorithm, including the new rounding step:</p>
   <div>
      <ol>
         <li>Compute the distance transform for just the upper left block of foreground pixels.</li>
         <li>Compute the distance transform for just the lower right block of foreground pixels.</li>
         <li>Add the two distance transforms together.</li>
         <li>Round to lower precision.</li>
         <li>The pixels in the regional minimum of the result lie along one or more of the shortest paths from one block to the other.</li>
      </ol>
   </div>
   <p>Next time I'll look into how to pick a particular path among the many shortest-path choices.</p>
<h4>All the posts in this series</h4>
      <ul>
         <li>the basic idea of finding shortest paths by adding two distance transforms together (<a href="http://blogs.mathworks.com/steve/2011/11/01/exploring-shortest-paths-part-1/">part 1</a>)
         </li>
         <li>the nonuniqueness of the shortest paths (<a href="http://blogs.mathworks.com/steve/2011/11/26/exploring-shortest-paths-part-2/">part 2</a>)
         </li>
         <li>handling floating-point round-off effects (<a href="http://blogs.mathworks.com/steve/2011/12/02/exploring-shortest-paths-part-3/">part 3</a>)
         </li>
         <li>using thinning to pick out a single path (<a href="http://blogs.mathworks.com/steve/2011/12/06/exploring-shortest-paths-part-4/">part 4</a>)
         </li>
         <li>using <tt>bwdistgeodesic</tt> to find shortest paths subject to constraint (<a href="http://blogs.mathworks.com/steve/2011/12/13/exploring-shortest-paths-part-5/">part 5</a>)
         </li>
      </ul>
<script language="JavaScript">
<!--

    function grabCode_fb9cda11c3d94bb4b0f7316b9abcbf01() {
        // Remember the title so we can use it in the new page
        title = document.title;

        // Break up these strings so that their presence
        // in the Javascript doesn't mess up the search for
        // the MATLAB code.
        t1='fb9cda11c3d94bb4b0f7316b9abcbf01 ' + '##### ' + 'SOURCE BEGIN' + ' #####';
        t2='##### ' + 'SOURCE END' + ' #####' + ' fb9cda11c3d94bb4b0f7316b9abcbf01';
    
        b=document.getElementsByTagName('body')[0];
        i1=b.innerHTML.indexOf(t1)+t1.length;
        i2=b.innerHTML.indexOf(t2);
 
        code_string = b.innerHTML.substring(i1, i2);
        code_string = code_string.replace(/REPLACE_WITH_DASH_DASH/g,'--');

        // Use /x3C/g instead of the less-than character to avoid errors 
        // in the XML parser.
        // Use '\x26#60;' instead of '<' so that the XML parser
        // doesn't go ahead and substitute the less-than character. 
        code_string = code_string.replace(/\x3C/g, '\x26#60;');

        author = 'Steve Eddins';
        copyright = 'Copyright 2011 The MathWorks, Inc.';

        w = window.open();
        d = w.document;
        d.write('<pre>\n');
        d.write(code_string);

        // Add author and copyright lines at the bottom if specified.
        if ((author.length > 0) || (copyright.length > 0)) {
            d.writeln('');
            d.writeln('%%');
            if (author.length > 0) {
                d.writeln('% _' + author + '_');
            }
            if (copyright.length > 0) {
                d.writeln('% _' + copyright + '_');
            }
        }

        d.write('</pre>\n');
      
      d.title = title + ' (MATLAB code)';
      d.close();
      }   
      
-->
</script><p style="text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray"><br /><a href="javascript:grabCode_fb9cda11c3d94bb4b0f7316b9abcbf01()"><span style="font-size: x-small;        font-style: italic;">Get 
            the MATLAB code 
            <noscript>(requires JavaScript)</noscript></span></a><br /><br />
      Published with MATLAB&reg; 7.13<br /></p>
</div>
<!--
fb9cda11c3d94bb4b0f7316b9abcbf01 ##### SOURCE BEGIN #####
%%
% In <http://blogs.mathworks.com/steve/2011/11/26/exploring-shortest-paths-part-2/ 
% part 2 of _Exploring shortest paths_>, I noted a problem with using the
% 'quasi-euclidean' distance transform to find the shortest paths between
% two objects in a binary image. Specifically, our algorithm resulted in a
% big, unfilled gap between the two objects.

bw = logical([ ...
    0 0 0 0 0 0 0
    0 1 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 0 0
    0 0 0 0 0 1 0 
    0 0 0 0 0 0 0  ]);
L = bwlabel(bw);
bw1 = (L == 1);
bw2 = (L == 2);

D1 = bwdist(bw1, 'quasi-euclidean');
D2 = bwdist(bw2, 'quasi-euclidean');
D = D1 + D2;

paths = imregionalmin(D);
P = false(size(bw));
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, 'InitialMagnification', 'fit')

%%
% The pixels marked in gray should be the set of pixels that lie along a
% shortest path from the two objects in the original image. You can
% plainly, see, however, that there's a large gap.
%
% To see why, let's examine more closely the values of |D|.

D

%%
% There appears to be an unbroken set of pixels between the two objects
% with value 9.6569. Actually, it turns out that the pixels do not all have
% the same value. For example, |D(3,3)| appear to have the same value
% |D(4,4)| but are actually slightly different.

D(3,3)

%%

D(4,4)

%%

D(3,3) - D(4,4)

%%
% The difference between the two is the corresponding single-precision
% relative floating-point precision:

eps(D(3,3))

%%
% This floating-point round-off error comes into play because of the way
% that multiples of sqrt(2) are being added in different orders.
%
% So we need to adjust our algorithm to account for floating-point
% round-off error. One way to do that is to round the distance transform
% values to some lower precision. For example, the code below rounds the
% distance transform to be a multiple of (1/32).

D = round(D*32)/32;

%%
% Now |D(3,3)| and |D(4,4)| are equal:

D(3,3) - D(4,4)

%%
% And |imregionalmin| works as expected to extract the set of pixels
% belonging to shortest paths. (I'm using
% <http://www.mathworks.com/matlabcentral/fileexchange/10502-image-overlay
% imoverlay> from the MATLAB Central File Exchange.)

paths = imregionalmin(D);
P = false(size(bw));
P = imoverlay(P, paths, [.5 .5 .5]);
P = imoverlay(P, bw, [1 1 1]);
imshow(P, 'InitialMagnification', 'fit')

%%
% Here's our revised algorithm, including the new rounding step:
%
% # Compute the distance transform for just the upper left block of
% foreground pixels.
% # Compute the distance transform for just the lower right block of
% foreground pixels.
% # Add the two distance transforms together.
% # Round to lower precision.
% # The pixels in the regional minimum of the result lie along one or more
% of the shortest paths from one block to the other.
%
% Next time I'll look into how to pick a particular path among the many
% shortest-path choices.
##### SOURCE END ##### fb9cda11c3d94bb4b0f7316b9abcbf01
--><img src="http://feeds.feedburner.com/~r/SteveOnImageProcessing/~4/4DmSYgrsRwA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blogs.mathworks.com/steve/2011/12/02/exploring-shortest-paths-part-3/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://blogs.mathworks.com/steve/2011/12/02/exploring-shortest-paths-part-3/</feedburner:origLink></item>
	</channel>
</rss>

