{"id":1742,"date":"2016-04-12T17:36:33","date_gmt":"2016-04-12T21:36:33","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=1742"},"modified":"2019-11-01T16:36:26","modified_gmt":"2019-11-01T20:36:26","slug":"intersecting-curves-that-dont-intersect","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2016\/04\/12\/intersecting-curves-that-dont-intersect\/","title":{"rendered":"Intersecting curves that don&#8217;t intersect"},"content":{"rendered":"<div class=\"content\"><p>A <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/answers\/265551-intersect-problem-when-none-of-the-pixels-overlap\">post in MATLAB Answers<\/a> earlier this year reminded me that working on a discrete grid can really mess up apparently obvious notions about geometry.<\/p><p>User <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/profile\/authors\/4675422-hg\">Hg<\/a> offered an image containing two intersecting curves. The problem is that the intersecting curves didn't intersect!<\/p><p>Here's the image:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/www.mathworks.com\/matlabcentral\/answers\/uploaded_files\/44249\/intersect2.png\" alt=\"\"> <\/p><p>The red curve and the blue curve, which obviously cross each other, do not have any pixels in common. Hg's question: \"What can I do to estimate the intersection point?\"<\/p><p>Before I get into answering the original question, let me take a brief side trip to see if I can reconstruct the original input matrix (or at least something close). The image posted on MATLAB Answers had the pixels obviously blown up, with different colors applied to distinguish between the curves. Can I get back to just a matrix with 0s, 1s, and 2s corresponding to pixels belonging to the background and the two curves?<\/p><p>It helps that the image is a PNG file, which is losslessly compressed. I took a close look at the pixels using <tt>imtool<\/tt>, and I was able to determine that pixels belong to one curve were colored using [237 28 36], and pixels belonging to the second curve were colored using [0 162 232]. Also, each pixel seemed to be magnified by about a factor of 16. Let's run with those numbers.<\/p><pre class=\"codeinput\">url = <span class=\"string\">'https:\/\/www.mathworks.com\/matlabcentral\/answers\/uploaded_files\/44249\/intersect2.png'<\/span>;\r\nrgb = imread(url);\r\nred = rgb(:,:,1);\r\ngreen = rgb(:,:,2);\r\nblue = rgb(:,:,3);\r\n<\/pre><pre class=\"codeinput\">curve1 = (red == 237) &amp; (green == 28) &amp; (blue == 36);\r\ncurve2 = (red == 0) &amp; (green == 162) &amp; (blue == 232);\r\nL = zeros(size(curve1));\r\nL(curve1) = 1;\r\nL(curve2) = 2;\r\nimshow(L,[],<span class=\"string\">'InitialMagnification'<\/span>,<span class=\"string\">'fit'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/IntersectingLinesExample_01.png\" alt=\"\"> <p>That looks good. Let's try shrinking it down by a factor of 16.<\/p><pre class=\"codeinput\">L16 = imresize(L,1\/16,<span class=\"string\">'nearest'<\/span>);\r\nimshow(L16,[],<span class=\"string\">'InitialMagnification'<\/span>,<span class=\"string\">'fit'<\/span>);\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/IntersectingLinesExample_02.png\" alt=\"\"> <p>Well, that's not perfect. But it's close enough to work with for answering Hg's question.<\/p><p><a href=\"https:\/\/www.mathworks.com\/matlabcentral\/profile\/authors\/1343420-image-analyst\">Image Analyst<\/a> offered a potential solution:<\/p><p><i>There is no one pixel where the overlap occurs. If you'll accept any of those 4 pixel locations as an overlap, then perhaps if you dilated, ANDed, then called bwulterode. Something like (untested)<\/i><\/p><pre>intImage = imdilate(bw1, true(3)) &amp; imdilate(bw2, true(3));\r\nintPoints = bwulterode(intImage);<\/pre><p>Image Analyst's idea is to use morphological dilation to thicken each curve individually, then use a logical AND operator (<tt>&amp;<\/tt>) to determine the set of pixels that belong to both thickened curves, and then compute the ultimate erosion using <tt>bwerode<\/tt> to shrink the intersection area down. Here it is in several steps.<\/p><pre class=\"codeinput\">curve1 = (L16 == 1);\r\ncurve2 = (L16 == 2);\r\ncurve1_thickened = imdilate(curve1,ones(3,3));\r\nimshow(curve1_thickened,<span class=\"string\">'InitialMagnification'<\/span>,<span class=\"string\">'fit'<\/span>)\r\ntitle(<span class=\"string\">'Curve 1 (thickened)'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/IntersectingLinesExample_03.png\" alt=\"\"> <pre class=\"codeinput\">curve2_thickened = imdilate(curve2,ones(3,3));\r\nimshow(curve2_thickened,<span class=\"string\">'InitialMagnification'<\/span>,<span class=\"string\">'fit'<\/span>)\r\ntitle(<span class=\"string\">'Curve 2 (thickened)'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/IntersectingLinesExample_04.png\" alt=\"\"> <pre class=\"codeinput\">curve_intersection = curve1_thickened &amp; curve2_thickened;\r\nimshow(curve_intersection,<span class=\"string\">'InitialMagnification'<\/span>,<span class=\"string\">'fit'<\/span>)\r\ntitle(<span class=\"string\">'Intersection of thickened curves'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/IntersectingLinesExample_05.png\" alt=\"\"> <pre class=\"codeinput\">ultimate_erosion = bwulterode(curve_intersection);\r\nimshow(ultimate_erosion,<span class=\"string\">'InitialMagnification'<\/span>,<span class=\"string\">'fit'<\/span>)\r\ntitle(<span class=\"string\">'Ultimate erosion of intersection'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/IntersectingLinesExample_06.png\" alt=\"\"> <p>Let's overlay the output of the ultimate erosion on top of the original image.<\/p><pre class=\"codeinput\">imshow(imfuse(L16,ultimate_erosion,<span class=\"string\">'falsecolor'<\/span>),<span class=\"string\">'InitialMagnification'<\/span>,<span class=\"string\">'fit'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/IntersectingLinesExample_07.png\" alt=\"\"> <p>You can see that the ultimate erosion is not a single point. An alternative is to compute the centroids of the intersection \"blobs.\"<\/p><pre class=\"codeinput\">s = regionprops(ultimate_erosion,<span class=\"string\">'Centroid'<\/span>)\r\n<\/pre><pre class=\"codeoutput\">\r\ns = \r\n\r\n    Centroid: [9.5000 6.5000]\r\n\r\n<\/pre><p>There's just one intersection blob, and its centroid is at (9.5,6.5). Let's plot that.<\/p><pre class=\"codeinput\">imshow(L16,[],<span class=\"string\">'InitialMagnification'<\/span>,<span class=\"string\">'fit'<\/span>)\r\nhold <span class=\"string\">on<\/span>\r\nplot(s.Centroid(1),s.Centroid(2),<span class=\"string\">'go'<\/span>,<span class=\"string\">'MarkerSize'<\/span>,15,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'MarkerFaceColor'<\/span>,<span class=\"string\">'g'<\/span>)\r\nhold <span class=\"string\">off<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/IntersectingLinesExample_08.png\" alt=\"\"> <p>How would you approach this problem? Leave your ideas and thoughts in the comments.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_2a4c25d036bb4df48261232f9a96d98e() {\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='2a4c25d036bb4df48261232f9a96d98e ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 2a4c25d036bb4df48261232f9a96d98e';\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 2016 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_2a4c25d036bb4df48261232f9a96d98e()\"><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; R2016a<br><\/p><\/div><!--\r\n2a4c25d036bb4df48261232f9a96d98e ##### SOURCE BEGIN #####\r\n%%\r\n% A <https:\/\/www.mathworks.com\/matlabcentral\/answers\/265551-intersect-problem-when-none-of-the-pixels-overlap \r\n% post in MATLAB Answers> earlier this year reminded me that working on a\r\n% discrete grid can really mess up apparently obvious notions about\r\n% geometry.\r\n%\r\n% User <https:\/\/www.mathworks.com\/matlabcentral\/profile\/authors\/4675422-hg\r\n% Hg> offered an image containing two intersecting curves. The problem is\r\n% that the intersecting curves didn't intersect!\r\n%\r\n% Here's the image:\r\n%\r\n% <<https:\/\/www.mathworks.com\/matlabcentral\/answers\/uploaded_files\/44249\/intersect2.png>>\r\n%\r\n% The red curve and the blue curve, which obviously cross each other, do not\r\n% have any pixels in common. Hg's question: \"What can I do to estimate the\r\n% intersection point?\"\r\n%\r\n% Before I get into answering the original question, let me take a brief side trip\r\n% to see if I can reconstruct the original input matrix (or at least\r\n% something close). The image posted on MATLAB Answers had the pixels\r\n% obviously blown up, with different colors applied to distinguish between\r\n% the curves. Can I get back to just a matrix with 0s, 1s, and 2s\r\n% corresponding to pixels belonging to the background and the two curves?\r\n%\r\n% It helps that the image is a PNG file, which is losslessly compressed. I\r\n% took a close look at the pixels using |imtool|, and I was able to determine\r\n% that pixels belong to one curve were colored using [237 28 36], and pixels\r\n% belonging to the second curve were colored using [0 162 232]. Also, each\r\n% pixel seemed to be magnified by about a factor of 16. Let's run with those\r\n% numbers.\r\n\r\nurl = 'https:\/\/www.mathworks.com\/matlabcentral\/answers\/uploaded_files\/44249\/intersect2.png';\r\nrgb = imread(url);\r\nred = rgb(:,:,1);\r\ngreen = rgb(:,:,2);\r\nblue = rgb(:,:,3);\r\n\r\n%%\r\n\r\ncurve1 = (red == 237) & (green == 28) & (blue == 36);\r\ncurve2 = (red == 0) & (green == 162) & (blue == 232);\r\nL = zeros(size(curve1));\r\nL(curve1) = 1;\r\nL(curve2) = 2;\r\nimshow(L,[],'InitialMagnification','fit')\r\n\r\n%%\r\n% That looks good. Let's try shrinking it down by a factor of 16.\r\n\r\nL16 = imresize(L,1\/16,'nearest');\r\nimshow(L16,[],'InitialMagnification','fit');\r\n\r\n%%\r\n% Well, that's not perfect. But it's close enough to work with for answering\r\n% Hg's question.\r\n%\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/profile\/authors\/1343420-image-analyst \r\n% Image Analyst> offered a potential solution:\r\n% \r\n% _There is no one pixel where the overlap occurs. If you'll accept any of\r\n% those 4 pixel locations as an overlap, then perhaps if you dilated, ANDed,\r\n% then called bwulterode. Something like (untested)_\r\n% \r\n%  intImage = imdilate(bw1, true(3)) & imdilate(bw2, true(3));\r\n%  intPoints = bwulterode(intImage);\r\n%\r\n% Image Analyst's idea is to use morphological dilation to thicken each\r\n% curve individually, then use a logical AND operator (|&|) to determine the\r\n% set of pixels that belong to both thickened curves, and then compute the\r\n% ultimate erosion using |bwerode| to shrink the intersection area down.\r\n% Here it is in several steps.\r\n\r\ncurve1 = (L16 == 1);\r\ncurve2 = (L16 == 2);\r\ncurve1_thickened = imdilate(curve1,ones(3,3));\r\nimshow(curve1_thickened,'InitialMagnification','fit')\r\ntitle('Curve 1 (thickened)')\r\n\r\n%%\r\ncurve2_thickened = imdilate(curve2,ones(3,3));\r\nimshow(curve2_thickened,'InitialMagnification','fit')\r\ntitle('Curve 2 (thickened)')\r\n\r\n%%\r\ncurve_intersection = curve1_thickened & curve2_thickened;\r\nimshow(curve_intersection,'InitialMagnification','fit')\r\ntitle('Intersection of thickened curves')\r\n\r\n%%\r\nultimate_erosion = bwulterode(curve_intersection);\r\nimshow(ultimate_erosion,'InitialMagnification','fit')\r\ntitle('Ultimate erosion of intersection')\r\n\r\n%%\r\n% Let's overlay the output of the ultimate erosion on top of the original\r\n% image.\r\n\r\nimshow(imfuse(L16,ultimate_erosion,'falsecolor'),'InitialMagnification','fit')\r\n\r\n%%\r\n% You can see that the ultimate erosion is not a single point. An\r\n% alternative is to compute the centroids of the intersection \"blobs.\"\r\n\r\ns = regionprops(ultimate_erosion,'Centroid')\r\n\r\n%%\r\n% There's just one intersection blob, and its centroid is at (9.5,6.5).\r\n% Let's plot that.\r\n\r\nimshow(L16,[],'InitialMagnification','fit')\r\nhold on\r\nplot(s.Centroid(1),s.Centroid(2),'go','MarkerSize',15,...\r\n    'MarkerFaceColor','g')\r\nhold off\r\n\r\n%%\r\n% How would you approach this problem? Leave your ideas and thoughts in the\r\n% comments.\r\n\r\n##### SOURCE END ##### 2a4c25d036bb4df48261232f9a96d98e\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/steve\/files\/IntersectingLinesExample_07.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>A post in MATLAB Answers earlier this year reminded me that working on a discrete grid can really mess up apparently obvious notions about geometry.User Hg offered an image containing two... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2016\/04\/12\/intersecting-curves-that-dont-intersect\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":1750,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[444,90,124,869,76,156,36,68,168,190,52,130],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/1742"}],"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=1742"}],"version-history":[{"count":2,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/1742\/revisions"}],"predecessor-version":[{"id":1752,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/1742\/revisions\/1752"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media\/1750"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=1742"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=1742"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=1742"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}