{"id":298,"date":"2015-09-17T11:16:24","date_gmt":"2015-09-17T15:16:24","guid":{"rendered":"https:\/\/blogs.mathworks.com\/graphics\/?p=298"},"modified":"2015-09-17T11:16:24","modified_gmt":"2015-09-17T15:16:24","slug":"what-is-a-contour","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/graphics\/2015\/09\/17\/what-is-a-contour\/","title":{"rendered":"What is a Contour?"},"content":{"rendered":"<div class=\"content\"><h3>What is a Contour?<\/h3><p>Last year we explored how <a href=\"https:\/\/blogs.mathworks.com\/graphics\/2014\/11\/18\/what-is-a-surface\/\">surfaces perform interpolation<\/a>. Today we're going to take a look at some closely related functions; the contour family. The family of contour functions consists <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/contour.html\">contour<\/a>, <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/contour3.html\">contour3<\/a>, and <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/contourf.html\">contourf<\/a> and a couple of other minor ones.<\/p><p>We'll start with a simple 7x5 array. We'll create a surface and a filled contour using contourf.<\/p><pre class=\"codeinput\">rng <span class=\"string\">default<\/span>\r\nz = randn(7,5)\r\n\r\na1 = subplot(1,2,1);\r\nh = surf(z);\r\nh.FaceColor = <span class=\"string\">'interp'<\/span>;\r\naxis <span class=\"string\">tight<\/span>\r\n\r\na2 = subplot(1,2,2);\r\ncontourf(z)\r\n<\/pre><pre class=\"codeoutput\">\r\nz =\r\n\r\n    0.5377    0.3426    0.7147   -1.2075    0.2939\r\n    1.8339    3.5784   -0.2050    0.7172   -0.7873\r\n   -2.2588    2.7694   -0.1241    1.6302    0.8884\r\n    0.8622   -1.3499    1.4897    0.4889   -1.1471\r\n    0.3188    3.0349    1.4090    1.0347   -1.0689\r\n   -1.3077    0.7254    1.4172    0.7269   -0.8095\r\n   -0.4336   -0.0631    0.6715   -0.3034   -2.9443\r\n\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/what_is_a_contour_01.png\" alt=\"\"> <p>It's hard to see the relationship at first. But if we look straight down on the surface and square up the aspect ratio of the axes (and squint a little) we can see that they are related.<\/p><pre class=\"codeinput\">axes(a1)\r\nview(2)\r\naxis([a1 a2],<span class=\"string\">'equal'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/what_is_a_contour_02.png\" alt=\"\"> <p>But, they certainly don't look the same though, do they?<\/p><p>It's easier to see what's going on if we look at a single contour level. That's easy to do with contour by setting the LevelList property. With surface we can simulate it by adding a white plane a Z==0 to hide the part of the surface which is below that.<\/p><pre class=\"codeinput\">axes(a1)\r\np = surface([1 5;1 5],[1 1; 7 7],zeros(2));\r\np.FaceColor = <span class=\"string\">'white'<\/span>;\r\np.EdgeColor = <span class=\"string\">'none'<\/span>;\r\n\r\naxes(a2)\r\ncontourf(z,<span class=\"string\">'LevelList'<\/span>,0)\r\naxis <span class=\"string\">equal<\/span>\r\ngrid <span class=\"string\">on<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/what_is_a_contour_03.png\" alt=\"\"> <p>They still don't look like they're showing the same thing though, do they? But look closely. Look at where the curve intersects the grid lines. The intersection points are the same in the two pictures. The differences are all between those intersection points. What we're seeing is the same thing we saw when we looked at surfaces. The interpolation function matters, and surface and contour use different interpolation functions.<\/p><p><a href=\"https:\/\/blogs.mathworks.com\/graphics\/2014\/11\/18\/what-is-a-surface\/\">Last time<\/a> we learned about the piecewise linear interpolation that surface uses, and we wrote an interpsurf function to give use more control by using the <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/interp2.html\">interp2 function<\/a>. Let's try using that with interp2's linear method and see if that matches what contour is doing.<\/p><pre class=\"codeinput\">axes(a1)\r\ninterpsurf(z,<span class=\"string\">'linear'<\/span>)\r\np = surface([1 5;1 5],[1 1; 7 7],zeros(2));\r\np.FaceColor = <span class=\"string\">'white'<\/span>;\r\np.EdgeColor = <span class=\"string\">'none'<\/span>;\r\nview(2)\r\naxis <span class=\"string\">tight<\/span>\r\naxis <span class=\"string\">equal<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/what_is_a_contour_04.png\" alt=\"\"> <p>No, they're still not the same. We can see that interpsurf is drawing curves in between the grid lines. You may remember that when we discussed the bilinear interpolation that interp2 is using here, we saw that it's giving us a quadratic equation of this form.<\/p><p>$$f(x,y) = c_1 + c_2 x + c_3 y + c_4 x y$$<\/p><p>This tells us that the curves we're seeing between the grids are actually segments of hyperbolas. But contour is drawing straight lines in between the grid. Its interpolation scheme is basically to do linear interpolation on each of the edges and then draw a straight line connecting them.<\/p><p>But there's another important difference, isn't there? You can see that in the squares where the curve intersects all four sides, there are a couple of different ways to connect the intersections. These are called the ambiguous cases. There are a couple of different ways to decide which way to go in these cases, and the surface and contour functionss don't use the same technique. Can you spot the square where the two algorithms connected the intersections differently?<\/p><p>We can use the same technique we used in the intersurf function to perform our own interpolation on the input to contour like this:<\/p><pre class=\"codeinput\">axes(a2)\r\n[x, y] = meshgrid(1:5,1:7);\r\n[x2,y2] = meshgrid(1:.1:5,1:.1:7);\r\nz2 = interp2(x,y,z, x2,y2, <span class=\"string\">'linear'<\/span>);\r\ncontourf(x2,y2,z2,<span class=\"string\">'LevelList'<\/span>,0)\r\naxis <span class=\"string\">equal<\/span>\r\ngrid <span class=\"string\">on<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/what_is_a_contour_05.png\" alt=\"\"> <p>That looks pretty good, and it matches our surface, but when we go back to the default LevelList we can see that it still doesn't look very smooth. That's because bilinear interpolation is only <a href=\"https:\/\/en.wikipedia.org\/wiki\/Smoothness\">C0 continuous<\/a> between 2x2 sections. This shows up as sudden changes of direction as the curves cross these boundaries.<\/p><pre class=\"codeinput\">axes(a1)\r\ninterpsurf(z,<span class=\"string\">'linear'<\/span>)\r\nview(2)\r\naxis <span class=\"string\">tight<\/span>\r\naxis <span class=\"string\">equal<\/span>\r\naxes(a2)\r\ncontourf(x2,y2,z2)\r\naxis <span class=\"string\">equal<\/span>\r\ngrid <span class=\"string\">on<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/what_is_a_contour_06.png\" alt=\"\"> <p>We don't usually notice this too much with surfaces because the colors or lighting tends to soften these discontinuities in the derivative. But the hard edges in a contour make it very noticeable.<\/p><p>What if we switch to a smoother interpolation method? For example, cubic.<\/p><pre class=\"codeinput\">axes(a1)\r\ninterpsurf(z,<span class=\"string\">'cubic'<\/span>)\r\nview(2)\r\naxis <span class=\"string\">tight<\/span>\r\naxis <span class=\"string\">equal<\/span>\r\naxes(a2)\r\nz2 = interp2(x,y,z, x2,y2, <span class=\"string\">'cubic'<\/span>);\r\ncontourf(x2,y2,z2)\r\naxis <span class=\"string\">equal<\/span>\r\ngrid <span class=\"string\">on<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/what_is_a_contour_07.png\" alt=\"\"> <p>That looks pretty nice, doesn't it? Can you use that to create an interpcontourf function, just like the <a href=\"https:\/\/blogs.mathworks.com\/graphics\/2014\/11\/18\/what-is-a-surface\/\">interpsurf function<\/a> we created last time?  But remember, there is no single \"correct\" interpolation scheme. You need to know something about the characteristics of your data to know which scheme is most appropriate. In some cases a very smooth interpolation method like cubic can result in a contour which makes your data look smoother than it really should.<\/p><p>The contour and surface functions use the simplest and fastest interpolation schemes for their jobs. That's because you can always do preprocessing up front to get characteristics like smoother interpolation, but you really can't do preprocessing to get performance.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_5d7d85a19c9643d08ac8fb90e2f75507() {\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='5d7d85a19c9643d08ac8fb90e2f75507 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 5d7d85a19c9643d08ac8fb90e2f75507';\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_5d7d85a19c9643d08ac8fb90e2f75507()\"><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; R2015b<br><\/p><\/div><!--\r\n5d7d85a19c9643d08ac8fb90e2f75507 ##### SOURCE BEGIN #####\r\n%% What is a Contour?\r\n%\r\n% Last year we explored how <https:\/\/blogs.mathworks.com\/graphics\/2014\/11\/18\/what-is-a-surface\/ surfaces perform interpolation>. \r\n% Today we're going to take a look at some closely related functions; the\r\n% contour family. The family of contour functions consists\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/contour.html contour>,\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/contour3.html contour3>, and\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/contourf.html contourf> and a \r\n% couple of other minor ones.\r\n%\r\n% We'll start with a simple 7x5 array. We'll create a surface and a filled \r\n% contour using contourf.\r\n%\r\nrng default\r\nz = randn(7,5)\r\n\r\na1 = subplot(1,2,1);\r\nh = surf(z);\r\nh.FaceColor = 'interp';\r\naxis tight\r\n\r\na2 = subplot(1,2,2);\r\ncontourf(z)\r\n\r\n%%\r\n% It's hard to see the relationship at first. But if we look straight down on the\r\n% surface and square up the aspect ratio of the axes (and squint a little)\r\n% we can see that they are related.\r\n%\r\naxes(a1)\r\nview(2)\r\naxis([a1 a2],'equal')\r\n\r\n%%\r\n% But, they certainly don't look the same though, do they?\r\n%\r\n% It's easier to see what's going on if we look at a single contour level.\r\n% That's easy to do with contour by setting the LevelList property. \r\n% With surface we can simulate it by adding\r\n% a white plane a Z==0 to hide the part of the surface which is below that.\r\naxes(a1)\r\np = surface([1 5;1 5],[1 1; 7 7],zeros(2));\r\np.FaceColor = 'white';\r\np.EdgeColor = 'none';\r\n\r\naxes(a2)\r\ncontourf(z,'LevelList',0)\r\naxis equal\r\ngrid on\r\n\r\n%%\r\n% They still don't look like they're showing the same thing though, do\r\n% they? But look closely. Look at where the curve intersects the grid\r\n% lines. The intersection points are the same in the two pictures. The\r\n% differences are all between those intersection points. What we're seeing\r\n% is the same thing we saw when we looked at surfaces. The interpolation\r\n% function matters, and surface and contour use different interpolation\r\n% functions.\r\n%\r\n% <https:\/\/blogs.mathworks.com\/graphics\/2014\/11\/18\/what-is-a-surface\/ Last time>\r\n% we learned about the piecewise linear interpolation that\r\n% surface uses, and we wrote an interpsurf function to give use more\r\n% control by using the <https:\/\/www.mathworks.com\/help\/matlab\/ref\/interp2.html interp2 function>. \r\n% Let's try using that with interp2's linear method and see if that matches \r\n% what contour is doing.\r\n%\r\naxes(a1)\r\ninterpsurf(z,'linear')\r\np = surface([1 5;1 5],[1 1; 7 7],zeros(2));\r\np.FaceColor = 'white';\r\np.EdgeColor = 'none';\r\nview(2)\r\naxis tight\r\naxis equal\r\n\r\n%%\r\n% No, they're still not the same. We can see that interpsurf is drawing\r\n% curves in between the grid lines. You may remember that when we discussed \r\n% the bilinear interpolation that interp2 is using here, we saw that it's \r\n% giving us a quadratic equation of this form.\r\n%\r\n% $$f(x,y) = c_1 + c_2 x + c_3 y + c_4 x y$$\r\n%\r\n% This tells us that the curves we're seeing between the grids are actually\r\n% segments of hyperbolas. But contour is drawing straight lines in between \r\n% the grid. Its interpolation scheme is basically to do linear\r\n% interpolation on each of the edges and then draw a straight line\r\n% connecting them.\r\n%\r\n% But there's another important difference, isn't there? You can see that \r\n% in the squares where the curve intersects all four sides, there are a \r\n% couple of different ways to connect the intersections. These are called\r\n% the ambiguous cases. There are a couple of different ways to decide which\r\n% way to go in these cases, and the surface and contour functionss don't use\r\n% the same technique. Can you spot the square where the two algorithms \r\n% connected the intersections differently?\r\n%\r\n\r\n%%\r\n% We can use the same technique we used in the intersurf function to\r\n% perform our own interpolation on the input to contour like this:\r\n%\r\naxes(a2)\r\n[x, y] = meshgrid(1:5,1:7);\r\n[x2,y2] = meshgrid(1:.1:5,1:.1:7);\r\nz2 = interp2(x,y,z, x2,y2, 'linear');\r\ncontourf(x2,y2,z2,'LevelList',0)\r\naxis equal\r\ngrid on\r\n\r\n%%\r\n% That looks pretty good, and it matches our surface, but when we go back to the default LevelList we\r\n% can see that it still doesn't look very smooth. That's because bilinear\r\n% interpolation is only <https:\/\/en.wikipedia.org\/wiki\/Smoothness C0\r\n% continuous> between 2x2 sections. This shows up as sudden changes of\r\n% direction as the curves cross these boundaries.\r\n%\r\naxes(a1)\r\ninterpsurf(z,'linear')\r\nview(2)\r\naxis tight\r\naxis equal\r\naxes(a2)\r\ncontourf(x2,y2,z2)\r\naxis equal\r\ngrid on\r\n\r\n%%\r\n% We don't usually notice this too much with surfaces because the colors or\r\n% lighting tends to soften these discontinuities in the derivative. But the\r\n% hard edges in a contour make it very noticeable. \r\n%\r\n% What if we switch to a smoother interpolation method? For example, cubic.\r\n%\r\naxes(a1)\r\ninterpsurf(z,'cubic')\r\nview(2)\r\naxis tight\r\naxis equal\r\naxes(a2)\r\nz2 = interp2(x,y,z, x2,y2, 'cubic');\r\ncontourf(x2,y2,z2)\r\naxis equal\r\ngrid on\r\n\r\n%%\r\n% That looks pretty nice, doesn't it? Can you use that to create an\r\n% interpcontourf function, just like the <https:\/\/blogs.mathworks.com\/graphics\/2014\/11\/18\/what-is-a-surface\/ interpsurf function>\r\n% we created last time?  But remember, there is no single \"correct\" \r\n% interpolation scheme. You need to know something about the \r\n% characteristics of your data to know which scheme is most appropriate. In\r\n% some cases a very smooth interpolation method like cubic can result in a\r\n% contour which makes your data look smoother than it really should.\r\n%\r\n% The contour and surface functions use the simplest and fastest\r\n% interpolation schemes for their jobs. That's because you can always do\r\n% preprocessing up front to get characteristics like smoother\r\n% interpolation, but you really can't do preprocessing to get performance.\r\n%\r\n\r\n##### SOURCE END ##### 5d7d85a19c9643d08ac8fb90e2f75507\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/graphics\/files\/feature_image\/what_is_a_contour_thumbnail.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>What is a Contour?Last year we explored how surfaces perform interpolation. Today we're going to take a look at some closely related functions; the contour family. The family of contour functions... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/graphics\/2015\/09\/17\/what-is-a-contour\/\">read more >><\/a><\/p>","protected":false},"author":89,"featured_media":304,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[5],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/298"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/users\/89"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/comments?post=298"}],"version-history":[{"count":5,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/298\/revisions"}],"predecessor-version":[{"id":303,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/298\/revisions\/303"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/media\/304"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/media?parent=298"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/categories?post=298"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/tags?post=298"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}