{"id":140,"date":"2007-06-08T14:09:55","date_gmt":"2007-06-08T18:09:55","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/2007\/06\/08\/upslope-area-part-5\/"},"modified":"2019-10-23T12:57:44","modified_gmt":"2019-10-23T16:57:44","slug":"upslope-area-part-5","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2007\/06\/08\/upslope-area-part-5\/","title":{"rendered":"Upslope area &#8211; Plateau processing"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n<p><em>[NOTE: After I originally wrote this post about handling plateaus, I discovered problems with the method, so I am no longer using it.  I revisited the plateau issue in a <a href=\"https:\/\/blogs.mathworks.com\/steve\/2007\/08\/23\/upslope-area-handling-plateaus-revisited\/\">later post<\/a>. -SE]<\/em>\r\n<\/p>\r\n\r\n   <p>Today I'll continue thinking about plateaus in a DEM and what they mean for calculating pixel flow. Here's a very tiny portion\r\n      of my DEM array, just northwest of <a href=\"http:\/\/maps.google.com\/maps?ie=UTF8&amp;hl=en&amp;ll=42.190309,-71.550608&amp;spn=0.044197,0.066605&amp;t=h&amp;z=14&amp;om=1\">North Pond<\/a>.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">a = [120 118 123; 120 118 125; 119 119 126]<\/pre><pre style=\"font-style:oblique\">\r\na =\r\n\r\n   120   118   123\r\n   120   118   125\r\n   119   119   126\r\n\r\n<\/pre><p>The center pixel doesn't have a downhill neighbor, so I'm calling it a <i>plateau pixel<\/i>.  No matter that it's a very small plateau; we can't compute a pixel flow direction for it, and that will cause problems\r\n      when we get around to computing upslope area.\r\n   <\/p>\r\n   <p>One technique used in image processing to deal with plateaus is called the <i>lower complete transformation<\/i>.  A <i>lower complete image<\/i> is one where the only pixels having no downhill neighors are the pixels belonging to regional minima.  Roughly speaking,\r\n      the lower complete transformation operates by elevating plateau pixels.  Pixels further from the plateau edge get elevated\r\n      more.  Of course, pixels uphill from the plateau need to be elevated as well so that they remain uphill.  I have read about\r\n      this technique but have never implemented it myself.  (For more information, I recommend P. Soille, <i>Morphological Image Analysis: Principles and Applications<\/i>, 2nd edition, Springer, 2003, pp. 222-226.)\r\n   <\/p>\r\n   <p>I'm going to use a different technique, one that's based on the algorithm in the Image Processing Toolbox function <tt>roifill<\/tt>.  This function fills in pixels in a particular region by smoothly interpolating from the pixels surrounding the region.\r\n       It does so by solving a sparse linear system based on the notion that, in the filled output, each pixel in the filled region\r\n      equals the average of its north, east, south, and west neighbors.  (It's kind of an interesting method.  Maybe that'll be\r\n      a future blog topic.)\r\n   <\/p>\r\n   <p>Here's the example (slightly modified) from the <tt>roifill<\/tt> documentation:\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">I = imread(<span style=\"color: #A020F0\">'eight.tif'<\/span>);\r\nimshow(I)\r\n\r\nc = [222 272 300 270 221 194 222];\r\nr = [21 21 75 121 121 75 21];\r\nhold <span style=\"color: #A020F0\">on<\/span>\r\nplot(c,r,<span style=\"color: #A020F0\">'r'<\/span>,<span style=\"color: #A020F0\">'LineWidth'<\/span>,4)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/140\/upslope_area_5_01.jpg\"> <pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">J = roifill(I,c,r);\r\nimshow(J)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/140\/upslope_area_5_02.jpg\"> <p>And here's what <tt>roifill<\/tt> does to our tiny snippet from the DEM:\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">mask = true(3, 3);\r\nb = roifill(a, mask)<\/pre><pre style=\"font-style:oblique\">\r\nb =\r\n\r\n  120.0000  118.0000  123.0000\r\n  120.0000  120.5000  125.0000\r\n  119.0000  119.0000  126.0000\r\n\r\n<\/pre><p>You can see that the center pixel has been replaced by a new value, and that the center pixel now has a downhill neighbor.<\/p>\r\n   <p>Here's a slightly bigger example, where the two center pixels have no downhill neighbors:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">a2 = pond(15:17, 19:22)<\/pre><pre style=\"font-style:oblique\">\r\na2 =\r\n\r\n   116   116   116   120\r\n   117   115   115   117\r\n   119   115   115   116\r\n\r\n<\/pre><p>And the output of <tt>roifill<\/tt><\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">mask2 = true(3, 4);\r\nb2 = roifill(a2, mask2)<\/pre><pre style=\"font-style:oblique\">\r\nb2 =\r\n\r\n   116   116   116   120\r\n   117   116   116   117\r\n   119   115   115   116\r\n\r\n<\/pre><p>Well, the two center pixels now have downhill neighbors, but WAIT!  The (1,2) pixel no longer has a downhill neighbor!  OK,\r\n      time to 'fess up.  I didn't really anticipate that this would be a problem.  I'll have to give this some thought.  My first\r\n      reaction is: It'll be OK because we can use the original, unmodified DEM values to compute the pixel flow for the nonplateau\r\n      pixels.  We only need the modified DEM values to compute pixel flow for the plateau pixels.  But I'll sleep on it.\r\n   <\/p>\r\n   <p>Assuming I can resolve that problem successfully, we can use <tt>roifill<\/tt> to compute pixel flow directions for the midplateau pixels.  It won't help us, though, for the plateau pixels that are part\r\n      of regional maxima or regional minima.  I'll figure out what to do about those next week.\r\n   <\/p><script language=\"JavaScript\">\r\n<!--\r\n\r\n    function grabCode_1978b5fcf5c5403f8ba971be90d05721() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='1978b5fcf5c5403f8ba971be90d05721 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 1978b5fcf5c5403f8ba971be90d05721';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        author = 'Steve Eddins';\r\n        copyright = 'Copyright 2007 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add author and copyright lines at the bottom if specified.\r\n        if ((author.length > 0) || (copyright.length > 0)) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (author.length > 0) {\r\n                d.writeln('% _' + author + '_');\r\n            }\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n      \r\n      d.title = title + ' (MATLAB code)';\r\n      d.close();\r\n      }   \r\n      \r\n-->\r\n<\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_1978b5fcf5c5403f8ba971be90d05721()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n            the MATLAB code \r\n            <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; 7.4<br><\/p>\r\n<\/div>\r\n<!--\r\n1978b5fcf5c5403f8ba971be90d05721 ##### SOURCE BEGIN #####\r\n%%\r\n% Today I'll continue thinking about plateaus in a DEM and what they mean for \r\n% calculating pixel flow. Here's a very tiny portion of my DEM array, just\r\n% northwest of <http:\/\/maps.google.com\/maps?ie=UTF8&hl=en&ll=42.190309,-71.550608&spn=0.044197,0.066605&t=h&z=14&om=1 \r\n% North Pond>.\r\n\r\na = [120 118 123; 120 118 125; 119 119 126]\r\n\r\n%%\r\n% The center pixel doesn't have a downhill neighbor, so I'm calling it a\r\n% _plateau pixel_.  No matter that it's a very small plateau; we can't\r\n% compute a pixel flow direction for it, and that will cause problems when\r\n% we get around to computing upslope area.\r\n%\r\n% One technique used in image processing to deal with plateaus is called\r\n% the _lower complete transformation_.  A _lower complete image_ is one\r\n% where the only pixels having no downhill neighors are the pixels\r\n% belonging to regional minima.  Roughly speaking, the lower complete \r\n% transformation operates by elevating plateau pixels.  Pixels further from\r\n% the plateau edge get elevated more.  Of course, pixels uphill from the\r\n% plateau need to be elevated as well so that they remain uphill.  I have\r\n% read about this technique but have never implemented it myself.  (For\r\n% more information, I recommend P. Soille, _Morphological Image Analysis:\r\n% Principles and Applications_, 2nd edition, Springer, 2003, pp. 222-226.)\r\n%\r\n% I'm going to use a different technique, one that's based on the algorithm\r\n% in the Image Processing Toolbox function |roifill|.  This function fills\r\n% in pixels in a particular region by smoothly interpolating from the\r\n% pixels surrounding the region.  It does so by solving a sparse linear \r\n% system based on the notion that, in the filled output, each pixel in the \r\n% filled region equals the average of its north, east, south, and west\r\n% neighbors.  (It's kind of an interesting method.  Maybe that'll be a\r\n% future blog topic.)\r\n%\r\n% Here's the example (slightly modified) from the |roifill| documentation:\r\n\r\nI = imread('eight.tif');\r\nimshow(I)\r\n\r\nc = [222 272 300 270 221 194 222];\r\nr = [21 21 75 121 121 75 21];\r\nhold on\r\nplot(c,r,'r','LineWidth',4)\r\n\r\n%%\r\nJ = roifill(I,c,r);\r\nimshow(J)\r\n\r\n%%\r\n% And here's what |roifill| does to our tiny snippet from the DEM:\r\n\r\nmask = true(3, 3);\r\nb = roifill(a, mask)\r\n\r\n%%\r\n% You can see that the center pixel has been replaced by a new value, and\r\n% that the center pixel now has a downhill neighbor.\r\n%\r\n% Here's a slightly bigger example, where the two center pixels have no\r\n% downhill neighbors:\r\n\r\na2 = pond(15:17, 19:22)\r\n\r\n%%\r\n% And the output of |roifill|\r\n\r\nmask2 = true(3, 4);\r\nb2 = roifill(a2, mask2)\r\n\r\n%%\r\n% Well, the two center pixels now have downhill neighbors, but WAIT!  The\r\n% (1,2) pixel no longer has a downhill neighbor!  OK, time to 'fess up.  I\r\n% didn't really anticipate that this would be a problem.  I'll have to give\r\n% this some thought.  My first reaction is: It'll be OK because we\r\n% can use the original, unmodified DEM values to compute the pixel flow for\r\n% the nonplateau pixels.  We only need the modified DEM values to compute\r\n% pixel flow for the plateau pixels.  But I'll sleep on it.\r\n%\r\n% Assuming I can resolve that problem successfully, we can use |roifill| to\r\n% compute pixel flow directions for the midplateau pixels.  It won't help\r\n% us, though, for the plateau pixels that are part of regional maxima or\r\n% regional minima.  I'll figure out what to do about those next week.\r\n\r\n##### SOURCE END ##### 1978b5fcf5c5403f8ba971be90d05721\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n[NOTE: After I originally wrote this post about handling plateaus, I discovered problems with the method, so I am no longer using it.  I revisited the plateau issue in a later post. -SE]\r\n\r\n\r\n  ... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2007\/06\/08\/upslope-area-part-5\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[9],"tags":[90,76,36,68,368,100],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/140"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/users\/42"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/comments?post=140"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/140\/revisions"}],"predecessor-version":[{"id":3536,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/140\/revisions\/3536"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=140"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=140"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=140"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}