{"id":1333,"date":"2015-06-17T11:44:25","date_gmt":"2015-06-17T15:44:25","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=1333"},"modified":"2019-11-01T11:42:05","modified_gmt":"2019-11-01T15:42:05","slug":"region-filling-and-laplaces-equation","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2015\/06\/17\/region-filling-and-laplaces-equation\/","title":{"rendered":"Region filling and Laplace&#8217;s equation"},"content":{"rendered":"<div class=\"content\"><p>Today I want to show you how to use a linear system of 90,300 equations and 90,300 unknowns to get rid of a leaf.<\/p><p><a href=\"https:\/\/blogs.mathworks.com\/steve\/2015\/06\/04\/image-region-filling-an-updated-design\/\">Earlier this month I told you<\/a> how the function <tt>roifill<\/tt> was recently renamed to <a href=\"https:\/\/www.mathworks.com\/help\/images\/ref\/regionfill.html\"><tt>regionfill<\/tt><\/a> to correct a functional design flaw. Now I want to follow up and talk about the algorithm underlying <tt>regionfill<\/tt>.<\/p><p>This was one of my earliest algorithm projects at MathWorks. And it was my earliest lesson the power of sparse linear algebra in MATLAB. About 21 years ago, Clay Thompson, one of the two developers of version 1.0 of Image Processing Toolbox, commented to me one day that maybe we could \"erase\" objects in an image by treating it as a \"soap-film problem.\" (The other developer of that first toolbox version was <a href=\"https:\/\/blogs.mathworks.com\/loren\">Loren Shure<\/a>.)<\/p><p>In other words, let's treat the pixel values surrounding the region we want to erase as heights of a closed loop of wire and then figure out the shape of a soap film bounded by that wire.<\/p><p>That sounded plausible, so I looked into it. (The World Wide Web was barely a year old at the time, and there was nothing like the modern search engine, so I sought out information using what we used to call \"books.\")<\/p><p>I found that, if you make some simplifying assumptions (such as no gravity), the soap film surface satisfies Laplace's equation:<\/p><p>$$\\frac{\\delta^2 f}{\\delta x^2} + \\frac{\\delta^2 f}{\\delta y^2} = 0$$<\/p><p>where the height of the wire loop around the region gives us the boundary conditions for the PDE.<\/p><p>In a simple, discretized version of Laplace's equation, the value of every grid element in the interior of the region equals the average of its north, east, south, and west neighbors in the grid.<\/p><p>Before I explore that idea further, though, let's look at some pictures to illustrate what we're trying to accomplish.<\/p><p>Here is the \"trees\" image (in gray) with a region drawn around what looks like a small leaf.<\/p><pre class=\"codeinput\">[X,map] = imread(<span class=\"string\">'trees.tif'<\/span>);\r\nI = im2double(ind2gray(X,map));\r\nimshow(I)\r\nx = [240 245 255 270 285 290 280 270 250 240];\r\ny = [155 165 170 173 172 166 161 155 153 155];\r\nhold <span class=\"string\">on<\/span>\r\nplot(x,y,<span class=\"string\">'y'<\/span>)\r\nhold <span class=\"string\">off<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/exploring_regionfill_01.png\" alt=\"\"> <p>Here's a zoomed-in view.<\/p><pre class=\"codeinput\">axis([230 300 140 180])\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/exploring_regionfill_02.png\" alt=\"\"> <p>We are going to \"erase\" that leaf by filling in the pixels inside that region. The function <tt>poly2mask<\/tt> is useful for finding the set of pixels inside a polygonal region.<\/p><pre class=\"codeinput\">mask = poly2mask(x,y,258,350);\r\nimshow(mask)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/exploring_regionfill_03.png\" alt=\"\"> <p>Let's think about the gray-scale pixel values as heights and view the image as a surface.<\/p><pre class=\"codeinput\">h = surf(I);\r\nh.EdgeColor = <span class=\"string\">'none'<\/span>;\r\nh.FaceColor = <span class=\"string\">'interp'<\/span>;\r\naxis <span class=\"string\">ij<\/span>\r\nview(-10,80)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/exploring_regionfill_04.png\" alt=\"\"> <p>Here's a close-up of the region we're interested in.<\/p><pre class=\"codeinput\">xlim([230 300])\r\nylim([140 180])\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/exploring_regionfill_05.png\" alt=\"\"> <p>We can use the region mask computed above to \"cut out\" the part of the surface inside the region.<\/p><pre class=\"codeinput\">h.FaceAlpha = <span class=\"string\">'interp'<\/span>;\r\nh.AlphaData = ~mask;\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/exploring_regionfill_06.png\" alt=\"\"> <p>A very simple approach to erasing the left would be to fill in the region with the mean value of pixels in the region. Here's how to do it. (I just love logical indexing with binary image masks!)<\/p><pre class=\"codeinput\">J = I;\r\nJ(mask) = mean(I(mask));\r\nh.ZData = J;\r\nh.AlphaData = true(size(J));\r\n\r\ntitle(<span class=\"string\">'Region filled with mean value'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/exploring_regionfill_07.png\" alt=\"\"> <pre class=\"codeinput\">imshow(J)\r\ntitle(<span class=\"string\">'Region filled with mean value'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/exploring_regionfill_08.png\" alt=\"\"> <pre class=\"codeinput\">axis([230 300 140 180])\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/exploring_regionfill_09.png\" alt=\"\"> <p>Now let's work toward a solution based on Laplace's equation. I'm going to set up a linear system of equations, $Ax = b$, so that the solution gives me every pixel of the output image. I'll think of the pixels as being numbered in columnwise order from 1 to the total number of pixels. The trees image has 90,300 pixels, so this is going to be a 90,300 by 90,300 linear system! It's a good thing it'll be very sparse.<\/p><p>For every pixel outside the masked region, the equation is simply the identity: output_pixel equals input_pixel.<\/p><p>For every pixel inside the masked region, the pixel value should equal the average of the pixel values of its north, east, south, and west neighbors.<\/p><p>First, number the pixels inside the masked region.<\/p><pre class=\"codeinput\">u = find(mask);\r\n<\/pre><p>Now number the pixels outside the region.<\/p><pre class=\"codeinput\">w = find(~mask);\r\n<\/pre><p>Using a technique I called \"neighbor indexing\" <a href=\"https:\/\/blogs.mathworks.com\/steve\/2008\/02\/25\/neighbor-indexing-2\/\">years ago<\/a>, we can find the north, east, and south, and west neighbors for all the pixels in the masked region this way:<\/p><pre class=\"codeinput\">M = size(mask,1);\r\nu_north = u - 1;\r\nu_east = u + M;\r\nu_south = u + 1;\r\nu_west = u - M;\r\n<\/pre><p>(Note: for the purpose of this blog post, I'm omitting code needed to handle the case where some mask pixels lie on the border of the image.)<\/p><p>Next, construct a sparse matrix representing the linear system.<\/p><pre class=\"codeinput\">v = ones(size(u));\r\nijv_mask = [<span class=\"keyword\">...<\/span>\r\n    u  u         1.00*v\r\n    u  u_north  -0.25*v\r\n    u  u_east   -0.25*v\r\n    u  u_south  -0.25*v\r\n    u  u_west   -0.25*v ];\r\n\r\nijv_nonmask = [w  w  1.00*ones(size(w))];\r\n\r\nijv = [ijv_mask; ijv_nonmask];\r\nA = sparse(ijv(:,1),ijv(:,2),ijv(:,3));\r\n<\/pre><p>What does the sparse linear system look like?<\/p><pre class=\"codeinput\">spy(A)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/exploring_regionfill_10.png\" alt=\"\"> <p>The right-hand vector, <tt>b<\/tt>, contains either the original pixel values (for pixels outside the mask) or 0 (for pixels inside the mask).<\/p><pre class=\"codeinput\">b = I(:);\r\nb(mask(:)) = 0;\r\n<\/pre><p>Finally we can solve the system!<\/p><pre class=\"codeinput\">x = A\\b;\r\n<\/pre><p>And then reshape the solution back into an image.<\/p><pre class=\"codeinput\">J2 = reshape(x,size(I));\r\nimshow(J2)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/exploring_regionfill_11.png\" alt=\"\"> <pre class=\"codeinput\">subplot(2,2,1)\r\nimshow(I)\r\naxis([230 300 140 180])\r\ntitle(<span class=\"string\">'Original'<\/span>)\r\n\r\nsubplot(2,2,2)\r\nimshow(J)\r\naxis([230 300 140 180])\r\ntitle(<span class=\"string\">'Region filled with mean'<\/span>)\r\n\r\nsubplot(2,2,3)\r\nimshow(J2)\r\naxis([230 300 140 180])\r\ntitle(<span class=\"string\">'Region filled using Laplace equation solution'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/exploring_regionfill_12.png\" alt=\"\"> <p>Today, the process of filling image regions is often called \"inpainting.\" Many different inpainting algorithms have been published.<\/p><p>I'll finish by confessing that I wrote an iterative solver for my first MATLAB implementation of this technique. When I showed my work to <a href=\"https:\/\/blogs.mathworks.com\/cleve\">Cleve<\/a>, he said, \"Why don't you use sparse backslash?\"<\/p><p>Oh! Lesson learned. Thanks, Cleve!<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_6ca26e89a11747ff8fed17be302da7ee() {\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='6ca26e89a11747ff8fed17be302da7ee ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 6ca26e89a11747ff8fed17be302da7ee';\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        copyright = 'Copyright 2015 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 copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\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     --> <\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_6ca26e89a11747ff8fed17be302da7ee()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; R2015a<br><\/p><\/div><!--\r\n6ca26e89a11747ff8fed17be302da7ee ##### SOURCE BEGIN #####\r\n%%\r\n% Today I want to show you how to use a linear system of 90,300 equations\r\n% and 90,300 unknowns to get rid of a leaf.\r\n%\r\n% <https:\/\/blogs.mathworks.com\/steve\/2015\/06\/04\/image-region-filling-an-updated-design\/ \r\n% Earlier this month I told you> how the function |roifill| was recently\r\n% renamed to <https:\/\/www.mathworks.com\/help\/images\/ref\/regionfill.html \r\n% |regionfill|> to correct a functional design flaw. Now I want\r\n% to follow up and talk about the algorithm underlying |regionfill|.\r\n%\r\n% This was one of my earliest algorithm projects at MathWorks. And it was\r\n% my earliest lesson the power of sparse linear algebra in MATLAB. About 21\r\n% years ago, Clay Thompson, one of the two developers of version 1.0 of\r\n% Image Processing Toolbox, commented to me one day that maybe we could\r\n% \"erase\" objects in an image by treating it as a \"soap-film problem.\" (The\r\n% other developer of that first toolbox version was\r\n% <https:\/\/blogs.mathworks.com\/loren Loren Shure>.)\r\n%\r\n% In other words, let's treat the pixel values surrounding the region we\r\n% want to erase as heights of a closed loop of wire and then figure out the\r\n% shape of a soap film bounded by that wire. \r\n%\r\n% That sounded plausible, so I looked into it. (The World Wide Web was\r\n% barely a year old at the time, and there was nothing like the modern\r\n% search engine, so I sought out information using what we used to call\r\n% \"books.\")\r\n%\r\n% I found that, if you make some simplifying assumptions (such as no\r\n% gravity), the soap film surface satisfies Laplace's equation:\r\n%\r\n% $$\\frac{\\delta^2 f}{\\delta x^2} + \\frac{\\delta^2 f}{\\delta y^2} = 0$$\r\n%\r\n% where the height of the wire loop around the region gives us the boundary\r\n% conditions for the PDE.\r\n%\r\n% In a simple, discretized version of Laplace's equation, the value of\r\n% every grid element in the interior of the region equals the average of\r\n% its north, east, south, and west neighbors in the grid.\r\n%\r\n% Before I explore that idea further, though, let's look at some pictures\r\n% to illustrate what we're trying to accomplish.\r\n%\r\n% Here is the \"trees\" image (in gray) with a region drawn around what looks\r\n% like a small leaf.\r\n\r\n[X,map] = imread('trees.tif');\r\nI = im2double(ind2gray(X,map));\r\nimshow(I)\r\nx = [240 245 255 270 285 290 280 270 250 240];\r\ny = [155 165 170 173 172 166 161 155 153 155];\r\nhold on\r\nplot(x,y,'y')\r\nhold off\r\n\r\n%%\r\n% Here's a zoomed-in view.\r\n\r\naxis([230 300 140 180])\r\n\r\n%%\r\n% We are going to \"erase\" that leaf by filling in the pixels inside that\r\n% region. The function |poly2mask| is useful for finding the set of pixels\r\n% inside a polygonal region.\r\n\r\nmask = poly2mask(x,y,258,350);\r\nimshow(mask)\r\n\r\n%%\r\n% Let's think about the gray-scale pixel values as heights and view the\r\n% image as a surface.\r\n\r\nh = surf(I);\r\nh.EdgeColor = 'none';\r\nh.FaceColor = 'interp';\r\naxis ij\r\nview(-10,80)\r\n\r\n%%\r\n% Here's a close-up of the region we're interested in.\r\n\r\nxlim([230 300])\r\nylim([140 180])\r\n\r\n%%\r\n% We can use the region mask computed above to \"cut out\" the part of the\r\n% surface inside the region.\r\n\r\nh.FaceAlpha = 'interp';\r\nh.AlphaData = ~mask;\r\n\r\n%%\r\n% A very simple approach to erasing the left would be to fill in the region\r\n% with the mean value of pixels in the region. Here's how to do it. (I just\r\n% love logical indexing with binary image masks!)\r\n\r\nJ = I;\r\nJ(mask) = mean(I(mask));\r\nh.ZData = J;\r\nh.AlphaData = true(size(J));\r\n\r\ntitle('Region filled with mean value')\r\n\r\n%%\r\nimshow(J)\r\ntitle('Region filled with mean value')\r\n\r\n%%\r\naxis([230 300 140 180])\r\n\r\n%%\r\n% Now let's work toward a solution based on Laplace's equation. I'm going\r\n% to set up a linear system of equations, $Ax = b$, so that the solution\r\n% gives me every pixel of the output image. I'll think of the pixels as\r\n% being numbered in columnwise order from 1 to the total number of pixels.\r\n% The trees image has 90,300 pixels, so this is going to be a 90,300 by\r\n% 90,300 linear system! It's a good thing it'll be very sparse.\r\n%\r\n% For every pixel outside the masked region, the equation is simply the\r\n% identity: output_pixel equals input_pixel.\r\n%\r\n% For every pixel inside the masked region, the pixel value should equal\r\n% the average of the pixel values of its north, east, south, and west\r\n% neighbors.\r\n%\r\n% First, number the pixels inside the masked region.\r\n\r\nu = find(mask);\r\n\r\n%%\r\n% Now number the pixels outside the region.\r\n\r\nw = find(~mask);\r\n\r\n%%\r\n% Using a technique I called \"neighbor indexing\"\r\n% <https:\/\/blogs.mathworks.com\/steve\/2008\/02\/25\/neighbor-indexing-2\/ years\r\n% ago>, we can find the north, east, and south, and west neighbors for all\r\n% the pixels in the masked region this way:\r\n\r\nM = size(mask,1);\r\nu_north = u - 1;\r\nu_east = u + M;\r\nu_south = u + 1;\r\nu_west = u - M;\r\n\r\n%%\r\n% (Note: for the purpose of this blog post, I'm omitting code needed to\r\n% handle the case where some mask pixels lie on the border of the image.)\r\n\r\n%%\r\n% Next, construct a sparse matrix representing the linear system.\r\nv = ones(size(u));\r\nijv_mask = [...\r\n    u  u         1.00*v\r\n    u  u_north  -0.25*v\r\n    u  u_east   -0.25*v\r\n    u  u_south  -0.25*v\r\n    u  u_west   -0.25*v ];\r\n\r\nijv_nonmask = [w  w  1.00*ones(size(w))];\r\n\r\nijv = [ijv_mask; ijv_nonmask];\r\nA = sparse(ijv(:,1),ijv(:,2),ijv(:,3));\r\n\r\n%%\r\n% What does the sparse linear system look like?\r\n\r\nspy(A)\r\n\r\n%%\r\n% The right-hand vector, |b|, contains either the original pixel values\r\n% (for pixels outside the mask) or 0 (for pixels inside the mask).\r\n\r\nb = I(:);\r\nb(mask(:)) = 0;\r\n\r\n%%\r\n% Finally we can solve the system!\r\nx = A\\b;\r\n\r\n%%\r\n% And then reshape the solution back into an image.\r\nJ2 = reshape(x,size(I));\r\nimshow(J2)\r\n\r\n%%\r\nsubplot(2,2,1)\r\nimshow(I)\r\naxis([230 300 140 180])\r\ntitle('Original')\r\n\r\nsubplot(2,2,2)\r\nimshow(J)\r\naxis([230 300 140 180])\r\ntitle('Region filled with mean')\r\n\r\nsubplot(2,2,3)\r\nimshow(J2)\r\naxis([230 300 140 180])\r\ntitle('Region filled using Laplace equation solution')\r\n\r\n%%\r\n% Today, the process of filling image regions is often called \"inpainting.\"\r\n% Many different inpainting algorithms have been published.\r\n%\r\n% I'll finish by confessing that I wrote an iterative solver for my first\r\n% MATLAB implementation of this technique. When I showed my work to\r\n% <https:\/\/blogs.mathworks.com\/cleve Cleve>, he said, \"Why don't you use\r\n% sparse backslash?\"\r\n%\r\n% Oh! Lesson learned. Thanks, Cleve!\r\n##### SOURCE END ##### 6ca26e89a11747ff8fed17be302da7ee\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/steve\/files\/exploring_regionfill_06.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>Today I want to show you how to use a linear system of 90,300 equations and 90,300 unknowns to get rid of a leaf.Earlier this month I told you how the function roifill was recently renamed to... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2015\/06\/17\/region-filling-and-laplaces-equation\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":1336,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[50,348,90,390,76,36,1119,308,288,68,296,374,170,368,190,338,340,72,398,52,100,216,360,298],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/1333"}],"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=1333"}],"version-history":[{"count":3,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/1333\/revisions"}],"predecessor-version":[{"id":1444,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/1333\/revisions\/1444"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media\/1336"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=1333"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=1333"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=1333"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}