{"id":390,"date":"2011-09-23T12:20:10","date_gmt":"2011-09-23T16:20:10","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/2011\/09\/23\/dealing-with-really-big-images-image-adapters\/"},"modified":"2019-10-29T16:54:18","modified_gmt":"2019-10-29T20:54:18","slug":"dealing-with-really-big-images-image-adapters","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2011\/09\/23\/dealing-with-really-big-images-image-adapters\/","title":{"rendered":"Dealing with &#8220;Really Big&#8221; Images: Image Adapters"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <introduction>\r\n      <p><i>I'd like to welcome back guest blogger Brendan Hannigan, for the third in a series of three posts on working with very large\r\n            images in MATLAB.<\/i><\/p>\r\n      <p>In the previous two blog posts, I've been discussing how to avoid Out of Memory errors while working with large images using\r\n         MATLAB and the Image Processing Toolbox.  I first showed how to view and explore arbitrarily large images by creating a reduced\r\n         resolution data set (R-Set) from an image file.  Next, I demonstrated how you can process large images files using a file-to-file\r\n         workflow, never loading the entire image into memory at once.\r\n      <\/p>\r\n   <\/introduction>\r\n   <h3>Contents<\/h3>\r\n   <div>\r\n      <ul>\r\n         <li><a href=\"#1\">\"Right, but my data is not in TIFF, NITF, or JPEG2000, remember?\"<\/a><\/li>\r\n         <li><a href=\"#5\">\"Whoa there buddy!.. Object - \"Oriented\" ?!  That's too complicated for me!\"<\/a><\/li>\r\n         <li><a href=\"#8\">\"Ok so, how did Doug solve his problem?\"<\/a><\/li>\r\n         <li><a href=\"#12\">\"Ok, that's some pretty dense code you have there.\"<\/a><\/li>\r\n         <li><a href=\"#15\">\"How do you know what properties and methods you need?\"<\/a><\/li>\r\n         <li><a href=\"#16\">\"Hey wait, you don't define ImageSize in your properties block!!\"<\/a><\/li>\r\n         <li><a href=\"#17\">\"Well that's not super intuitive, but ok.  What goes inside the methods?\"<\/a><\/li>\r\n         <li><a href=\"#22\">\"How am I supposed to know which block it needs?\"<\/a><\/li>\r\n         <li><a href=\"#26\">\"Ok I think I got it, but what about writing to new files?\"<\/a><\/li>\r\n         <li><a href=\"#30\">\"Cool, everything turned out better than expected!\"<\/a><\/li>\r\n      <\/ul>\r\n   <\/div>\r\n   <h3>\"Right, but my data is not in TIFF, NITF, or JPEG2000, remember?\"<a name=\"1\"><\/a><\/h3>\r\n   <p>Yes, there's the problem.  <tt>rsetwrite<\/tt> &amp; <tt>blockproc<\/tt> support a few file formats \"natively\", but not everyone works with data in those formats.\r\n   <\/p>\r\n   <p>There are some issues we face when creating functions like <tt>rsetwrite<\/tt> &amp; <tt>blockproc<\/tt> which allow incremental processing of files from disc.  Here are two:\r\n   <\/p>\r\n   <div>\r\n      <ol>\r\n         <li>Not all file formats are amenable to incremental \"region-based\" I\/O.<\/li>\r\n         <li>There are a lot of file formats.  Seriously.<\/li>\r\n      <\/ol>\r\n   <\/div>\r\n   <p>That said, we wanted to provide this large data workflow to as many of out customers as possible.  So, as of release R2010a,\r\n      in addition to our \"built-in\" file formats, both <tt>rsetwrite<\/tt> and <tt>blockproc<\/tt> also support \"image adapter\" objects!\r\n   <\/p>\r\n   <p>The <tt>ImageAdapter<\/tt> is an object-oriented MATLAB class.  It is actually an \"abstract\" class, meaning that by itself it is not very useful.  What\r\n      it <b>does<\/b> do it define an interface for reading and writing image data.  All you have to do is to tell us how to read and\/or write\r\n      sub-regions of your particular file format, and then we can do the rest!\r\n   <\/p>\r\n   <h3>\"Whoa there buddy!.. Object - \"Oriented\" ?!  That's too complicated for me!\"<a name=\"5\"><\/a><\/h3>\r\n   <p>No, it's not.  Don't sweat it, it's really not.  I won't go into a full tutorial on how to write MATLAB classes in this blog\r\n      as there are excellent videos and tutorials available on our website and in our product documentation that cover that.  Instead,\r\n      I will walk through a quick example that recently came up in this very blog.\r\n   <\/p>\r\n   <p>In 2009 Steve published a blog post titled \"MATLAB R2009a - imread and multipage TIFFs\".  Over the last 2 years many folks\r\n      have commented on this post in an ongoing discussion about multi-page TIFF files.  One customer, we'll call him \"Doug\", <a href=\"https:\/\/blogs.mathworks.com\/steve\/2009\/04\/02\/matlab-r2009a-imread-and-multipage-tiffs\/#comment-24057\">had a problem using <tt>blockproc<\/tt> to process arbitrary pages of a multi-page TIFF file<\/a>.\r\n   <\/p>\r\n   <p>\"Doug\" was frustrated because he found that there was no way to tell <tt>blockproc<\/tt> that he wanted to process the Nth \"page\" in his TIFF file. <tt>blockproc<\/tt> is hard-wired to process the first page of a TIFF file when passed a multi-page TIFF image.  We didn't provide a syntactic\r\n      option to select which page to process, because we wanted to avoid format-specific syntaxes\/parameters in <tt>blockproc<\/tt>.  Otherwise you can imagine the function interface could get pretty complex pretty fast.\r\n   <\/p>\r\n   <h3>\"Ok so, how did Doug solve his problem?\"<a name=\"8\"><\/a><\/h3>\r\n   <p>This was a perfect use case for the <tt>ImageAdapter<\/tt> class.  Image adapter objects are useful when you want to have more control over the I\/O in <tt>blockproc<\/tt> and <tt>rsetwrite<\/tt>.  You may want to just control some specific aspect of how your file is read\/written (like Doug) or you might want to read\/write\r\n      a completely new file format.\r\n   <\/p>\r\n   <p>I wrote Doug a quick image adapter class to solve his problem which I will share with you, but first let's look at how it\r\n      is used in a quick example.  The image adapter class is called <tt>PagedTiffAdapter<\/tt>.  We'll be using it to work with a multi-paged TIFF image, mri.tif (<a href=\"https:\/\/blogs.mathworks.com\/images\/steve\/2011\/mri.tif\">download link<\/a>).\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #228B22\">% Get some image information, we'll need this later.<\/span>\r\nfilename = <span style=\"color: #A020F0\">'mri.tif'<\/span>;\r\npage = 5;\r\ninfo = imfinfo(filename);\r\ncmap = info(page).Colormap;\r\n\r\n<span style=\"color: #228B22\">% Create our PagedTiffAdapter object!<\/span>\r\nmy_adapter = PagedTiffAdapter(filename,page);\r\n\r\n<span style=\"color: #228B22\">% Let's not \"do\" anything to the data, let's just read it and return it<\/span>\r\nno_op_fun = @(bs) bs.data;\r\n\r\n<span style=\"color: #228B22\">% Call blockproc using our image adapter object as the input source<\/span>\r\nsingle_page = blockproc(my_adapter,[100 100],no_op_fun);\r\n\r\n<span style=\"color: #228B22\">% Display our single page from this TIFF file<\/span>\r\nimshow(single_page,cmap)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2011\/blockproc_3_01.jpg\"> <p>Voila.  That's pretty simple right?  We've now used <tt>blockproc<\/tt> to read in the 5th page of our multi-page TIFF file, <tt>mri.tif<\/tt>.  Granted, this is not a particularly compelling use of block processing, but I'm just trying to show how you can use image\r\n      adapter objects in place of \"conventional\" input images.\r\n   <\/p>\r\n   <p>Let's have a look at the class now.<\/p><pre>classdef PagedTiffAdapter &lt; ImageAdapter\r\n    properties\r\n        Filename\r\n        Info\r\n        Page\r\n    end\r\n    methods\r\n        function obj = PagedTiffAdapter(filename, page)\r\n            obj.Filename = filename;\r\n            obj.Info = imfinfo(filename);\r\n            obj.Page = page;\r\n            obj.ImageSize = [obj.Info(page).Height obj.Info(page).Width];\r\n        end\r\n        function result = readRegion(obj, start, count)\r\n            result = imread(obj.Filename,'Index',obj.Page,...\r\n                'Info',obj.Info,'PixelRegion', ...\r\n                {[start(1), start(1) + count(1) - 1], ...\r\n                [start(2), start(2) + count(2) - 1]});\r\n        end\r\n        function result = close(obj) %#ok\r\n        end\r\n    end\r\nend<\/pre><h3>\"Ok, that's some pretty dense code you have there.\"<a name=\"12\"><\/a><\/h3>\r\n   <p>The class is quite straightforward.  It begins with a <tt>classdef<\/tt> line, which defines the name of the class and also indicates that the class inherits from our base-class, <tt>ImageAdapter<\/tt>, using the <tt>&lt;<\/tt> symbol.\r\n   <\/p>\r\n   <p>Next we see 2 sub-sections of our class definition, a <tt>properties<\/tt> block which holds important data that we will need over the lifespan of each object...\r\n   <\/p><pre>    properties\r\n        Filename\r\n        Info\r\n        Page\r\n    end<\/pre><p>...and a <tt>methods<\/tt> block which defines the behavior of the objects.\r\n   <\/p><pre>    methods\r\n        function obj = PagedTiffAdapter(filename, page)\r\n            obj.Filename = filename;\r\n            obj.Info = imfinfo(filename);\r\n            obj.Page = page;\r\n            obj.ImageSize = [obj.Info(page).Height obj.Info(page).Width];\r\n        end\r\n        function result = readRegion(obj, start, count)\r\n            result = imread(obj.Filename,'Index',obj.Page,...\r\n                'Info',obj.Info,'PixelRegion', ...\r\n                {[start(1), start(1) + count(1) - 1], ...\r\n                [start(2), start(2) + count(2) - 1]});\r\n        end\r\n        function result = close(obj) %#ok\r\n        end\r\n    end<\/pre><h3>\"How do you know what properties and methods you need?\"<a name=\"15\"><\/a><\/h3>\r\n   <p>Ahh, good question.  Classes which inherit from the <tt>ImageAdapter<\/tt> base class are REQUIRED to have:\r\n   <\/p>\r\n   <div>\r\n      <ol>\r\n         <li>A class constructor for initialization (all MATLAB classes require this)<\/li>\r\n         <li>a <tt>readRegion<\/tt> method (required by the <tt>ImageAdapter<\/tt> base class)\r\n         <\/li>\r\n         <li>a <tt>close<\/tt> method (required by the <tt>ImageAdapter<\/tt> base class)\r\n         <\/li>\r\n         <li>a <tt>ImageSize<\/tt> property (required by the <tt>ImageAdapter<\/tt> base class)\r\n         <\/li>\r\n      <\/ol>\r\n   <\/div>\r\n   <h3>\"Hey wait, you don't define ImageSize in your properties block!!\"<a name=\"16\"><\/a><\/h3>\r\n   <p>That is true.  The <tt>ImageSize<\/tt> property is defined in the base class, so you don't have to redefine it here, you just have to <i>set it<\/i>. That's why at the end of my class constructor, I make sure to set the <tt>ImageSize<\/tt> property to be the size of the page that I am interested in.\r\n   <\/p>\r\n   <h3>\"Well that's not super intuitive, but ok.  What goes inside the methods?\"<a name=\"17\"><\/a><\/h3>\r\n   <p>This class is going to be used to read data from a TIFF file, so in the class constructor all we do is gather information\r\n      about the file that we'll need later and store that information in the appropriate properties.\r\n   <\/p>\r\n   <p>Let's look the other methods individually.  First the <tt>close<\/tt> method.\r\n   <\/p><pre>        function result = close(obj) %#ok\r\n        end<\/pre><p>The <tt>close<\/tt> method, in this case does nothing.  The reason it does nothing is that we are doing our actual file I\/O using the <tt>imread<\/tt> function, which does not require us to open the file handle directly.  If we were writing an image adapter to read say, some\r\n      arbitrarily formatted binary image, then we would likely be opening our file handle in the constructor, storing it in a property,\r\n      and then in the <tt>close<\/tt> method we would close the file handle and do any other necessary clean up tasks. This example class is just very simple,\r\n      so we have no \"cleaning up\" to do when we are done, but if we did, we would put that code in our <tt>close<\/tt> method.  Regardless of what clean up code you need, you <i>must<\/i> have a close method.  The <tt>close<\/tt> method is called by <tt>blockproc<\/tt> and <tt>rsetwrite<\/tt> only once, after all file I\/O has completed.\r\n   <\/p>\r\n   <p>Now let's look closely at the <tt>readRegion<\/tt> method.\r\n   <\/p><pre>        function result = readRegion(obj, start, count)\r\n            result = imread(obj.Filename,'Index',obj.Page,...\r\n                'Info',obj.Info,'PixelRegion', ...\r\n                {[start(1), start(1) + count(1) - 1], ...\r\n                [start(2), start(2) + count(2) - 1]});\r\n        end<\/pre><p>This is the real work horse of the class.  You will probably never need to call this method (or any method other than the\r\n      constructor) yourself. Instead the image adapter <i>clients<\/i> will call these functions. <tt>blockproc<\/tt> is going to call this <tt>readRegion<\/tt> method when it wants to read a block of the input image.  It's your job to figure out which block it needs, read that data,\r\n      and send it back to it.\r\n   <\/p>\r\n   <h3>\"How am I supposed to know which block it needs?\"<a name=\"22\"><\/a><\/h3>\r\n   <p>It'll tell you!  It's all contained in the 2 input arguments to the <tt>readRegion<\/tt> method, <tt>start<\/tt> and <tt>count<\/tt>.  The <tt>start<\/tt> argument is a 2-element vector specifying the [row col] of the first pixel we need. The <tt>count<\/tt> argument is a 2-element vector specifying the size of the requested region in [rows cols].\r\n   <\/p>\r\n   <p>For example, let's say <tt>start<\/tt> was [5 9] and <tt>count<\/tt> was [2 3].  Your method should return the data in rows 5-6 and columns 9-11, just as if you had indexed a variable like this:\r\n   <\/p>\r\n   <p><tt>return_to_blockproc = myVariable(5:6,9:11);<\/tt><\/p>\r\n   <p>So your implementation of <tt>readRegion<\/tt> needs to take these 2 input arguments and then return the appropriate image data that they specify.\r\n   <\/p>\r\n   <p>What will that mean for you?  Well it depends on what your image adapter is designed for.  In this case we are simply reading\r\n      a specific page of a TIFF file, so I'm using <tt>start<\/tt> and <tt>count<\/tt> to construct a <tt>'PixelRegion'<\/tt> argument to the <tt>imread<\/tt> function that will fetch only the pixels I am interested in.  In your case, your <tt>readRegion<\/tt> might be pulling data from some binary formatted file, using a 3rd party mex-file to read a piece of data, or just whatever!\r\n       That's the beauty of the image adapters, you can get your data from whereever you want.\r\n   <\/p>\r\n   <h3>\"Ok I think I got it, but what about writing to new files?\"<a name=\"26\"><\/a><\/h3>\r\n   <p>You can use your class to write to files as well, by defining a <tt>writeRegion<\/tt> method.  The <tt>writeRegion<\/tt> method is (not surprisingly) almost the exact opposite of the <tt>readRegion<\/tt> method.  Instead of you returning data from a specified block, you will receive data as an input argument and then write\r\n      that data to our image at a specified block location.\r\n   <\/p>\r\n   <p>After you write a <tt>writeRegion<\/tt> method, you can then use objects of your class as <tt>'Destination'<\/tt> parameters to <tt>blockproc<\/tt>, allowing you to both read and write to arbitrarily large files of arbitrary format!\r\n   <\/p>\r\n   <p>Specifying a <tt>writeRegion<\/tt> method is completely optional, but you <i>must<\/i> specify it if you want to use your image adapter object as a <tt>'Destination'<\/tt> in <tt>blockproc<\/tt>.  Otherwise, your objects will be \"read-only\".\r\n   <\/p>\r\n   <p>In the spirit of brevity, I'm not going to do that for our <tt>PagedTiffAdapter<\/tt>.  I've found that <tt>writeRegion<\/tt> methods are often more complex than their <tt>readRegion<\/tt> counterparts.  I'll leave that as an exercise for the reader.\r\n   <\/p>\r\n   <h3>\"Cool, everything turned out better than expected!\"<a name=\"30\"><\/a><\/h3>\r\n   <p>Thanks!  Well that about wraps it up.  If you want to learn more about writing and using image adapter objects there is a\r\n      chapter in the Image Processing Toolbox Users' Guide called \"Working with Data in Unsupported Formats\".  There we walk thought\r\n      writing an adapter for a more complex binary formatted file.  You can also see the documentation for <tt>blockproc<\/tt>, <tt>rsetwrite<\/tt>, and <tt>ImageAdapter<\/tt>.\r\n   <\/p>\r\n   <p>Thanks again Steve for letting me talk about this stuff!  Have a great day!<\/p><script language=\"JavaScript\">\r\n<!--\r\n\r\n    function grabCode_d6030c5f4a994a2e9973d4b455b409da() {\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='d6030c5f4a994a2e9973d4b455b409da ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' d6030c5f4a994a2e9973d4b455b409da';\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        author = 'Steve Eddins';\r\n        copyright = 'Copyright 2011 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 author and copyright lines at the bottom if specified.\r\n        if ((author.length > 0) || (copyright.length > 0)) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (author.length > 0) {\r\n                d.writeln('% _' + author + '_');\r\n            }\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      \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_d6030c5f4a994a2e9973d4b455b409da()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n            the MATLAB code \r\n            <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; 7.13<br><\/p>\r\n<\/div>\r\n<!--\r\nd6030c5f4a994a2e9973d4b455b409da ##### SOURCE BEGIN #####\r\n%% \r\n% _I'd like to welcome back guest blogger Brendan Hannigan, for the third\r\n% in a series of three posts on working with very large images in MATLAB._\r\n%\r\n% In the previous two blog posts, I've been discussing how to avoid Out of\r\n% Memory errors while working with large images using MATLAB and the Image\r\n% Processing Toolbox.  I first showed how to view and explore arbitrarily\r\n% large images by creating a reduced resolution data set (R-Set) from an\r\n% image file.  Next, I demonstrated how you can process large images files\r\n% using a file-to-file workflow, never loading the entire image into memory\r\n% at once.\r\n\r\n\r\n%% \"Right, but my data is not in TIFF, NITF, or JPEG2000, remember?\"\r\n% Yes, there's the problem.  |rsetwrite| & |blockproc| support a few file\r\n% formats \"natively\", but not everyone works with data in those formats.\r\n\r\n%%\r\n% There are some issues we face when creating functions like |rsetwrite| &\r\n% |blockproc| which allow incremental processing of files from disc.  Here\r\n% are two:\r\n%\r\n% # Not all file formats are amenable to incremental \"region-based\" I\/O.\r\n% # There are a lot of file formats.  Seriously.\r\n\r\n%%\r\n% That said, we wanted to provide this large data workflow to as many of\r\n% out customers as possible.  So, as of release R2010a, in addition to our\r\n% \"built-in\" file formats, both |rsetwrite| and |blockproc| also support\r\n% \"image adapter\" objects!\r\n\r\n%%\r\n% The |ImageAdapter| is an object-oriented MATLAB class.  It is actually an\r\n% \"abstract\" class, meaning that by itself it is not very useful.  What it\r\n% *does* do it define an interface for reading and writing image data.  All\r\n% you have to do is to tell us how to read and\/or write sub-regions of your\r\n% particular file format, and then we can do the rest!\r\n\r\n%% \"Whoa there buddy!.. Object - \"Oriented\" ?!  That's too complicated for me!\"\r\n% No, it's not.  Don't sweat it, it's really not.  I won't go into a full\r\n% tutorial on how to write MATLAB classes in this blog as there are\r\n% excellent videos and tutorials available on our website and in our\r\n% product documentation that cover that.  Instead, I will walk through a\r\n% quick example that recently came up in this very blog.\r\n\r\n%%\r\n% In 2009 Steve published a blog post titled \"MATLAB R2009a - imread and\r\n% multipage TIFFs\".  Over the last 2 years many folks have commented on\r\n% this post in an ongoing discussion about multi-page TIFF files.  One\r\n% customer, we'll call him \"Doug\",\r\n% <https:\/\/blogs.mathworks.com\/steve\/2009\/04\/02\/matlab-r2009a-imread-and-multipage-tiffs\/#comment-24057\r\n% had a problem using |blockproc| to process arbitrary pages of a\r\n% multi-page TIFF file>.\r\n\r\n%%\r\n% \"Doug\" was frustrated because he found that there was no way to tell\r\n% |blockproc| that he wanted to process the Nth \"page\" in his TIFF file.\r\n% |blockproc| is hard-wired to process the first page of a TIFF file when\r\n% passed a multi-page TIFF image.  We didn't provide a syntactic option to\r\n% select which page to process, because we wanted to avoid format-specific\r\n% syntaxes\/parameters in |blockproc|.  Otherwise you can imagine the\r\n% function interface could get pretty complex pretty fast.\r\n\r\n%% \"Ok so, how did Doug solve his problem?\"\r\n% This was a perfect use case for the |ImageAdapter| class.  Image adapter\r\n% objects are useful when you want to have more control over the I\/O in\r\n% |blockproc| and |rsetwrite|.  You may want to just control some specific\r\n% aspect of how your file is read\/written (like Doug) or you might want to\r\n% read\/write a completely new file format.\r\n\r\n%%\r\n% I wrote Doug a quick image adapter class to solve his problem which I\r\n% will share with you, but first let's look at how it is used in a quick\r\n% example.  The image adapter class is called |PagedTiffAdapter|.  We'll\r\n% be using it to work with a multi-paged TIFF image, mri.tif \r\n% (<https:\/\/blogs.mathworks.com\/images\/steve\/2011\/mri.tif download link>).\r\n\r\n% Get some image information, we'll need this later.\r\nfilename = 'mri.tif';\r\npage = 5;\r\ninfo = imfinfo(filename);\r\ncmap = info(page).Colormap;\r\n\r\n% Create our PagedTiffAdapter object!\r\nmy_adapter = PagedTiffAdapter(filename,page);\r\n\r\n% Let's not \"do\" anything to the data, let's just read it and return it\r\nno_op_fun = @(bs) bs.data;\r\n\r\n% Call blockproc using our image adapter object as the input source\r\nsingle_page = blockproc(my_adapter,[100 100],no_op_fun);\r\n\r\n% Display our single page from this TIFF file\r\nimshow(single_page,cmap)\r\n\r\n%%\r\n% Voila.  That's pretty simple right?  We've now used |blockproc| to read\r\n% in the 5th page of our multi-page TIFF file, |mri.tif|.  Granted, this is\r\n% not a particularly compelling use of block processing, but I'm just\r\n% trying to show how you can use image adapter objects in place of\r\n% \"conventional\" input images.\r\n\r\n%%\r\n% Let's have a look at the class now.\r\n%\r\n%  classdef PagedTiffAdapter < ImageAdapter\r\n%      properties\r\n%          Filename\r\n%          Info\r\n%          Page\r\n%      end\r\n%      methods\r\n%          function obj = PagedTiffAdapter(filename, page)\r\n%              obj.Filename = filename;\r\n%              obj.Info = imfinfo(filename);\r\n%              obj.Page = page;\r\n%              obj.ImageSize = [obj.Info(page).Height obj.Info(page).Width];\r\n%          end\r\n%          function result = readRegion(obj, start, count)\r\n%              result = imread(obj.Filename,'Index',obj.Page,...\r\n%                  'Info',obj.Info,'PixelRegion', ...\r\n%                  {[start(1), start(1) + count(1) - 1], ...\r\n%                  [start(2), start(2) + count(2) - 1]});\r\n%          end\r\n%          function result = close(obj) %#ok\r\n%          end\r\n%      end\r\n%  end\r\n\r\n%% \"Ok, that's some pretty dense code you have there.\"\r\n% The class is quite straightforward.  It begins with a |classdef| line,\r\n% which defines the name of the class and also indicates that the class\r\n% inherits from our base-class, |ImageAdapter|, using the |<| symbol.\r\n\r\n%%\r\n% Next we see 2 sub-sections of our class definition, a |properties| block\r\n% which holds important data that we will need over the lifespan of each\r\n% object...\r\n%\r\n%      properties\r\n%          Filename\r\n%          Info\r\n%          Page\r\n%      end\r\n\r\n%%\r\n% ...and a |methods| block which defines the behavior of the objects.\r\n%\r\n%      methods\r\n%          function obj = PagedTiffAdapter(filename, page)\r\n%              obj.Filename = filename;\r\n%              obj.Info = imfinfo(filename);\r\n%              obj.Page = page;\r\n%              obj.ImageSize = [obj.Info(page).Height obj.Info(page).Width];\r\n%          end\r\n%          function result = readRegion(obj, start, count)\r\n%              result = imread(obj.Filename,'Index',obj.Page,...\r\n%                  'Info',obj.Info,'PixelRegion', ...\r\n%                  {[start(1), start(1) + count(1) - 1], ...\r\n%                  [start(2), start(2) + count(2) - 1]});\r\n%          end\r\n%          function result = close(obj) %#ok\r\n%          end\r\n%      end\r\n\r\n%% \"How do you know what properties and methods you need?\"\r\n% Ahh, good question.  Classes which inherit from the |ImageAdapter| base\r\n% class are REQUIRED to have:\r\n%\r\n% # A class constructor for initialization (all MATLAB classes require this)\r\n% # a |readRegion| method (required by the |ImageAdapter| base class)\r\n% # a |close| method (required by the |ImageAdapter| base class)\r\n% # a |ImageSize| property (required by the |ImageAdapter| base class)\r\n\r\n%% \"Hey wait, you don't define ImageSize in your properties block!!\"\r\n% That is true.  The |ImageSize| property is defined in the base class, so\r\n% you don't have to redefine it here, you just have to _set it_. That's why\r\n% at the end of my class constructor, I make sure to set the |ImageSize|\r\n% property to be the size of the page that I am interested in.\r\n\r\n%% \"Well that's not super intuitive, but ok.  What goes inside the methods?\"\r\n% This class is going to be used to read data from a TIFF file, so in the\r\n% class constructor all we do is gather information about the file that\r\n% we'll need later and store that information in the appropriate\r\n% properties.\r\n\r\n%%\r\n% Let's look the other methods individually.  First the |close| method.\r\n%\r\n%          function result = close(obj) %#ok\r\n%          end\r\n\r\n%%\r\n% The |close| method, in this case does nothing.  The reason it does\r\n% nothing is that we are doing our actual file I\/O using the |imread|\r\n% function, which does not require us to open the file handle directly.  If\r\n% we were writing an image adapter to read say, some arbitrarily formatted\r\n% binary image, then we would likely be opening our file handle in the\r\n% constructor, storing it in a property, and then in the |close| method we\r\n% would close the file handle and do any other necessary clean up tasks.\r\n% This example class is just very simple, so we have no \"cleaning up\" to do\r\n% when we are done, but if we did, we would put that code in our |close|\r\n% method.  Regardless of what clean up code you need, you _must_ have a\r\n% close method.  The |close| method is called by |blockproc| and\r\n% |rsetwrite| only once, after all file I\/O has completed.\r\n\r\n%%\r\n% Now let's look closely at the |readRegion| method.\r\n%\r\n%          function result = readRegion(obj, start, count)\r\n%              result = imread(obj.Filename,'Index',obj.Page,...\r\n%                  'Info',obj.Info,'PixelRegion', ...\r\n%                  {[start(1), start(1) + count(1) - 1], ...\r\n%                  [start(2), start(2) + count(2) - 1]});\r\n%          end\r\n\r\n%%\r\n% This is the real work horse of the class.  You will probably never need\r\n% to call this method (or any method other than the constructor) yourself.\r\n% Instead the image adapter _clients_ will call these functions.\r\n% |blockproc| is going to call this |readRegion| method when it wants to\r\n% read a block of the input image.  It's your job to figure out which block\r\n% it needs, read that data, and send it back to it.\r\n\r\n%% \"How am I supposed to know which block it needs?\"\r\n% It'll tell you!  It's all contained in the 2 input arguments to the\r\n% |readRegion| method, |start| and |count|.  The |start| argument is a\r\n% 2-element vector specifying the [row col] of the first pixel we need. The\r\n% |count| argument is a 2-element vector specifying the size of the\r\n% requested region in [rows cols].\r\n\r\n%%\r\n% For example, let's say |start| was [5 9] and |count| was [2 3].  Your\r\n% method should return the data in rows 5-6 and columns 9-11, just as if\r\n% you had indexed a variable like this:\r\n%\r\n% |return_to_blockproc = myVariable(5:6,9:11);|\r\n\r\n%%\r\n% So your implementation of |readRegion| needs to take these 2 input\r\n% arguments and then return the appropriate image data that they specify.\r\n\r\n%%\r\n% What will that mean for you?  Well it depends on what your image adapter\r\n% is designed for.  In this case we are simply reading a specific page of a\r\n% TIFF file, so I'm using |start| and |count| to construct a\r\n% |'PixelRegion'| argument to the |imread| function that will fetch only\r\n% the pixels I am interested in.  In your case, your |readRegion| might be\r\n% pulling data from some binary formatted file, using a 3rd party mex-file\r\n% to read a piece of data, or just whatever!  That's the beauty of the\r\n% image adapters, you can get your data from whereever you want.\r\n\r\n%% \"Ok I think I got it, but what about writing to new files?\"\r\n% You can use your class to write to files as well, by defining a\r\n% |writeRegion| method.  The |writeRegion| method is (not surprisingly)\r\n% almost the exact opposite of the |readRegion| method.  Instead of you\r\n% returning data from a specified block, you will receive data as an input\r\n% argument and then write that data to our image at a specified block\r\n% location.\r\n\r\n%%\r\n% After you write a |writeRegion| method, you can then use objects of your\r\n% class as |'Destination'| parameters to |blockproc|, allowing you to both\r\n% read and write to arbitrarily large files of arbitrary format!\r\n\r\n%%\r\n% Specifying a |writeRegion| method is completely optional, but you _must_\r\n% specify it if you want to use your image adapter object as a\r\n% |'Destination'| in |blockproc|.  Otherwise, your objects will be\r\n% \"read-only\".\r\n\r\n%%\r\n% In the spirit of brevity, I'm not going to do that for our\r\n% |PagedTiffAdapter|.  I've found that |writeRegion| methods are often more\r\n% complex than their |readRegion| counterparts.  I'll leave that as an\r\n% exercise for the reader.\r\n\r\n%% \"Cool, everything turned out better than expected!\"\r\n% Thanks!  Well that about wraps it up.  If you want to learn more about\r\n% writing and using image adapter objects there is a chapter in the Image\r\n% Processing Toolbox Users' Guide called \"Working with Data in Unsupported\r\n% Formats\".  There we walk thought writing an adapter for a more complex\r\n% binary formatted file.  You can also see the documentation for\r\n% |blockproc|, |rsetwrite|, and |ImageAdapter|.\r\n\r\n%%\r\n% Thanks again Steve for letting me talk about this stuff!  Have a great\r\n% day!\r\n\r\n\r\n##### SOURCE END ##### d6030c5f4a994a2e9973d4b455b409da\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   \r\n      I'd like to welcome back guest blogger Brendan Hannigan, for the third in a series of three posts on working with very large\r\n            images in MATLAB.\r\n   ... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2011\/09\/23\/dealing-with-really-big-images-image-adapters\/\">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":[711,825,332,36,665],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/390"}],"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=390"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/390\/revisions"}],"predecessor-version":[{"id":3755,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/390\/revisions\/3755"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=390"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=390"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=390"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}