{"id":931,"date":"2013-12-31T07:00:01","date_gmt":"2013-12-31T12:00:01","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=931"},"modified":"2021-02-11T06:41:26","modified_gmt":"2021-02-11T11:41:26","slug":"automating-data-extraction-1","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2013\/12\/31\/automating-data-extraction-1\/","title":{"rendered":"Automating the extraction of real data from an image of the data &#8211; part 1"},"content":{"rendered":"<div class=\"content\"><!--introduction--><p><i>I'd like to welcome back my fellow MATLAB Central blogger Brett Shoelson for the first in a three-part series on extracting curve values from a plot. You can find Brett over at the <a href=\"https:\/\/blogs.mathworks.com\/pick\/\">File Exchange Pick of the Week blog<\/a>, or you can check out his many <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/911\">File Exchange contributions<\/a>. -Steve<\/i><\/p><p>In my role as an application engineer, I frequently have the opportunity to help customers with their image processing-related problems. In fact, this is one of the aspects of my job that I enjoy the most; each customer's image poses unique--and sometimes difficult--challenges. I get to help solve them, and move on to other things.<\/p><p>Recently, a customer shared with me an image of a chart showing, among other things, the efficiency of a pump as a function of flow rate. He asked if I could help him automate the extraction of real data from this chart; he has to do this over and over, and is currently following a very manual workflow--clicking along the desired curve and recording the positions of his mouseclicks. If you're ever faced with a similar challenge and you can find a way to get the <i>original<\/i> data from which the chart was created, that's not only significantly easier, but less noisy than extracting the data from an image. But in this case, the original efficiency-versus-flow data were not available, and the customer had no alternative.<\/p><!--\/introduction--><h3>Contents<\/h3><div><ul><li><a href=\"#0ae6fa85-3853-4ec7-bbeb-57d13ebe9a37\">Here's the original image as the customer shared it<\/a><\/li><li><a href=\"#b18a7db7-bc64-4749-9ba9-5495b5234e83\">The consistent features<\/a><\/li><li><a href=\"#48348d73-9703-4c7b-ad7a-74d876c854e5\">Take a moment to think about the problem<\/a><\/li><li><a href=\"#72f50488-223d-4893-9302-181affd3fefc\">My solution, in three parts<\/a><\/li><li><a href=\"#68f32fa6-b8a5-4930-8ae0-589268803ef2\">Boosting the \"SNR\"<\/a><\/li><li><a href=\"#69ba4045-f293-4cf1-b430-1bbaef1ecce9\">Masking the original color image<\/a><\/li><li><a href=\"#f73da576-f9d0-4267-91be-2c44a043a829\">Done...for now.<\/a><\/li><li><a href=\"#2383cf6a-47c0-4993-b782-4ac5cf832d63\">The complete series<\/a><\/li><\/ul><\/div><h4>Here's the original image as the customer shared it<a name=\"0ae6fa85-3853-4ec7-bbeb-57d13ebe9a37\"><\/a><\/h4><pre class=\"codeinput\">url = <span class=\"string\">'https:\/\/blogs.mathworks.com\/images\/steve\/2013\/example-pump-perf-curve.jpg'<\/span>;\r\nchart = imread(url);\r\nimshow(chart)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/extractEfficiencyWriteup1_01.png\" alt=\"\"> <h4>The consistent features<a name=\"b18a7db7-bc64-4749-9ba9-5495b5234e83\"><\/a><\/h4><p>There are three axes in this image. In the top-most of the axes (which is also the largest), there are curves representing both \"Head\" (in m) and \"Efficiency\" (in percent) versus flow rate (m^3\/h). (The efficiency is labeled on the right side of the top axis.) The customer was interested <i>only<\/i> in the data reflected by the \"efficiency\" curve, as labeled in the image.<\/p><p>What information can we rely on from chart to chart? After asking a few questions, I learned that the efficiency curve always starts in the lower left corner of the top axis. Moreover, the axes and curves in this graph are similar--in form, shape, and color--to the corresponding curves in the many other graphs from which he needs to extract these data. (Otherwise, <i>automating<\/i> this data extraction would be impossible, and we'd have to try for a more \"semi-automated\" approach to facilitate manual analyses.)<\/p><h4>Take a moment to think about the problem<a name=\"48348d73-9703-4c7b-ad7a-74d876c854e5\"><\/a><\/h4><p>I like to make the point that solutions to image processing problems are typically non-unique. Your own approach may be different--perhaps radically so--from my own. In fact, I often reflect on problems that I've already solved, and realize that I might solve it differently if I were to face it again.<\/p><p>Before you read my solution, take a moment to look at the customer's image and think about how <i>you<\/i> would approach the problem. When I'm done sharing my workflow, I will encourage you to comment on it, or even to improve upon it. That way, we can learn from each other!<\/p><h4>My solution, in three parts<a name=\"72f50488-223d-4893-9302-181affd3fefc\"><\/a><\/h4><p>I'm going to step through the steps I took to automate this data-extraction process. This was somewhat tricky, so to keep this blog post from being too long, I'm going to break it up into three parts:<\/p><div><ul><li>In the first part, I'll isolate the axes that contains the efficiency curve, and then begin to isolate the two <b>blue<\/b> curves within that axes.<\/li><li>In the second post, I'll segment the remaining curves, and process them to isolate only the curve of interest.<\/li><li>Finally, in post three, I'll extract the x-y- coordinates of the efficiency curve, and create a custom \"fit object\" that will allow me to automate the determination of efficiency versus flow rate. Along the way, I'll share my thoughts about this process.<\/li><\/ul><\/div><h4>Boosting the \"SNR\"<a name=\"68f32fa6-b8a5-4930-8ae0-589268803ef2\"><\/a><\/h4><p>The information we're after (the \"signal\") is represented graphically in the \"Efficiency\" curve in the top axes. The rest of the curves are just \"noise\" here, and our initial task is to ascertain how to discard all of those noise sources, while keeping the signal intact.<\/p><p>Recognize that each axis in the original image is delineated by a black border (though that's difficult to see here, with the screen capture). With little effort, we can convert those black borders to white; that will allow us to create masks of each axis, and to select (or discard) specific ones.<\/p><pre class=\"codeinput\">imshow(chart)\r\ntitle(<span class=\"string\">'Original RGB'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/extractEfficiencyWriteup1_02.png\" alt=\"\"> <p>Let's create a reversed, binary version of the original; this will cast the black borders to white:<\/p><pre class=\"codeinput\">bw = im2bw(imcomplement(rgb2gray(chart))); <span class=\"comment\">% I combined some steps here<\/span>\r\nimshow(bw)\r\ntitle(<span class=\"string\">'Grayscaled\/Reversed\/Binarized'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/extractEfficiencyWriteup1_03.png\" alt=\"\"> <p>Now that the axes are bordered in white, we can fill the image regions to create solid regions of interest:<\/p><pre class=\"codeinput\">filled = imfill(bw,<span class=\"string\">'holes'<\/span>);\r\nimshow(filled)\r\ntitle(<span class=\"string\">'All Regions Filled'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/extractEfficiencyWriteup1_04.png\" alt=\"\"> <p>Since I know that the axis I want to keep is the largest one. I can use that information to create a mask that will be useful for discarding a lot of the noise in the original image:<\/p><pre class=\"codeinput\">cc = bwconncomp(filled);\r\nstats = regionprops(cc,<span class=\"string\">'Area'<\/span>);\r\nA = [stats.Area];\r\n[~,biggest] = max(A);\r\nfilled(labelmatrix(cc)~=biggest) = 0;\r\nimshow(filled);\r\ntitle(<span class=\"string\">'Axis mask'<\/span>)\r\nbb = regionprops(filled,<span class=\"string\">'BoundingBox'<\/span>); <span class=\"comment\">%We'll use this later!<\/span>\r\nbb = bb.BoundingBox;\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/extractEfficiencyWriteup1_05.png\" alt=\"\"> <p>(Note that the selection of the region [axes] of interest [axes 4, bottom right] was achieved simply by applying a logical constraint, and that I could easily have elected to use any combination of logical conditions to select the desired axes. For instance, I could have calculated both areas and centroids of the \"blobs\" in the \"All-Regions-Filled\" black and white image. Then I could have picked the region that is larger than a threshold area value, and that has a <i>y<\/i>-centroid above those of the other axes.)<\/p><h4>Masking the original color image<a name=\"69ba4045-f293-4cf1-b430-1bbaef1ecce9\"><\/a><\/h4><p>Now we can use this mask to pare the original color image, plane-by-plane:<\/p><pre class=\"codeinput\">chart(~repmat(filled,[1 1 3])) = 0;\r\nimshow(chart)\r\ntitle(<span class=\"string\">'Masked RGB Original'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/extractEfficiencyWriteup1_06.png\" alt=\"\"> <p>We've now discarded a lot of the noise, but the difficult part remains. Unfortunately, several non-interest curves intersect the curve we need. Even more unfortunately, one of those curves is the same color as the curve of interest! But perhaps we can continue along the path of noise reduction by exploring and using the color information in the image; it's possible that something in a grayscale representation of the image (<tt>rgb2gray<\/tt>, or individual red, green, or blue colorplanes) could be useful here. To explore that possibility, I like to use <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/19706-explorergb\">ExploreRGB<\/a>.<\/p><pre>ExploreRGB(chart)<\/pre><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/pick\/files\/ExploreRGBChart.png\" alt=\"\"> <\/p><p>Nothing that's immediately obvious jumps out at me that will make the selection of the efficiency curve any easier. But let's click on the \"Advanced-Mode\" toolbar button (upper left) to explore different colorspace representations:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/pick\/files\/ExploreRGBChart3.png\" alt=\"\"> <\/p><p>Now, by studying the \"blue chrominance image\" (in the 4th row, 2nd column), we can see that the blue curves can readily differentiated from non-blue curves simply by changing colorspaces.<\/p><p>(Recognize that I <i>could<\/i> simply click on the blue-chrominance image, and then right-click to export that image to the base workspace, but I'll show the equivalent code here:) Convert to YCbCr, extract blue chrominance image:<\/p><pre class=\"codeinput\">curveOfInterest = rgb2ycbcr(chart);\r\ncurveOfInterest = curveOfInterest(:,:,2);\r\nimshow(curveOfInterest);\r\ntitle(<span class=\"string\">'Cb\/masked'<\/span>);\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/extractEfficiencyWriteup1_07.png\" alt=\"\"> <p>By the way, I didn't <i>have<\/i> to create handles to the subplot axes (ax(1)--ax(4), above). But doing so allows me to use <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/18291-expandaxes-hndls-rotenable-\">expandAxes<\/a>, which conveniently lets me click on any of those axes to expand it to full screen, and then to right-click to export the image it contains to my MATLAB workspace.<\/p><h4>Done...for now.<a name=\"f73da576-f9d0-4267-91be-2c44a043a829\"><\/a><\/h4><p>Let's stop there. Next week, we'll resume the problem and isolate only the efficiency curve. (That, it turns out, is the most difficult part of this problem!)<\/p><h4>The complete series<a name=\"2383cf6a-47c0-4993-b782-4ac5cf832d63\"><\/a><\/h4><div><ul><li><a href=\"https:\/\/blogs.mathworks.com\/steve\/2013\/12\/31\/automating-data-extraction-1\/\">Part 1<\/a><\/li><li><a href=\"https:\/\/blogs.mathworks.com\/steve\/2014\/01\/07\/automating-data-extraction-2\/\">Part 2<\/a><\/li><li><a href=\"https:\/\/blogs.mathworks.com\/steve\/2014\/01\/14\/automating-data-extraction-3\/\">Part 3<\/a><\/li><\/ul><\/div><script language=\"JavaScript\"> <!-- \r\n    function grabCode_27ca62f2167440ce9eafe4ab2ff9eab1() {\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='27ca62f2167440ce9eafe4ab2ff9eab1 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 27ca62f2167440ce9eafe4ab2ff9eab1';\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 2013 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_27ca62f2167440ce9eafe4ab2ff9eab1()\"><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; R2013b<br><\/p><p class=\"footer\"><br>\r\n      Published with MATLAB&reg; R2013b<br><\/p><\/div><!--\r\n27ca62f2167440ce9eafe4ab2ff9eab1 ##### SOURCE BEGIN #####\r\n%% Automating the extraction of real data from an image of the data\r\n%\r\n% _I'd like to welcome back my fellow MATLAB Central blogger Brett Shoelson\r\n% for the first in a three-part series on extracting curve values from a\r\n% plot. You can find Brett over at the <https:\/\/blogs.mathworks.com\/pick\/ \r\n% File Exchange Pick of the Week blog>, or you \r\n% can check out his many <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/911 \r\n% File Exchange contributions>. -Steve_\r\n%\r\n% In my role as an application engineer, I frequently have the opportunity\r\n% to help customers with their image processing-related problems. In fact,\r\n% this is one of the aspects of my job that I enjoy the most; each\r\n% customer's image poses uniqueREPLACE_WITH_DASH_DASHand sometimes difficultREPLACE_WITH_DASH_DASHchallenges. I get\r\n% to help solve them, and move on to other things.\r\n%\r\n%\r\n% Recently, a customer shared with me an image of a chart showing, among\r\n% other things, the efficiency of a pump as a function of flow rate. He\r\n% asked if I could help him automate the extraction of real data from this\r\n% chart; he has to do this over and over, and is currently following a very\r\n% manual workflowREPLACE_WITH_DASH_DASHclicking along the desired curve and recording the\r\n% positions of his mouseclicks. If you're ever faced with a similar\r\n% challenge and you can find a way to get the _original_ data from which\r\n% the chart was created, that's not only significantly easier, but less\r\n% noisy than extracting the data from an image. But in this case, the\r\n% original efficiency-versus-flow data were not available, and the customer\r\n% had no alternative.\r\n\r\n%% Here's the original image as the customer shared it\r\nurl = 'https:\/\/blogs.mathworks.com\/images\/steve\/2013\/example-pump-perf-curve.jpg';\r\nchart = imread(url);\r\nimshow(chart)\r\n\r\n%% The consistent features\r\n% There are three axes in this image. In the top-most of the axes (which is\r\n% also the largest), there are curves representing both \"Head\" (in m) and\r\n% \"Efficiency\" (in percent) versus flow rate (m^3\/h). (The efficiency is\r\n% labeled on the right side of the top axis.) The customer was interested\r\n% _only_ in the data reflected by the \"efficiency\" curve, as labeled in the\r\n% image. \r\n\r\n%% \r\n% What information can we rely on from chart to chart? After asking a few\r\n% questions, I learned that the efficiency curve always starts in the lower\r\n% left corner of the top axis. Moreover, the axes and curves in this graph\r\n% are similarREPLACE_WITH_DASH_DASHin form, shape, and colorREPLACE_WITH_DASH_DASHto the corresponding curves in\r\n% the many other graphs from which he needs to extract these data.\r\n% (Otherwise, _automating_ this data extraction would be impossible, and\r\n% we'd have to try for a more \"semi-automated\" approach to facilitate\r\n% manual analyses.)\r\n\r\n%% Take a moment to think about the problem\r\n% I like to make the point that solutions to image processing problems are\r\n% typically non-unique. Your own approach may be differentREPLACE_WITH_DASH_DASHperhaps\r\n% radically soREPLACE_WITH_DASH_DASHfrom my own. In fact, I often reflect on problems that I've\r\n% already solved, and realize that I might solve it differently if I were\r\n% to face it again.\r\n%\r\n% Before you read my solution, take a moment to look at the customer's\r\n% image and think about how _you_ would approach the problem. When I'm done\r\n% sharing my workflow, I will encourage you to comment on it, or even to\r\n% improve upon it. That way, we can learn from each other!\r\n\r\n%% My solution, in three parts\r\n% I'm going to step through the steps I took to automate this\r\n% data-extraction process. This was somewhat tricky, so to keep this blog\r\n% post from being too long, I'm going to break it up into three parts:\r\n% \r\n% * In the first part, I'll isolate the axes that contains the efficiency curve,\r\n% and then begin to isolate the two *blue* curves within that axes.\r\n% * In the second post, I'll segment the remaining curves, and process them\r\n% to isolate only the curve of interest.\r\n% * Finally, in post three, I'll extract the x-y- coordinates of the\r\n% efficiency curve, and create a custom \"fit object\" that will allow me to\r\n% automate the determination of efficiency versus flow rate. Along the way,\r\n% I'll share my thoughts about this process.\r\n\r\n%% Boosting the \"SNR\"\r\n% The information we're after (the \"signal\") is represented graphically in\r\n% the \"Efficiency\" curve in the top axes. The rest of the curves are just\r\n% \"noise\" here, and our initial task is to ascertain how to discard all of\r\n% those noise sources, while keeping the signal intact.\r\n%\r\n% Recognize that each axis in the original image is delineated by a black\r\n% border (though that's difficult to see here, with the screen capture).\r\n% With little effort, we can convert those black borders to white; that\r\n% will allow us to create masks of each axis, and to select (or discard)\r\n% specific ones.\r\n\r\nimshow(chart)\r\ntitle('Original RGB')\r\n\r\n%%\r\n% Let's create a reversed, binary version of the original; this will cast\r\n% the black borders to white:\r\nbw = im2bw(imcomplement(rgb2gray(chart))); % I combined some steps here\r\nimshow(bw)\r\ntitle('Grayscaled\/Reversed\/Binarized')\r\n\r\n%%\r\n% Now that the axes are bordered in white, we can fill the image regions to\r\n% create solid regions of interest:\r\nfilled = imfill(bw,'holes');\r\nimshow(filled)\r\ntitle('All Regions Filled')\r\n\r\n%%\r\n% Since I know that the axis I want to keep is the largest one. I can use\r\n% that information to create a mask that will be useful for discarding a\r\n% lot of the noise in the original image:\r\ncc = bwconncomp(filled);\r\nstats = regionprops(cc,'Area');\r\nA = [stats.Area];\r\n[~,biggest] = max(A);\r\nfilled(labelmatrix(cc)~=biggest) = 0;\r\nimshow(filled);\r\ntitle('Axis mask')\r\nbb = regionprops(filled,'BoundingBox'); %We'll use this later!\r\nbb = bb.BoundingBox;\r\n\r\n%%\r\n% (Note that the selection of the region [axes] of interest [axes 4, bottom\r\n% right] was achieved simply by applying a logical constraint, and that I\r\n% could easily have elected to use any combination of logical conditions to\r\n% select the desired axes. For instance, I could have calculated both areas\r\n% and centroids of the \"blobs\" in the \"All-Regions-Filled\" black and white\r\n% image. Then I could have picked the region that is larger than a\r\n% threshold area value, and that has a _y_-centroid above those of the other\r\n% axes.)\r\n\r\n%% Masking the original color image\r\n% Now we can use this mask to pare the original color image, plane-by-plane:\r\nchart(~repmat(filled,[1 1 3])) = 0;\r\nimshow(chart)\r\ntitle('Masked RGB Original')\r\n\r\n%% \r\n% We've now discarded a lot of the noise, but the difficult part remains.\r\n% Unfortunately, several non-interest curves intersect the curve we need.\r\n% Even more unfortunately, one of those curves is the same color as the\r\n% curve of interest! But perhaps we can continue along the path of noise\r\n% reduction by exploring and using the color information in the image; it's\r\n% possible that something in a grayscale representation of the image\r\n% (|rgb2gray|, or individual red, green, or blue colorplanes) could be\r\n% useful here. To explore that possibility, I like to use \r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/19706-explorergb ExploreRGB>.\r\n%\r\n%  ExploreRGB(chart)\r\n%%\r\n% \r\n% <<https:\/\/blogs.mathworks.com\/pick\/files\/ExploreRGBChart.png>>\r\n% \r\n\r\n%% \r\n% Nothing that's immediately obvious jumps out at me that will make the\r\n% selection of the efficiency curve any easier. But let's click on the\r\n% \"Advanced-Mode\" toolbar button (upper left) to explore different\r\n% colorspace representations:\r\n\r\n%%\r\n% \r\n% <<https:\/\/blogs.mathworks.com\/pick\/files\/ExploreRGBChart3.png>>\r\n% \r\n%%\r\n% Now, by studying the \"blue chrominance image\" (in the 4th row, 2nd\r\n% column), we can see that the blue curves can readily differentiated from\r\n% non-blue curves simply by changing colorspaces.\r\n\r\n%% \r\n% (Recognize that I _could_ simply click on the blue-chrominance image, and\r\n% then right-click to export that image to the base workspace, but I'll\r\n% show the equivalent code here:)\r\n% Convert to YCbCr, extract blue chrominance image:\r\ncurveOfInterest = rgb2ycbcr(chart);\r\ncurveOfInterest = curveOfInterest(:,:,2);\r\nimshow(curveOfInterest);\r\ntitle('Cb\/masked');\r\n\r\n%%\r\n% By the way, I didn't _have_ to create handles to the subplot axes\r\n% (ax(1)REPLACE_WITH_DASH_DASHax(4), above). But doing so allows me to use \r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/18291-expandaxes-hndls-rotenable- expandAxes>,\r\n% which conveniently lets me click on any of those axes to expand it to\r\n% full screen, and then to right-click to export the image it contains to\r\n% my MATLAB workspace.\r\n\r\n%% Done...for now.\r\n% Let's stop there. Next week, we'll resume the problem and isolate only\r\n% the efficiency curve. (That, it turns out, is the most difficult part of\r\n% this problem!)\r\n\r\n%% The complete series\r\n%\r\n% * <https:\/\/blogs.mathworks.com\/steve\/2013\/12\/13\/automating-data-extraction-1\/ Part 1> \r\n% * <https:\/\/blogs.mathworks.com\/steve\/2013\/12\/13\/automating-data-extraction-2\/ Part 2>\r\n% * <https:\/\/blogs.mathworks.com\/steve\/2013\/12\/13\/automating-data-extraction-3\/ Part 3>\r\n\r\n##### SOURCE END ##### 27ca62f2167440ce9eafe4ab2ff9eab1\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img decoding=\"async\"  class=\"img-responsive\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2013\/extractEfficiencyWriteup1_07.png\" onError=\"this.style.display ='none';\" \/><\/div><!--introduction--><p><i>I'd like to welcome back my fellow MATLAB Central blogger Brett Shoelson for the first in a three-part series on extracting curve values from a plot. You can find Brett over at the <a href=\"https:\/\/blogs.mathworks.com\/pick\/\">File Exchange Pick of the Week blog<\/a>, or you can check out his many <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/911\">File Exchange contributions<\/a>. -Steve<\/i>... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2013\/12\/31\/automating-data-extraction-1\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[561,84,146,136,76,36,122,168,116,104,1055,52],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/931"}],"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=931"}],"version-history":[{"count":10,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/931\/revisions"}],"predecessor-version":[{"id":4390,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/931\/revisions\/4390"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=931"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=931"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=931"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}