{"id":157,"date":"2007-08-07T11:46:59","date_gmt":"2007-08-07T15:46:59","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/2007\/08\/07\/upslope-area-flow-matrix\/"},"modified":"2019-10-23T13:45:28","modified_gmt":"2019-10-23T17:45:28","slug":"upslope-area-flow-matrix","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2007\/08\/07\/upslope-area-flow-matrix\/","title":{"rendered":"Upslope area &#8211; Forming and solving the flow matrix"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <p>About twelve years ago, I was implementing the algorithm that became <tt>roifill<\/tt> in the Image Processing Toolbox.  In this algorithm, a specified set of pixels is replaced (or filled in) such that each\r\n      of the replaced pixels equals the average of its north, east, south, and west neighbors.  I had implemented an iterative algorithm,\r\n      which worked but was slow.  When I asked MATLAB creator and company cofounder <a href=\"https:\/\/www.mathworks.com\/company\/aboutus\/founders\/clevemoler.html\">Cleve<\/a> if he knew a better way, he immediately replied, \"It looks like a sparse linear system to me.  Why don't you just use backslash?\"\r\n   <\/p>\r\n   <p>Head slap!  Of course.<\/p>\r\n   <p>Now here I am again, looking at a very similar set of equations and thinking again about using sparse backslash.<\/p>\r\n   <p>In the Tarboton paper, the upslope area calculation is formulated recursively.  That is, the upslope area of a pixel equals\r\n      1 plus some fraction of the upslope area of its neighboring pixels.  (The specific fraction is a function of the flow direction\r\n      for the neighbors.)  So that's a linear system of equations.  Let's see how to set up and solve the system in MATLAB.\r\n   <\/p>\r\n   <p>We'll start by making a test \"DEM\" to work with.  I like using <tt>peaks<\/tt>.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">E = peaks;\r\nimshow(E, [], <span style=\"color: #A020F0\">'InitialMagnification'<\/span>, <span style=\"color: #A020F0\">'fit'<\/span>)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/157\/upslope_area_8_01.png\"> <p>Next, compute the pixel flow directions, using the function <tt>dem_flow<\/tt> from my <a title=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadFile.do?objectId=15818&amp;objectType=FILE (link no longer works)\">upslope area contribution on the MATLAB Central File Exchange<\/a>.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">R = dem_flow(peaks);<\/pre><p>Each row and column of our sparse system will correspond to an individual pixel.  It will be convenient to make a matrix,\r\n      the same size as E, that \"numbers\" or labels the individual pixels of E.  These labels will be used as the sparse matrix row\r\n      and column coordinates.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">pixel_labels = 1:numel(E);\r\npixel_labels = reshape(pixel_labels, size(E));\r\npixel_labels(1:4, 1:6)<\/pre><pre style=\"font-style:oblique\">\r\nans =\r\n\r\n     1    50    99   148   197   246\r\n     2    51   100   149   198   247\r\n     3    52   101   150   199   248\r\n     4    53   102   151   200   249\r\n\r\n<\/pre><p>Let's consider north neighbors.  Here's the set of pixels with north neighbors:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">pixels_with_north_neighbors = pixel_labels(2:end, :);<\/pre><p>And here are the corresponding north neighbors:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">north_neighbors = pixel_labels(1:end-1, :);<\/pre><p>The angle pointing inward from a pixel's north neighbor to itself is -pi\/2:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">inward_angle = -pi\/2;<\/pre><p>A pixel's north neighbor has a flow that points in a particular direction.  That direction is contained in <tt>R<\/tt>.  If the flow direction is the same as the inward angle, then the fraction of flow from that neighbor is 1.0.  If the flow\r\n      direction is pointing more than pi\/4 radians away from the inward angle, then the fraction of flow from that neighbor is 0.0.\r\n       For inbetween angular differences, the fraction varies linearly from 1.0 to 0.0.\r\n   <\/p>\r\n   <p>I have written simple functions to compute angular differences and the corresponding weights.  The angular difference computation\r\n      looks like this:\r\n   <\/p><pre> d = mod(theta1 - theta2 + pi, 2*pi) - pi;<\/pre><p>and the directional weight computation looks like this:<\/p><pre> w = max(1 - abs(d)*4\/pi, 0);<\/pre><p>With these functions, we can compute the north neighbor weights for all the pixels at once:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">w = directional_weight(angular_difference(R(north_neighbors), <span style=\"color: #0000FF\">...<\/span>\r\n    inward_angle));<\/pre><p>Ignoring the other neighbors for the moment, the upslope area equation looks like this:<\/p><pre>  Ap = 1 + wAn<\/pre><p>where Ap is the upslope area of the pixel, An is the upslope area of the north neighbor, and w is the directional weight based\r\n      on the flow direction of the north neighbor.  Rewrite the equation this way to see how to construct the linear equation matrix:\r\n   <\/p><pre> Ap - wAn = 1<\/pre><p>So each row of our matrix is going to have a 1 on the diagonal, and it's going to have the negative of the directional flow\r\n      weights for each of its neighbors.  If we used only the northern weights computed above, we could construct the sparse matrix\r\n      this way:\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">i = pixels_with_north_neighbors(:);\r\nj = north_neighbors(:);\r\nw = w(:);\r\n\r\n<span style=\"color: #228B22\">% Add diagonal terms<\/span>\r\nii = [i ; pixel_labels(:)];\r\njj = [j ; pixel_labels(:)];\r\nww = [w ; ones(numel(pixel_labels), 1)];\r\n\r\n<span style=\"color: #228B22\">% And construct the linear system matrix with a call to sparse<\/span>\r\nT = sparse(ii, jj, ww);\r\nspy(T)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/157\/upslope_area_8_02.png\"> <p>We have to zoom in to clearly see the off-diagonal terms:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">xlim([0 20])\r\nylim([0 20])<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/157\/upslope_area_8_03.png\"> <p>I'm going to call <tt>T<\/tt> the <i>flow matrix<\/i>.  To finish computing <tt>T<\/tt>, we'd have to repeat some of the steps above for each of the seven other neighbor directions, which is way too tedious to\r\n      reproduce here.  I'll just use the <tt>flow_matrix<\/tt> function in my <a title=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadFile.do?objectId=15818&amp;objectType=FILE (link no longer works)\">MATLAB Central File Exchange submission<\/a>:\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">T = flow_matrix(E, R);<\/pre><p>Now we can compute the upslope area of each pixel by solving the matrix equation:<\/p><pre>  Ta = b<\/pre><p>where T is the sparse matrix we just formed, a is the vector of upslope areas for every pixel, and b is a vector of ones.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">a = T \\ ones(numel(E), 1);\r\nA = reshape(a, size(E));\r\n\r\nimshow(A, [], <span style=\"color: #A020F0\">'InitialMagnification'<\/span>, <span style=\"color: #A020F0\">'fit'<\/span>)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/157\/upslope_area_8_04.png\"> <p>I have found that using log(A) works well for visualizing an upslope area matrix:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">imshow(log(A), [], <span style=\"color: #A020F0\">'InitialMagnification'<\/span>, <span style=\"color: #A020F0\">'fit'<\/span>)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/157\/upslope_area_8_05.png\"> <p>The brighter values have the highest upslope areas.<\/p>\r\n   <p>In my next post in this series, I'll revisit the issue of handling plateaus.<\/p><script language=\"JavaScript\">\r\n<!--\r\n\r\n    function grabCode_2b94e7e752cb40629ca5f8d94c6abc3c() {\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='2b94e7e752cb40629ca5f8d94c6abc3c ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 2b94e7e752cb40629ca5f8d94c6abc3c';\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_2b94e7e752cb40629ca5f8d94c6abc3c()\"><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\n2b94e7e752cb40629ca5f8d94c6abc3c ##### SOURCE BEGIN #####\r\n%%\r\n% About twelve years ago, I was implementing the algorithm that became\r\n% |roifill| in the Image Processing Toolbox.  In this algorithm, a\r\n% specified set of pixels is replaced (or filled in) such that each\r\n% of the replaced pixels equals the average of its north, east, south, and\r\n% west neighbors.  I had implemented an iterative algorithm, which worked\r\n% but was slow.  When I asked MATLAB creator and company cofounder \r\n% <https:\/\/www.mathworks.com\/company\/aboutus\/founders\/clevemoler.html Cleve> \r\n% if he knew a better way, he immediately replied, \"It looks like a sparse\r\n% linear system to me.  Why don't you just use backslash?\"\r\n%\r\n% Head slap!  Of course.\r\n%\r\n% Now here I am again, looking at a very similar set of equations and\r\n% thinking again about using sparse backslash.\r\n%\r\n% In the Tarboton paper, the upslope area calculation is formulated\r\n% recursively.  That is, the upslope area of a pixel equals 1 plus some\r\n% fraction of the upslope area of its neighboring pixels.  (The specific\r\n% fraction is a function of the flow direction for the neighbors.)  So\r\n% that's a linear system of equations.  Let's see how to set up and solve\r\n% the system in MATLAB.\r\n%\r\n% We'll start by making a test \"DEM\" to work with.  I like using |peaks|.\r\n\r\nE = peaks;\r\nimshow(E, [], 'InitialMagnification', 'fit')\r\n\r\n%%\r\n% Next, compute the pixel flow directions, using the function |dem_flow|\r\n% from my \r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadFile.do?objectId=15818&objectType=FILE \r\n% upslope area contribution on the MATLAB Central File Exchange>.\r\n\r\nR = dem_flow(peaks);\r\n\r\n%%\r\n% Each row and column of our sparse system will correspond to an individual\r\n% pixel.  It will be convenient to make a matrix, the same size as E, that\r\n% \"numbers\" or labels the individual pixels of E.  These labels will be\r\n% used as the sparse matrix row and column coordinates.\r\n\r\npixel_labels = 1:numel(E);\r\npixel_labels = reshape(pixel_labels, size(E));\r\npixel_labels(1:4, 1:6)\r\n\r\n%%\r\n% Let's consider north neighbors.  Here's the set of pixels with north\r\n% neighbors:\r\n\r\npixels_with_north_neighbors = pixel_labels(2:end, :);\r\n\r\n%%\r\n% And here are the corresponding north neighbors:\r\n\r\nnorth_neighbors = pixel_labels(1:end-1, :);\r\n\r\n%%\r\n% The angle pointing inward from a pixel's north neighbor to itself is\r\n% -pi\/2:\r\n\r\ninward_angle = -pi\/2;\r\n\r\n%%\r\n% A pixel's north neighbor has a flow that points in a particular\r\n% direction.  That direction is contained in |R|.  If the flow direction is\r\n% the same as the inward angle, then the fraction of flow from that\r\n% neighbor is 1.0.  If the flow direction is pointing more than pi\/4\r\n% radians away from the inward angle, then the fraction of flow from that\r\n% neighbor is 0.0.  For inbetween angular differences, the fraction varies\r\n% linearly from 1.0 to 0.0.\r\n%\r\n% I have written simple functions to compute angular differences and the\r\n% corresponding weights.  The angular difference computation looks like\r\n% this:\r\n%\r\n%   d = mod(theta1 - theta2 + pi, 2*pi) - pi;\r\n%\r\n% and the directional weight computation looks like this:\r\n%\r\n%   w = max(1 - abs(d)*4\/pi, 0);\r\n%\r\n% With these functions, we can compute the north neighbor weights for all\r\n% the pixels at once:\r\n\r\nw = directional_weight(angular_difference(R(north_neighbors), ...\r\n    inward_angle));\r\n\r\n%%\r\n% Ignoring the other neighbors for the moment, the upslope area\r\n% equation looks like this:\r\n%\r\n%    Ap = 1 + wAn\r\n%\r\n% where Ap is the upslope area of the pixel, An is the upslope area of\r\n% the north neighbor, and w is the directional weight based on the flow\r\n% direction of the north neighbor.  Rewrite the equation this way to see\r\n% how to construct the linear equation matrix:\r\n%\r\n%   Ap - wAn = 1\r\n%\r\n% So each row of our matrix is going to have a 1 on the diagonal, and it's\r\n% going to have the negative of the directional flow weights for each of\r\n% its neighbors.  If we used only the northern weights computed above, we\r\n% could construct the sparse matrix this way:\r\n\r\ni = pixels_with_north_neighbors(:);\r\nj = north_neighbors(:);\r\nw = w(:);\r\n\r\n% Add diagonal terms\r\nii = [i ; pixel_labels(:)];\r\njj = [j ; pixel_labels(:)];\r\nww = [w ; ones(numel(pixel_labels), 1)];\r\n\r\n% And construct the linear system matrix with a call to sparse\r\nT = sparse(ii, jj, ww);\r\nspy(T)\r\n\r\n%%\r\n% We have to zoom in to clearly see the off-diagonal terms:\r\n\r\nxlim([0 20])\r\nylim([0 20])\r\n\r\n%%\r\n% I'm going to call |T| the _flow matrix_.  To finish computing |T|, we'd \r\n% have to repeat some of the steps above for each of the seven other\r\n% neighbor directions, which is way too tedious to reproduce here.  I'll\r\n% just use the |flow_matrix| function in my \r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/loadFile.do?objectId=15818&objectType=FILE \r\n% MATLAB Central File Exchange submission>:\r\n\r\nT = flow_matrix(E, R);\r\n\r\n%%\r\n% Now we can compute the upslope area of each pixel by solving the matrix\r\n% equation:\r\n%\r\n%    Ta = b\r\n%\r\n% where T is the sparse matrix we just formed, a is the vector of upslope\r\n% areas for every pixel, and b is a vector of ones.\r\n\r\na = T \\ ones(numel(E), 1);\r\nA = reshape(a, size(E));\r\n\r\nimshow(A, [], 'InitialMagnification', 'fit')\r\n\r\n%%\r\n% I have found that using log(A) works well for visualizing an upslope area\r\n% matrix:\r\n\r\nimshow(log(A), [], 'InitialMagnification', 'fit')\r\n\r\n%%\r\n% The brighter values have the highest upslope areas.\r\n%\r\n% In my next post in this series, I'll revisit the issue of handling\r\n% plateaus.\r\n##### SOURCE END ##### 2b94e7e752cb40629ca5f8d94c6abc3c\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   About twelve years ago, I was implementing the algorithm that became roifill in the Image Processing Toolbox.  In this algorithm, a specified set of pixels is replaced (or filled in) such that... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2007\/08\/07\/upslope-area-flow-matrix\/\">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":[36,224,122,388,162,288,354,170,338,340,360,298],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/157"}],"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=157"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/157\/revisions"}],"predecessor-version":[{"id":3552,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/157\/revisions\/3552"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=157"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=157"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=157"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}