{"id":5965,"date":"2015-05-08T09:00:09","date_gmt":"2015-05-08T13:00:09","guid":{"rendered":"https:\/\/blogs.mathworks.com\/pick\/?p=5965"},"modified":"2017-08-29T14:15:43","modified_gmt":"2017-08-29T18:15:43","slug":"how-do-you-create-a-mask-from-a-variable-thickness-open-freehand-curve","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/pick\/2015\/05\/08\/how-do-you-create-a-mask-from-a-variable-thickness-open-freehand-curve\/","title":{"rendered":"How do you create a mask from a variable-thickness open, freehand curve?"},"content":{"rendered":"<div class=\"content\"><!--introduction--><!--\/introduction--><p><a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/911\">Brett<\/a>'s Pick this week is <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/32696-2d-line-curvature-and-normals\">2D Line Curvature and Normals<\/a>, by <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/profile\/authors\/1097878-dirk-jan-kroon\">Dirk-Jan Kroon<\/a>.<\/p><p>Recently, I began working on an app to facilitate interactive image segmentations. Ultimately, I want to create a suite of tools that allow me to use some combination of automated and manual manipulations to create flexibly a complicated mask of my images. As part of that project, I want the ability to create closed <i>or open (!)<\/i> <a href=\"https:\/\/www.mathworks.com\/help\/images\/ref\/imfreehand.html\"><tt>imfreehand<\/tt><\/a> or <a href=\"https:\/\/www.mathworks.com\/help\/images\/ref\/impoly.html\"><tt>impoly<\/tt><\/a> regions, and to use those regions to modify my mask.<\/p><p>Creating masks from closed regions is easy with the <a title=\"https:\/\/www.mathworks.com\/help\/images\/ref\/imroi.html (link no longer works)\"><tt>imroi<\/tt><\/a> tools of the <a href=\"https:\/\/www.mathworks.com\/products\/image.html\">Image Processing Toolbox<\/a>; the \"createMask\" method trivializes the process. But creating masks from open regions can produce some unexpected, and unwanted, results.<\/p><p>Suppose, for example, that I wanted to create a mask from a manual tracing of the lower left petal of the \"yellow lily\" image that ships with the Image Processing Toolbox:<\/p><p>I read in and display the image, and trace the boundary of interest:<\/p><pre class=\"language-matlab\">lily = imread(<span class=\"string\">'C:\\Program Files\\MATLAB\\R2015a\\toolbox\\images\\imdata\\yellowlily.jpg'<\/span>);\r\nax(1) = subplot(1,2,1);\r\nimshow(lily)\r\ntitle(<span class=\"string\">'Lovely Lily'<\/span>)\r\n<span class=\"comment\">% I did this manually, saved the positions to a Mat-file:<\/span>\r\n<span class=\"comment\">% h = impoly(imgca,'closed',false);<\/span>\r\nload <span class=\"string\">outlinedLily<\/span>\r\nh = impoly(imgca,xsys,<span class=\"string\">'closed'<\/span>,false);\r\nxsys = getPosition(h);\r\nxs = xsys(:,1);\r\nys = xsys(:,2);\r\n<span class=\"comment\">% (Note: you can do this with |imfreehand|, too, but I find that I can be<\/span>\r\n<span class=\"comment\">%  much more accurate with |impoly|.)<\/span>\r\ndefaultMask = createMask(h);\r\nhold <span class=\"string\">on<\/span>\r\nplot(xs,ys,<span class=\"string\">'r.'<\/span>,<span class=\"string\">'markersize'<\/span>,16);\r\n<\/pre><p>If I simply use the createMask method, I get a mask of the filled region within the closed ROI:<\/p><pre class=\"language-matlab\">ax(2) = subplot(1,2,2);\r\nimshow(defaultMask);\r\ntitle(<span class=\"string\">'Default createMask'<\/span>)\r\n<\/pre><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/pick\/files\/normalLine1.png\" alt=\"\"> <\/p><p>But that's not what I wanted! I could use <a href=\"https:\/\/www.mathworks.com\/help\/images\/ref\/bwperim.html\">bwperim<\/a> (for example) on that mask, but then I would have to remove the effects of the unwanted region closing. What I <i>really<\/i> wanted was a thin \"shell\" around my drawn region, from which I could create my mask.<\/p><p>Enter Dirk's <tt>LineNormals2D<\/tt>.<\/p><pre class=\"language-matlab\">N = LineNormals2D(xsys); <span class=\"comment\">%Dirk-Jan!!!! This rocks!<\/span>\r\nthicknessMultiplier = 2;\r\nposn = [xs-thicknessMultiplier*N(:,1) ys-thicknessMultiplier*N(:,2);\r\n\tflipud(xs+thicknessMultiplier*N(:,1)) flipud(ys+thicknessMultiplier*N(:,2))];\r\nh = impoly(ax(2),posn);\r\nxsys = getPosition(h);\r\nxs = xsys(:,1);\r\nys = xsys(:,2);\r\nplot(ax(1),xs,ys,<span class=\"string\">'g.'<\/span>,<span class=\"string\">'markersize'<\/span>,16);\r\n<\/pre><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/pick\/files\/normalLine2.png\" alt=\"\"> <\/p><p>Now...<\/p><pre class=\"language-matlab\">imshow(createMask(h),<span class=\"string\">'parent'<\/span>,ax(2))\r\ntitle(<span class=\"string\">'That''s more like it!'<\/span>)\r\n<\/pre><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/pick\/files\/normalLine3.png\" alt=\"\"> <\/p><p>The line above, in which I used the output of <tt>LineNormals2D<\/tt> to create my \"posn\" vector, might seem a bit puzzling. In a nutshell, Dirk-Jan's function calculated the position of the <i>normal<\/i> at each point on my <tt>imroi<\/tt>. I subtract those values from, and then add them to, the points on my curve to calculate desired positions. (thicknessMultiplier just scales the amount of offset.) I use <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/flipud.html\"><tt>flipud<\/tt><\/a> to have the \"inward points\" flip around and start where the \"outward points\" left off:<\/p><pre class=\"language-matlab\">t = 0:pi\/64:3*pi;\r\nxy = [t',sin(t)'];\r\nN = LineNormals2D(xy);\r\nplot(xy(:,1),xy(:,2),<span class=\"string\">'b.'<\/span>)\r\nplot(xy(:,1)-N(:,1),xy(:,2)-N(:,2),<span class=\"string\">'r.'<\/span>)\r\nplot(xy(:,1)+N(:,1),xy(:,2)+N(:,2),<span class=\"string\">'g.'<\/span>)\r\nlegend({<span class=\"string\">'Original points'<\/span>,<span class=\"string\">'XY-N'<\/span>,<span class=\"string\">'XY+N'<\/span>})\r\nxlabel(<span class=\"string\">'t'<\/span>)\r\nylabel(<span class=\"string\">'sin(t)'<\/span>)\r\n<\/pre><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/pick\/files\/normalLine5.png\" alt=\"\"> <\/p><p>Zooming in, we can see what LineNormals2D did more explicitly:<\/p><pre class=\"language-matlab\">h = impoly(ax(2),posn);\r\nsetColor(h,[1 0 1])\r\nax.xlim\r\nset(ax,<span class=\"string\">'xlim'<\/span>,[165 230],<span class=\"string\">'ylim'<\/span>,[1120 1200])\r\n<\/pre><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/pick\/files\/normalLine4.png\" alt=\"\"> <\/p><p>There are undoubtedly other ways of creating this mask, but this is what initially occurred to me. Swag, of course, goes to Dirk-Jan for his fine code, but also to anyone who shows me another clever way to implement this functionality!<\/p><p>As always, I welcome your <a href=\"https:\/\/blogs.mathworks.com\/pick\/?p=5965#respond\">thoughts and comments<\/a>.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_737cbed635074e599cd573d08b1e0106() {\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='737cbed635074e599cd573d08b1e0106 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 737cbed635074e599cd573d08b1e0106';\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_737cbed635074e599cd573d08b1e0106()\"><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\n737cbed635074e599cd573d08b1e0106 ##### SOURCE BEGIN #####\r\n%% How do you create a mask from a variable-thickness open, freehand curve?\r\n%% \r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/911 Brett>'s Pick this week\r\n% is\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/32696-2d-line-curvature-and-normals 2D Line Curvature and Normals>, \r\n% by <https:\/\/www.mathworks.com\/matlabcentral\/profile\/authors\/1097878-dirk-jan-kroon Dirk-Jan Kroon>.\r\n%\r\n% Recently, I began working on an app to facilitate interactive image\r\n% segmentations. Ultimately, I want to create a suite of tools that allow\r\n% me to use some combination of automated and manual manipulations to\r\n% create flexibly a complicated mask of my images. As part of that project, I want\r\n% the ability to create closed _or open (!)_ \r\n% <https:\/\/www.mathworks.com\/help\/images\/ref\/imfreehand.html |imfreehand|> or \r\n% <https:\/\/www.mathworks.com\/help\/images\/ref\/impoly.html |impoly|> regions, and\r\n% to use those regions to modify my mask. \r\n%\r\n% Creating masks from closed regions is easy with the\r\n% <https:\/\/www.mathworks.com\/help\/images\/ref\/imroi.html |imroi|> tools of\r\n% the <https:\/\/www.mathworks.com\/products\/image\/index-b.html Image Processing Toolbox>; \r\n% the \"createMask\" method trivializes the process. But creating masks from\r\n% open regions can produce some unexpected, and unwanted, results.\r\n% \r\n% Suppose, for example, that I wanted to create a mask from a manual\r\n% tracing of the lower left petal of the \"yellow lily\" image that ships\r\n% with the Image Processing Toolbox:\r\n\r\n%%\r\n% I read in and display the image, and trace the boundary of interest:\r\n\r\n%%\r\n%\r\n%   lily = imread('C:\\Program Files\\MATLAB\\R2015a\\toolbox\\images\\imdata\\yellowlily.jpg');\r\n%   ax(1) = subplot(1,2,1);\r\n%   imshow(lily)\r\n%   title('Lovely Lily')\r\n%   % I did this manually, saved the positions to a Mat-file:\r\n%   % h = impoly(imgca,'closed',false); \r\n%   load outlinedLily\r\n%   h = impoly(imgca,xsys,'closed',false); \r\n%   xsys = getPosition(h);\r\n%   xs = xsys(:,1);\r\n%   ys = xsys(:,2);\r\n%   % (Note: you can do this with |imfreehand|, too, but I find that I can be\r\n%   %  much more accurate with |impoly|.)\r\n%   defaultMask = createMask(h);\r\n%   hold on\r\n%   plot(xs,ys,'r.','markersize',16);\r\n\r\n%%\r\n% If I simply use the createMask method, I get a mask of the filled region\r\n% within the closed ROI:\r\n\r\n%%\r\n%\r\n%   ax(2) = subplot(1,2,2);\r\n%   imshow(defaultMask);\r\n%   title('Default createMask')\r\n\r\n%%\r\n% \r\n% <<https:\/\/blogs.mathworks.com\/pick\/files\/normalLine1.png>>\r\n\r\n%%\r\n% But that's not what I wanted! I could use\r\n% <https:\/\/www.mathworks.com\/help\/images\/ref\/bwperim.html bwperim> (for\r\n% example) on that mask, but then I would have to remove the effects of the\r\n% unwanted region closing. What I _really_ wanted was a thin \"shell\" around\r\n% my drawn region, from which I could create my mask.\r\n\r\n%%\r\n% Enter Dirk's |LineNormals2D|.\r\n\r\n%%\r\n%\r\n%   N = LineNormals2D(xsys); %Dirk-Jan!!!! This rocks!\r\n%   thicknessMultiplier = 2;\r\n%   posn = [xs-thicknessMultiplier*N(:,1) ys-thicknessMultiplier*N(:,2);\r\n%   \tflipud(xs+thicknessMultiplier*N(:,1)) flipud(ys+thicknessMultiplier*N(:,2))];\r\n%   h = impoly(ax(2),posn);\r\n%   xsys = getPosition(h);\r\n%   xs = xsys(:,1);\r\n%   ys = xsys(:,2);\r\n%   plot(ax(1),xs,ys,'g.','markersize',16);\r\n\r\n%%\r\n% \r\n% <<https:\/\/blogs.mathworks.com\/pick\/files\/normalLine2.png>>\r\n\r\n%%\r\n% Now...\r\n\r\n%%\r\n%\r\n%   imshow(createMask(h),'parent',ax(2))\r\n%   title('That''s more like it!')\r\n\r\n%%\r\n% \r\n% <<https:\/\/blogs.mathworks.com\/pick\/files\/normalLine3.png>>\r\n\r\n%%\r\n% The line above, in which I used the output of |LineNormals2D| to create\r\n% my \"posn\" vector, might seem a bit puzzling. In a nutshell, Dirk-Jan's\r\n% function calculated the position of the _normal_ at each point on my\r\n% |imroi|. I subtract those values from, and then add them to, the points\r\n% on my curve to calculate desired positions. (thicknessMultiplier just\r\n% scales the amount of offset.) I use <https:\/\/www.mathworks.com\/help\/matlab\/ref\/flipud.html |flipud|>\r\n% to have the \"inward points\" flip around and start where the \"outward\r\n% points\" left off:\r\n\r\n%%\r\n%\r\n%   t = 0:pi\/64:3*pi;\r\n%   xy = [t',sin(t)'];\r\n%   N = LineNormals2D(xy);\r\n%   plot(xy(:,1),xy(:,2),'b.')\r\n%   plot(xy(:,1)-N(:,1),xy(:,2)-N(:,2),'r.')\r\n%   plot(xy(:,1)+N(:,1),xy(:,2)+N(:,2),'g.')\r\n%   legend({'Original points','XY-N','XY+N'})\r\n%   xlabel('t')\r\n%   ylabel('sin(t)')\r\n\r\n%%\r\n% \r\n% <<https:\/\/blogs.mathworks.com\/pick\/files\/normalLine5.png>>\r\n\r\n%%\r\n% Zooming in, we can see what LineNormals2D did more explicitly:\r\n\r\n%%\r\n%\r\n%   h = impoly(ax(2),posn);\r\n%   setColor(h,[1 0 1])\r\n%   ax.xlim\r\n%   set(ax,'xlim',[165 230],'ylim',[1120 1200])\r\n\r\n%%\r\n% \r\n% <<https:\/\/blogs.mathworks.com\/pick\/files\/normalLine4.png>>\r\n\r\n%% \r\n% There are undoubtedly other ways of creating this mask, but this is what\r\n% initially occurred to me. Swag, of course, goes to Dirk-Jan for his fine\r\n% code, but also to anyone who shows me another clever way to implement\r\n% this functionality!\r\n\r\n%%\r\n% As always, I welcome your\r\n% <https:\/\/blogs.mathworks.com\/pick\/?p=5965#respond thoughts and comments>.\r\n##### SOURCE END ##### 737cbed635074e599cd573d08b1e0106\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img decoding=\"async\"  class=\"img-responsive\" src=\"https:\/\/blogs.mathworks.com\/pick\/files\/normalLine1.png\" onError=\"this.style.display ='none';\" \/><\/div><p>Brett's Pick this week is 2D Line Curvature and Normals, by Dirk-Jan Kroon.Recently, I began working on an app to facilitate interactive image segmentations. Ultimately, I want to create a suite of... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/pick\/2015\/05\/08\/how-do-you-create-a-mask-from-a-variable-thickness-open-freehand-curve\/\">read more >><\/a><\/p>","protected":false},"author":34,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[16],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/posts\/5965"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/users\/34"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/comments?post=5965"}],"version-history":[{"count":7,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/posts\/5965\/revisions"}],"predecessor-version":[{"id":8840,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/posts\/5965\/revisions\/8840"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/media?parent=5965"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/categories?post=5965"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/pick\/wp-json\/wp\/v2\/tags?post=5965"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}