{"id":3851,"date":"2020-10-20T08:14:25","date_gmt":"2020-10-20T12:14:25","guid":{"rendered":"https:\/\/blogs.mathworks.com\/loren\/?p=3851"},"modified":"2020-12-10T17:34:54","modified_gmt":"2020-12-10T22:34:54","slug":"fill-image-nan-values-with-local-average","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/loren\/2020\/10\/20\/fill-image-nan-values-with-local-average\/","title":{"rendered":"Fill Image NaN Values with Local Average"},"content":{"rendered":"<div class=\"content\"><!--introduction--><p>Recently we had a customer ask how to fill in NaN values in an image with a neighborhood local mean. My friend, colleague, and occasional blogger, <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/911\">Brett Shoelson<\/a>, joins me today to show you several viable techniques.<\/p><!--\/introduction--><h3>Contents<\/h3><div><ul><li><a href=\"#24570e80-3b19-4e89-83d6-8c201691513f\">Create data<\/a><\/li><li><a href=\"#4a12062a-f453-4623-aeb4-cf9a26eeb181\">Use regionfill to Replace NaNs - Solution 1<\/a><\/li><li><a href=\"#89ba4c8a-9ef3-4828-a207-0347db591273\">Replace NaNs with Local Average - Solution 2<\/a><\/li><li><a href=\"#d5880e24-aeb1-4639-bbb4-1acdb69f05f9\">Hmmm<\/a><\/li><li><a href=\"#93135026-b338-4003-8d79-056b2f849e92\">Use Region Labeling to Fill - Solution 3<\/a><\/li><li><a href=\"#072daf02-6b2d-427f-95c5-b6c8d7b80d73\">Others?<\/a><\/li><\/ul><\/div><h4>Create data<a name=\"24570e80-3b19-4e89-83d6-8c201691513f\"><\/a><\/h4><p>Let's create an image and artificially create some holes with NaNs.  It's a image of type <tt>uint8<\/tt>, and can't represent NaN values so we are going to convert to floating point.<\/p><pre class=\"codeinput\">img = imread(<span class=\"string\">'rice.png'<\/span>);\r\nwhos <span class=\"string\">img<\/span>\r\n<\/pre><pre class=\"codeoutput\">  Name        Size             Bytes  Class    Attributes\r\n\r\n  img       256x256            65536  uint8              \r\n\r\n<\/pre><pre class=\"codeinput\">thisIsWhatHappens = uint8(NaN)\r\n<\/pre><pre class=\"codeoutput\">thisIsWhatHappens =\r\n  uint8\r\n   0\r\n<\/pre><p>Convert image from uint8 to single.<\/p><pre class=\"codeinput\">singleImg = im2single(img);\r\ntlo = tiledlayout(2,2, <span class=\"string\">'TileSpacing'<\/span>, <span class=\"string\">'compact'<\/span>, <span class=\"string\">'Padding'<\/span>, <span class=\"string\">'none'<\/span>);\r\nnexttile(tlo);\r\nimshow(singleImg)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/2020\/nanfilling_01.png\" alt=\"\"> <p>Add some NaN values.  Single rice grains are smaller than 270 pixels, so we are choosing to introduce NaNs in areas larger than a typical grain. First we threshold the image to segment the grains of rice.  And we discard blobs smaller than 270 pixels. This gives us a mask of large or contiguous rice grains.  We replace these areas in the original image with NaNs.  You can see that in the 3rd image.<\/p><pre class=\"codeinput\">nanMask = imbinarize(singleImg,<span class=\"string\">\"adaptive\"<\/span>);\r\nnanMask = bwareaopen(nanMask, 270);\r\nnexttile(tlo)\r\nimshow(nanMask)\r\nsingleImgNaN = singleImg;\r\nsingleImgNaN(nanMask) = NaN;\r\nnexttile(tlo)\r\nimshow(singleImgNaN)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/2020\/nanfilling_02.png\" alt=\"\"> <p>Let's now consider several approaches for filling in these NaN regions.<\/p><h4>Use regionfill to Replace NaNs - Solution 1<a name=\"4a12062a-f453-4623-aeb4-cf9a26eeb181\"><\/a><\/h4><p><tt><a href=\"https:\/\/www.mathworks.com\/help\/images\/ref\/regionfill.html\">regionfill<\/a><\/tt> provides an the \"out-of-the-box,\" documented approach to region filling. It works well, and is fast. It fills regions in images using inward interpolation--in this case, replacing the NaNs with a smooth representation of local background values. If you're interested in the algorithm, visit the documentation page. This is your go-to algorithm for most region-filling needs.<\/p><pre class=\"codeinput\">imgrf = regionfill(img, nanMask);\r\nnexttile(tlo)\r\nimshow(imgrf)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/2020\/nanfilling_03.png\" alt=\"\"> <p>This works great, but is not necessarily what the customer asked for.<\/p><h4>Replace NaNs with Local Average - Solution 2<a name=\"89ba4c8a-9ef3-4828-a207-0347db591273\"><\/a><\/h4><p>Let's try something else next. Let's first define what we mean by \"neighboring pixels\".<\/p><p>If we have a single pixel, we can define its neighbors as \"4-connected,\" meaning the pixels North, East, South, and West are neighbors, or we can define neighbors as \"8-connected,\" in which case we include NE, SE, SW, and NW in addition.  We can represent these as offsets relative to a pixel position in the image.<\/p><p>One caution: we need to be careful to not step beyond the edges of the image, so we will pad the image with a 1-pixel border of zeros. (The functions in the &lt;https:\/\/www.mathworks.com\/products\/image.html Image Processing Toolbox &gt; take care of such things for you, when necessary.)<\/p><p>Add a single-pixel black frame around the perimeter of the image:<\/p><pre class=\"codeinput\">paddedImg = padarray(singleImgNaN, [1 1], 0, <span class=\"string\">'both'<\/span>);\r\n<\/pre><p>Create 4-connected and 8-connected offsets.<\/p><pre class=\"codeinput\">[m, n] = size(singleImg);\r\nneighbors4 = [-1, 1, m, -m];\r\nneighbors8 = [neighbors4, -m-1, -m+1, m-1, m+1];\r\n<\/pre><p>We don't necessarily want to change the output size of the image.  To accommodate this, we can treat the positions in the original image separately from the padded image.<\/p><p>Let's find NaNs first.<\/p><pre class=\"codeinput\">originalNaNPos = find(isnan(singleImgNaN));\r\npaddedImageNaNs = find(isnan(paddedImg));\r\n<\/pre><p>We're going to overwrite individual pixels in the output image so we are making a copy we can alter.<\/p><pre class=\"codeinput\">imglocav = singleImgNaN;\r\n<\/pre><p>We can create and use some anonymous functions to compute the means using the 2 different neighborhood definitions, ignoring NaNs.<\/p><pre class=\"codeinput\">f4 = @(ind) mean(paddedImg(ind + neighbors4),<span class=\"string\">\"omitnan\"<\/span>);\r\nf8 = @(ind) mean(paddedImg(ind + neighbors8),<span class=\"string\">\"omitnan\"<\/span>);\r\n<\/pre><p>Let's show what this looks like for 4-connected neighbors.  We find the positions of the NaNs in the padded image, and compute the neighbor means for each of these.  We then take this computed values and replace the corresponding location in the original image with these.<\/p><pre class=\"codeinput\"><span class=\"keyword\">for<\/span> ii = 1:numel(paddedImageNaNs)\r\n    imglocav(originalNaNPos(ii)) = f4(paddedImageNaNs(ii));\r\n<span class=\"keyword\">end<\/span>\r\nimshow(imglocav)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/2020\/nanfilling_04.png\" alt=\"\"> <h4>Hmmm<a name=\"d5880e24-aeb1-4639-bbb4-1acdb69f05f9\"><\/a><\/h4><p>Well, we see the first problem with this approach: any NaN pixel that is surrounded entirely by NaN neighbors--will be replaced with a NaN!<\/p><pre class=\"codeinput\">whyNaN = mean(nan(4, 1), <span class=\"string\">\"omitnan\"<\/span>)\r\n<\/pre><pre class=\"codeoutput\">whyNaN =\r\n   NaN\r\n<\/pre><p>So while we've shrunk the NaN-holes, we haven't entirely removed them. (Notice that when we zoom in and use <tt>impixelinfo<\/tt> we can see the values of the pixels we're talking about.)<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/2020\/zoomedScreenshot.png\" alt=\"\"> <\/p><p>So what to do?<\/p><p>Let's iterate! Now let's consider what happens if we iterate until there are no more NaNs.<\/p><p>Let's reset our image first.<\/p><pre class=\"codeinput\">imglocav = singleImgNaN;\r\n<\/pre><p>Now iterate<\/p><pre class=\"codeinput\"><span class=\"keyword\">while<\/span> nnz(isnan(imglocav)) &gt; 0\r\n    originalNaNPos = find(isnan(imglocav));\r\n    <span class=\"keyword\">for<\/span> ii = 1:numel(originalNaNPos)\r\n        imglocav(originalNaNPos(ii)) = f4(paddedImageNaNs(ii));\r\n    <span class=\"keyword\">end<\/span>\r\n<span class=\"keyword\">end<\/span>\r\nimshow(imglocav)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/2020\/nanfilling_05.png\" alt=\"\"> <p>We think this is what the customer was asking for, but now we see a potential second problem: the NaN pixels that were entirely surrounded by NaNs in their four- or eight-connectedness, have been replaced with zeros.<\/p><p>The computer did what we asked, but not necessarily what we wanted!<\/p><h4>Use Region Labeling to Fill - Solution 3<a name=\"93135026-b338-4003-8d79-056b2f849e92\"><\/a><\/h4><p>We are going to take advantage of the <tt>nanMask<\/tt> we computed earlier. We find the outlines, and label distinct connected areas. Next we loop over each area and fill each one with the mean of its perimeter values. No iteration is necessary, but we will end up with flat regions of uniform intensity.<\/p><pre class=\"codeinput\">edgeMask = edge(nanMask);\r\nbwl = bwlabel(edgeMask);\r\nbwl2 = bwlabel(nanMask);\r\nimX = singleImgNaN;\r\n\r\n<span class=\"keyword\">for<\/span> ii = 1:max(bwl(:))\r\n    thisEdgeMean = mean(imX(bwl == ii), <span class=\"string\">\"omitnan\"<\/span>);\r\n    imX(bwl2 == ii) = thisEdgeMean;\r\n<span class=\"keyword\">end<\/span>\r\nimshow(imX)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/2020\/nanfilling_06.png\" alt=\"\"> <h4>Others?<a name=\"072daf02-6b2d-427f-95c5-b6c8d7b80d73\"><\/a><\/h4><p>What other ways do you think about this problem?  We know there are a lot of different ways.  Let us know <a href=\"https:\/\/blogs.mathworks.com\/loren\/?p=3851#respond\">here<\/a>.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_7e89fee5853042ec8eb98855f3efc306() {\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='7e89fee5853042ec8eb98855f3efc306 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 7e89fee5853042ec8eb98855f3efc306';\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 2020 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_7e89fee5853042ec8eb98855f3efc306()\"><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; R2020b<br><\/p><\/div><!--\r\n7e89fee5853042ec8eb98855f3efc306 ##### SOURCE BEGIN #####\r\n%% Fill Image NaN Values with Local Average\r\n% Recently we had a customer ask how to fill in NaN values in an image with\r\n% a neighborhood local mean. My friend, colleague, and occasional blogger,\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/911 Brett\r\n% Shoelson>, joins me today to show you several viable techniques.\r\n\r\n%% Create data\r\n% Let's create an image and artificially create some holes with NaNs.  It's\r\n% a image of type |uint8|, and can't represent NaN values so we are going\r\n% to convert to floating point.\r\n\r\nimg = imread('rice.png'); \r\nwhos img\r\n%%\r\nthisIsWhatHappens = uint8(NaN)\r\n\r\n%%\r\n% Convert image from uint8 to single.\r\nsingleImg = im2single(img); \r\ntlo = tiledlayout(2,2, 'TileSpacing', 'compact', 'Padding', 'none');\r\nnexttile(tlo);\r\nimshow(singleImg)\r\n\r\n%%\r\n% Add some NaN values.  Single rice grains are smaller than 270 pixels, so\r\n% we are choosing to introduce NaNs in areas larger than a typical grain.\r\n% First we threshold the image to segment the grains of rice.  And we\r\n% discard blobs smaller than 270 pixels. This gives us a mask of large or\r\n% contiguous rice grains.  We replace these areas in the original image\r\n% with NaNs.  You can see that in the 3rd image.\r\nnanMask = imbinarize(singleImg,\"adaptive\");\r\nnanMask = bwareaopen(nanMask, 270);\r\nnexttile(tlo)\r\nimshow(nanMask)\r\nsingleImgNaN = singleImg;\r\nsingleImgNaN(nanMask) = NaN;\r\nnexttile(tlo)\r\nimshow(singleImgNaN)\r\n\r\n%%\r\n% Let's now consider several approaches for filling in these NaN\r\n% regions.\r\n\r\n%% Use regionfill to Replace NaNs - Solution 1\r\n% |<https:\/\/www.mathworks.com\/help\/images\/ref\/regionfill.html regionfill>|\r\n% provides an the \"out-of-the-box,\" documented approach to region filling.\r\n% It works well, and is fast. It fills regions in images using inward\r\n% interpolationREPLACE_WITH_DASH_DASHin this case, replacing the NaNs with a smooth\r\n% representation of local background values. If you're interested in the\r\n% algorithm, visit the documentation page. This is your go-to algorithm for\r\n% most region-filling needs.\r\nimgrf = regionfill(img, nanMask); \r\nnexttile(tlo)\r\nimshow(imgrf)\r\n\r\n%%\r\n% This works great, but is not necessarily what the customer asked for.\r\n\r\n%% Replace NaNs with Local Average - Solution 2\r\n% Let's try something else next. Let's first define what we mean by\r\n% \"neighboring pixels\".\r\n%\r\n% If we have a single pixel, we can define its neighbors as \"4-connected,\"\r\n% meaning the pixels North, East, South, and West are neighbors, or we can\r\n% define neighbors as \"8-connected,\" in which case we include NE, SE, SW,\r\n% and NW in addition.  We can represent these as offsets relative to a\r\n% pixel position in the image.\r\n%\r\n% One caution: we need to be careful to not step beyond the edges of the\r\n% image, so we will pad the image with a 1-pixel border of zeros. (The\r\n% functions in the <https:\/\/www.mathworks.com\/products\/image.html Image Processing Toolbox > take care of such things for\r\n% you, when necessary.)\r\n\r\n%% \r\n% Add a single-pixel black frame around the perimeter of the image:\r\npaddedImg = padarray(singleImgNaN, [1 1], 0, 'both');\r\n\r\n%%\r\n% Create 4-connected and 8-connected offsets.\r\n[m, n] = size(singleImg);\r\nneighbors4 = [-1, 1, m, -m];\r\nneighbors8 = [neighbors4, -m-1, -m+1, m-1, m+1];\r\n\r\n%%\r\n% We don't necessarily want to change the output size of the image.  To\r\n% accommodate this, we can treat the positions in the original image\r\n% separately from the padded image.\r\n\r\n%%  \r\n% Let's find NaNs first.\r\noriginalNaNPos = find(isnan(singleImgNaN));\r\npaddedImageNaNs = find(isnan(paddedImg));\r\n\r\n%%\r\n% We're going to overwrite individual pixels in the output image so we\r\n% are making a copy we can alter.\r\nimglocav = singleImgNaN; \r\n\r\n%%\r\n% We can create and use some anonymous functions to compute the means using\r\n% the 2 different neighborhood definitions, ignoring NaNs.\r\nf4 = @(ind) mean(paddedImg(ind + neighbors4),\"omitnan\");\r\nf8 = @(ind) mean(paddedImg(ind + neighbors8),\"omitnan\");\r\n\r\n%%\r\n% Let's show what this looks like for 4-connected neighbors.  We find the\r\n% positions of the NaNs in the padded image, and compute the neighbor means\r\n% for each of these.  We then take this computed values and replace the\r\n% corresponding location in the original image with these. \r\n\r\nfor ii = 1:numel(paddedImageNaNs)\r\n    imglocav(originalNaNPos(ii)) = f4(paddedImageNaNs(ii));\r\nend\r\nimshow(imglocav)\r\n\r\n%% Hmmm\r\n% Well, we see the first problem with this approach: any NaN pixel that is\r\n% surrounded entirely by NaN neighborsREPLACE_WITH_DASH_DASHwill be replaced with a NaN!\r\n\r\nwhyNaN = mean(nan(4, 1), \"omitnan\")\r\n\r\n%%\r\n% So while we've shrunk the NaN-holes, we haven't entirely removed them.\r\n% (Notice that when we zoom in and use |impixelinfo| we can see the values\r\n% of the pixels we're talking about.)\r\n\r\n%%\r\n% \r\n% <<zoomedScreenshot.png>>\r\n% \r\n\r\n%% \r\n% So what to do?\r\n\r\n%%\r\n% Let's iterate!\r\n% Now let's consider what happens if we iterate until there are no more\r\n% NaNs. \r\n%\r\n% Let's reset our image first.\r\nimglocav = singleImgNaN;\r\n\r\n%% \r\n% Now iterate\r\nwhile nnz(isnan(imglocav)) > 0\r\n    originalNaNPos = find(isnan(imglocav));\r\n    for ii = 1:numel(originalNaNPos)\r\n        imglocav(originalNaNPos(ii)) = f4(paddedImageNaNs(ii));\r\n    end\r\nend\r\nimshow(imglocav)\r\n\r\n%%\r\n% We think this is what the customer was asking for, but now we see a\r\n% potential second problem: the NaN pixels that were entirely surrounded by\r\n% NaNs in their four- or eight-connectedness, have been replaced with zeros. \r\n%\r\n% The computer did what we asked, but not necessarily what we wanted!\r\n\r\n%% Use Region Labeling to Fill - Solution 3\r\n% We are going to take advantage of the |nanMask| we computed earlier. We\r\n% find the outlines, and label distinct connected areas. Next we loop over\r\n% each area and fill each one with the mean of its perimeter values. No\r\n% iteration is necessary, but we will end up with flat regions of uniform\r\n% intensity.\r\n\r\nedgeMask = edge(nanMask);\r\nbwl = bwlabel(edgeMask);\r\nbwl2 = bwlabel(nanMask);\r\nimX = singleImgNaN;\r\n\r\nfor ii = 1:max(bwl(:))\r\n    thisEdgeMean = mean(imX(bwl == ii), \"omitnan\");\r\n    imX(bwl2 == ii) = thisEdgeMean;\r\nend\r\nimshow(imX)\r\n\r\n%% Others?\r\n% What other ways do you think about this problem?  We know there are a lot\r\n% of different ways.  Let us know\r\n% <https:\/\/blogs.mathworks.com\/loren\/?p=3851#respond here>.\r\n\r\n\r\n\r\n##### SOURCE END ##### 7e89fee5853042ec8eb98855f3efc306\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img decoding=\"async\"  class=\"img-responsive\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/2020\/nanfilling_06.png\" onError=\"this.style.display ='none';\" \/><\/div><!--introduction--><p>Recently we had a customer ask how to fill in NaN values in an image with a neighborhood local mean. My friend, colleague, and occasional blogger, <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/911\">Brett Shoelson<\/a>, joins me today to show you several viable techniques.... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/loren\/2020\/10\/20\/fill-image-nan-values-with-local-average\/\">read more >><\/a><\/p>","protected":false},"author":39,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[27],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/3851"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/users\/39"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/comments?post=3851"}],"version-history":[{"count":2,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/3851\/revisions"}],"predecessor-version":[{"id":3855,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/3851\/revisions\/3855"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/media?parent=3851"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/categories?post=3851"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/tags?post=3851"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}