Steve on Image Processing
April 2nd, 2009
MATLAB R2009a – imread and multipage TIFFs
One of the changes we made to MATLAB in R2009a was to improve the way imread handles multipage TIFF files. (A multipage TIFF file is one that contains multiple images. I'm not sure, but I think this terminology stems from the use of TIFF to store FAX data.) As I've written about before, in the last few years some of our users have adopted a workflow that involves storing a large number of individual images in a single TIFF file. It turns out that the following loop had a bad performance characteristics:
fname = 'my_file_with_lots_of_images.tif';
info = imfinfo(fname);
num_images = numel(info);
for k = 1:num_images
A = imread(fname, k);
% ... Do something with image A ...
end
One problem was that imfinfo took a long time when the file contained many images. A second problem was that the for-loop execution time scaled with num_images^2 instead of num_images. Neither problem was severe unless num_images was in the thousands, but then the bad scaling took over and made it unusable.
The reason for the bad scaling has to do with the image location is stored in the file as a kind of linked list. I've given more detail on this previously.
Step 1 in addressing the performance problem was to make imfinfo run a lot faster. This enhancement was made in one of the 2008 releases, either R2008a or R2008b. I forget which one.
Step 2, in R2009a, was to add new information to the output of imfinfo and a new syntax to imread to make the read step scale better. The output of imfinfo now includes the locations of every image in the file, and you can now pass that information to imread to help it locate a specific image more quickly.
Here's how the processing loop might look using R2009a:
fname = 'my_file_with_lots_of_images.tif';
info = imfinfo(fname);
num_images = numel(info);
for k = 1:num_images
A = imread(fname, k, 'Info', info);
% ... Do something with image A ...
end
I compared R2008b and R2009a for reading 65,000 images from a TIFF file. It took just under an hour in R2008b, for an average of about 21 seconds per image. It took about 90 seconds in R2009a, for an average of about 1.5 milliseconds per image.
We're not done yet with TIFF enhancements. TIFF is a very flexible format that is used in many different applications and industries. We have more enhancement requests logged related to TIFF than for all the other image formats combined. So we are continuing to devote resources to expanding what MATLAB users can do with TIFF files. If you have any particular requests related to TIFF, please comment on this post.
16:45 UTC |
Posted in Uncategorized |
Permalink |
73 Comments »
You can follow any responses to this entry through the RSS 2.0 feed.
Both comments and pings are currently closed.
|
Steve
I agree. I wrote a simple function tif2img (http://www.mathworks.com/matlabcentral/fileexchange/21002) which reads multi-tiff images and also img2tif (http://www.mathworks.com/matlabcentral/fileexchange/23188) that reads and loads multi-tiffs to workspace. Nevertheless
I do have one complain about how Matlab handles tiffs. If you save an array to image using the imwrite and save it as a tiff for some Matlab 2008a doesn’t add the SubFile Type tag to the tif. This tag is optional -to my knowledge- yet most image editors, e.g. Gimp and Photoshop will add the tag and Matlab doesn’t. You may think it is not important, but applications like Virtual Cell (VCell) require this tag in the file in order to recognize. The non-Matlab solution is quite simple, use the tiffset from libtiff.
Ivan—As far as I know, we’ve never heard this complaint before. We’ll look into it.
Ivan—The SubFile Type tag is optional. According to the TIFF spec, if the tag is not present, it’s value should be assumed to be 0, meaning that the image is a “primary” image. An application that requires this tag to be set in order to read a primary image correctly has not implemented the spec correctly. However, I’ll make a note in our enhancements database to see what we can do in a future release.
Thanks for this tip. I’ve confirmed an ~3x improvement on our images (~256×64 frames of uint16), for movies/stacks ~10,000 frames in length.
You know this feature is undocumented still, right?
The performance gap with ImageJ is still quite significant (~10x for our 10000 frame data). If you don’t close this soon (and I mean soon!), I’ll have little choice but to become an ImageJ expert, and leave Matlab in the dust.
Vijay—The new syntax is mentioned in the M-file help but was accidentally omitted from the reference page. I believe that’s been fixed for the next release.
I have been looking online and have not found a solution. I get how to read multipage tiffs but how do I create them using MATLAB and/or the IP toolbox?
Never mind I see how to do it.
for i = 1:10
imwrite(myImage(:,:,i), ’10-pk_img.tif’,'tif’, ‘Compression’, ‘none’, ‘WriteMode’, ‘append’);
end
PZ posted a comment here to agree with Vijay Iyer’s critical comments above. That’s fine – I have no problem with criticism here, as long as it is relevant to the topic of the post. But PZ, I don’t let comments go through with author names and author URLs that appear to be advertisements.
Sorry about this irrelevant topic.
I really want to write a 1024 x 1024 matrix with floating values to a tiff. Is it doable in Matlab? I know Matlab can read a tiff with floating values without any problem, but I was never able to write a tiff with floating values.
Peter—Your question is more relevant to the topic of this post than most of the comments that come in. ;-)
No, there’s no way to do this in MATLAB right now. It’s on our list of things to work on for a future release.
this speedup is great, thanks so much for implementing it. i’m curious about a similar speedup in imwrite. for large stacks (eg, >1000), i find that imwrite is unacceptably slow. i use the following code now:
data=uint8(floor(255*(data-min(data)/(max(data)-min(data)))); %convert to 8-bit image
for t=1:T
if t==1, mod=’overwrite’; else mod=’append’; end
imwrite(reshape(FF(:,t),height,width),fname,’tif’,'Compression’,'none’,'WriteMode’,mod)
end
would the basic idea of this speedup work for imwrite as well?
Joshuav—Thanks for the comment. We are working on ways to speed up writing stacks to TIFF files. It probably won’t look exactly the same as what we’ve already done with imread.
Steve,
Will Matlab2009b have the function of saving floating-point TIFF? How about GEOTIFF?
Mike
Mike—I generally avoid commenting on specific schedules for software changes.
It would be great if there was an option to load up a variable and write it at once instead of iteratively… at the moment imwrite will not handle this, for example, this imwrite statement will fail when it is a perfectly legitimate request:
for k = 1:num_images A(:,:,k) = imread(fname, k, 'Info', info); end imwrite (A(:,:,k),outfile.tif)On that note, it would be great imread could read all frames at once. If I want to do a simple operation on all frames, I have to run it through a loop and iteratively operate. But it would be great if imread assumed ALL frames, rather than the first, where this code would read, flip, and write all frames:
A=imread('tifname'); A=flipupd(A); imwrite(A,outfile);or at least make a way to address all frames e.g.
A(:,:,:)=imread('tifname',:)sorry, that first imwrite statement should look like
Grant—Your suggestions are reasonable and other people have made them as well. I see at least two interface design problems, though. First, what would you like imwrite to do in the case where num_images equals 3? If you pass an M-by-N-by-3 array to imwrite today, it writes a single-frame RGB image. How can imwrite distinguish between between a single-frame RGB image and 3 image frames?
Second, what should imread do for TIFF files that contain images of different size, bit depths, color formats, etc.? You can’t stack such images into a single multidimensional MATLAB array. You could return a cell array, but that would lose the convenience you’re looking for, and it would introduce an awkward syntactic discontinuity for the case where num_images equals 1.
I’m sure that all of these functional behavior issues could be solved. However, because the for-loop is very straightfoward to write, and because of the many other TIFF-related enhancement requests, we are unlikely to implement something like you suggest in the near future.
Hi Steve, my question is on making multipage tif files, can this be done in matlab? I have csv files that I want to wrap together into a multipage tif. My first test was using imwrite and trying to append 2 different matrices, I seem to only get the first one, it is at least 16 bit but only one file. Anyone have any suggestions?
Can you give us an overview of what the LibTIFF library wrapper that’s included in 2009b will get us? This seems like it could be the most powerful way to work with large multi-page TIFF files, streamed reads/writes, etc.
Gerard—I get a two-image TIFF file when I run your code. How do you know your file contains only one image? What’s the output of this code:
num_images = numel(imfinfo('16bit.tif'))Vijay—Patience. R2009b is scheduled to ship next month. After it is released I will be happy to discuss what’s in it.
Not sure if this is still the best way to do things, but I am using 2009a still and wrote a function similar to the one Grant (above) requested – importing all tiffs in a stack, but assuming they have the same size, and bit depth, and number of color channels.
– Jamie
function xyt_stack=import_tiff_stack(fname) % returns an array with xy in first two dimensions, stack in thrid % dimension, and multi [color] channels (if present) in fourth dimension % NB - assumes all images in stack have same size, bit depth, and color ch. info = imfinfo(fname); [img_size1 img_size2 img_size3]=size(imread(fname, 1, 'Info', info)); img_type=['uint' num2str(info(1).BitDepth)]; num_images = numel(info); xyt_stack=zeros([img_size1 img_size2 num_images img_size3], img_type); %preallocate array of correct type and size to improve speed for k = 1:num_images A = imread(fname, k, 'Info', info); for ch=1:size(A,3) xyt_stack(:,:,k,ch)=A(:,:,ch); end endNB – for a 512x512x8000 tiff stack, the code above took 57.414 seconds to execute on my (~2006) workstation, while ImageJ took 18.797 seconds to open the same file. *Only* a factor of 3 slower!
Hi Steve,
Thank for your great work and your really useful blog.
I looking for a way to
1) to read a stack of tiff images and to keep the header information (image description, resolution…) for each files
2) to process this data
3) to save the result in a stack of tif file with the header information of the input stack.
As you suggested, I read the stack as follow
info = imfinfo([path name]);
dim = numel(info)
% Image reading (I am using matlab 2007a)
for i = 1:dim
imgIn(:,:,i) = imread([path name],i);
end
1) My first question is:
- when I check: ‘info(1,1).ImageDescription’, I get the description.
- when I check: ‘info(1,2).ImageDescription’, I get nothing.
My original file is a stack of multispectral images and the channel information is included in the ImageDescrpition field.
Do you know a way to get this information for all the tif file.
2) My second question is about the writing part.
I would like to copy/paste the header information of the input stack images in the output stack images.
As you suggested I use
imwrite(imgOut(:,:,1),[path nameout])
for k = 2:dim
imwrite(imgOut(:,:,k),[path nameout], ‘writemode’, ‘append’);
end
Do you know a way to add the ‘info’ variable for each tiff file?
Thank in advance for your help.
Best regards
Pierrick
Pierrick—Regarding your first question … if info(2).ImageDescription is empty, then there was no description in the file for the 2nd image.
Regarding your second question … except for ‘Description’, imwrite doesn’t support writing metadata to the file. You could use the Tiff object to do it, but you’ll have to upgrade your MATLAB because the Tiff object was introduced after R2007a.
Hi Steve, thanks again for your great blog. This was very useful information!
The only thing I don’t like about this is that according to the documented approach, I have to pass in the entire imf struct array with every imread call. So if I want image 1000, I have to do imread(impath, 1000, ‘Info’, imfarr) where imfarr is an array with at least 1000 entries. It would be nice if I could do something like imread(impath, 1000, ‘Info’, imf1000). Where imf1000 is just the single imf entry for page 1000.
It turns out there are several hacky ways to achieve the desired behavior. A simple one is: hack(1000) = imf1000; imread(impath, 1000, ‘Info’, hack). Even more fun (i.e. hackier) is: imread(impath, 1, ‘Info’, imf1000), which on my machine does indeed get me page 1000. Do you think a less hacked version of this might ever become documented functionality?
I can imagine an implementation where each imf entry, in addition to the Offset field, also has a PageNum field. Then if imread gets an ‘Info’ argument of only length == 1, it would just make a sanity check that the imf entry has the correct PageNum before efficiently loading and returning the data.
Thanks!
P
Peter—I don’t understand what you don’t like about passing in the entire info struct to imread.
Hi Steve, I’m imagining a case where we have a bunch of images that we’re storing information for. Information includes path and page to enable loading the raw data on demand. I thought we could also include the imf entry to enable efficient offset retrieval, except would be nice not to have to store the entire imf struct. Of course, there are other ways to work around this, like have a Map between path and imf stored, but keeping things simple has its attractions.
Rather than the implementation I suggested above, I suppose more logical would be a situation where you call imread without a page number but just with a single imf entry.
As a more practical question though, I’ve tried using this ‘Info’ argument on R2010a but I’m not seeing much speedup between a = imread(fname, i) and a = imread(fname, i, ‘Info’, imf). Is there some other bottleneck that could be stopping me from seeing the performance benefit?
Peter—This syntax was not created to speed up the case of reading a single image from a multipage TIFF. It is to speed up reading all N images from a multipage TIFF, particularly where N is in the thousands or tens of thousands.
Hmm, I’m confused; in your example you are still reading the images as a for loop, so it still seems to read them one at a time although in the end you read all of them. So it seems it could still apply to my situation?
(Sorry, in the cases I’ve tested I have not seen speedup in reading with a for loop all images in a multipage TIFF, although I have only tested TIFFs as large as 4000 pages.)
Peter- Thank a lot for your response.
I have asked to my system administrator to upgrade my matlab version and now I have 2010a.
I tried to use Tiff object as you suggested to me in order to keep all the informations contained in the header of my multiframe tiff file.
However, I did not success to open my stack with t.read(), I only get the first one.
I was doing that
% Image reading
for i = 1:dim
imgIn(:,:,i) = imread([path name],i,’Info’, info);
end
I tried that
t_in=Tiff([path name],’r')
for i = 1:dim
% I do not know how to access to the different images
% the images are not considered as IFD
img(:,:,i) = t_in.read();
end
t_in.read() gives me:
t_in =
TIFF File: ‘test.tif’
Mode: ‘r’
Current Image Directory: 1
Number Of Strips: 64
SubFileType: Tiff.SubFileType.Default
Photometric: Tiff.Photometric.MinIsBlack
ImageLength: 512
ImageWidth: 512
RowsPerStrip: 8
BitsPerSample: 16
Compression: Tiff.Compression.None
SampleFormat: Tiff.SampleFormat.UInt
SamplesPerPixel: 1
PlanarConfiguration: Tiff.PlanarConfiguration.Chunky
ImageDescription: [Intensity Mapping]
Map Ch0: Range=00141 to 02966
[Intensity Mapping End]
[Display Settings]
Gamma 0=1
DisplayMode= 545
DisplayZoom= 1
[Display Settings End]
[Acquisition Parameters]
…
Moreover, it appears that we cannot change the Namefile field. My idea was to copy header from t_in as follows:
1) t_out = t_in
2) Change the namefile with t_out.setTag(‘NameFile’, nameout)
3) use t_out.write(imgOut)
Do you have an idea to copy/paste the header and to write the processed 3D data in the same way than the input data.
Thank in advance
Best
Pierrick
Hi Pierrick,
Regarding this “NameFile” tag, it’s not necessary. I think you are referring to the “Filename” field of the IMFINFO output structure. The name of the file is taken care of in the Tiff constructor like so
t_in = Tiff('test.tif','r'); t_out = Tiff('myfile.tif','w');You will need to set some minimum tags for each image. These include Photometric, ImageLength, ImageWidth, BitsPerSample, SamplesPerPixel, PlanarConfiguration, and either RowsPerStrip or both of TileLength and TileWidth.
Check the doc for “Exporting Image Data and Metadata to TIFF files” for a quick intro for everything you need to write A SINGLE IMAGE TIFF. For multi-page TIFFs, you need to use Tiff’s “writeDirectory” method to set up to write the next image, and the “nextDirectory” to set up to read the next image in the input file. So your series of tasks is basically
Hi John,
Thank a lot for you help.
I tried to use your suggestions but its seems that I felt to correctly use Tiff object. I do the follows steps:
t_in = Tiff(fullfile(pathin, namein),'r'); t_out = Tiff(fullfile(pathout, nout),'w'); for k = 1 : dim t_out.setTag('ImageWidth', t_in.getTag(t_in.TagID.ImageWidth)); t_out.setTag('ImageLength', t_in.getTag(t_in.TagID.ImageLength)); t_out.setTag('Photometric', t_in.getTag(t_in.TagID.Photometric)); listTag = t_in.getTagNames; nbTg = size(listTag); for tg = 1 : nbTg(1) TagID = ['t_in.TagID.' listTag{tg}]; t_out.setTag(listTag{tg},t_in.getTag(eval(TagID))); end t_out.write(imgOut(:,:,k)); t_in.nextDirectory(); t_out.writeDirectory(); endI do first :Width, lenght and Photometric, because in other case I get
“??? Error using ==> tifflib
Unable to retrieve the photometric interpretation, which must be available before setting BitsPerSample.”
The order of tag seems not really well manage by tifflib.
Unfortunately, my approach do not work and give me some error like
“??? Error using ==> tifflib
Unable to retrieve Thresholding.
Error in ==> Tiff>Tiff.getTag at 795
tagValue = tifflib(‘getField’,obj.FileID,tagId);”
Thresholding is in t_in, thus in the list of tag, but the value cannot be obtained.
and gives me:
t_in.getTag(eval(TagID)) ??? Error using ==> tifflib Unable to retrieve Thresholding. Error in ==> Tiff>Tiff.getTag at 795 tagValue = tifflib('getField',obj.FileID,tagId);I have to do something worng but I cannot find my error.
Do you have an idea?
Thank in advance
Best
Pierrick
Hello,
I use the following routine to append two files, say x1.tif and x2.tif. I believe I’m running into a problem where I’m writing two IFD’s, i.e. once I start appending (when x==2) I create a new IFD. Is there a way to preserve the original IFD on a second imwrite invocation?
Thanks,
Grant
nFrames=(numel(info)); % grab number of frames for x=1:2 if x==2 tifname=x2_file; %paraphrased else tifname=x1_file; end for frame=1:nFrames %A cannot be pre-allocated. A(:,:) = imread(tifname, frame, 'Info', info); if frame==1 && x==1; imwrite(A,outfile, 'tif', 'Compression','none') else imwrite(A,outfile,'tif','Compression','none','WriteMode','append'); end end endGuess I should be more clear about the problem. So with the above code I’m appending two files to make a 6000-slice stack. The first file is 4092 slices, the other is 1908. I can open the resulting 6000 image file with ImageJ just fine as a virtual stack, but it chokes right at slice 4093 when I try to open it regularly. There is no problem opening a file made with the above script that has not entered the second imwrite (append) instance, e.g. a file with
number of images: 10
offset to first image: 8
gap between images: 446
opens fine.
but I run into a “TiffDeocder: Negative Seek Offset” error in imageJ trying to load into anything that has been appended, e.g.
number of images: 6000
offset to first image: 8
gap between images: variable (-507458 to 508350)
Notice the weird variable (and negative!) gap between images. So after talking with the ImageJ creator it seems that, for a reason I don’t understand, Matlab starts writing weird IFDs once it starts appending file 2. I thought it might be because it’s using another IFD value from the second file. So I was just wondering if there’s a way to e.g. preserve or otherwise specify the original IFD throughout the imwrite process.
Any help would be appreciated!
Grant
Grant—How big is the file (in bytes)?
The final 6000-slice stack is 3,148,404,008 bytes. The first (x1) file I use for appending is 2GB…
Possibly there is a problem in the implementation with crossing the 2GB boundary. I suggest that you contact technical support, and I’ll also let the development team know about this thread.
The recent changes to the imread and imwrite turned my whole image processing suite into a total mess. While imread now takes some 10x longer to load stacks with more than a few hundred slices (even with the ‘Info’ option), imwrite simply fails after writing a certain number of slices into a new multi-tiff.
The only way I know for saving multi-tiffs in Matlab is something like this:
for i=1:z imwrite(A(:,:,i),filename,'Compression','none','Resolution',1,'WriteMode', 'append'); end;It used to work just great (and fast) until the recent “improvements” to the Matlab’s image handling functions. Now the code that used to work fine produces the following error:
??? Error using ==> imwrite at 453 Can't open file "Time000000.tif" for writing. You may not have write permission. Error in ==> USaveStack at 30 imwrite(A(:,:,i),filename,'Compression','none','Resolution',1,'WriteMode',over);It does, however, write the first forty something slices.
Any suggestions?
Urle—Please contact technical support,and I will forward your note to the development team.
Hi
I am new to MATLAB, and I’m having some trouble with image processing tools. We take images of surfaces at different depths, and save them as individual tiff files (one image file at each depth). We have about 50 different depths at which these images are recorded at each location on the surface.
I am interested in building the series of image files into a 3D array containing intensity information at each (x,y,z) location. Could you let me know how this can be done?
It would be great if anyone could share a code for this.
thanks!
Suchi—There are some contributions on the MATLAB Central File Exchange related to three-dimensional reconstruction. You might take a look there.
Hi Steve,
I’m trying to plot a georeferenced image, with for instance imagesc.m, only within closed prescribed polygon (containing x,y) sections. Normally I use patch.m for doing this but then I fill the polygon sections with a uniform color.
Obviously my polygon has diffent dimensions compared to the georeferenced image. Do you have any suggestions how to tackle this issue?
Let me know if this is not the right blog to post this query?
Thanks in advance!
Jan
Jan—This is a not a general Q&A forum. I usually only allow comments that are relevant to the post. You might consider posting your question to the comp.soft-sys.matlab newsgroup.
I am having the same problem as Urle. Short multipage tiffs (< 100 pages) rarely have this problem. Larger tiffs (2000 pages) get through the first ~150 pages fine before returning the error. Trying to append subsequent images to the file works–for about 50 more frames at a time after that. This gets frustrating fast. The workaround I’ve found is to add a pause, BUT so far as I’ve found the pause has to be at least half a second, so making a multipage tiff of a 1 minute movie can take more than 5 minutes to tackle! Long story short, it works, but not well.
By the way, I’m using 2009a on a Windows 7 machine.
Beth—See this more recent post. It has more information and a workaround.
I’ve been struggling a little recently with large, multipage TIFF reads and just came across this post.
So I can use the usual:
image = imread('mytiff.tif','Index',2);but this starts to slow down pretty quickly with larger images.
To speed up the read, and give the user some feedback, I’ve done a block read instead:
layer = 2; blockread = @(block_struct) imread('mytiff.tif','Index',layer,... 'PixelRegion',{rows,cols}); image = blockproc('mytiff.tif',[M N],blockread);Now this works great for multipage TIFFs with identically sized images. But it fails completely when the first page is differently sized compared to the rest. It turns out this is the case with the multipage TIFFs I get from a piece of equipment. Once placed into a blockproc context, the imread function seems to assume that all pages are sized identically to the first. For example, if the first page is smaller, my blockread function quits prematurely and displays a crop of the 2nd layer that has similar dimensions to the 1st layer. I suppose this is a bug in imread.
To work around this, I am forced to use the first (usual) approach, which can be really frustrating to wait for. Some of these images take many minutes to load.
Hi Doug,
This isn’t a bug. When you call blockproc with the first argument ‘mytiff.tif’, it will do a 2D block-wise pass across the image, processing each MxN block of image data. Before doing this, it queries the size of the image so it knows how many blocks it will process based on the size of the image and the MxN block size you specify.
In the case of a multi page TIFF file, blockproc uses the size of the first page as the image size. There is no way to specify a different page to use for this.
In your example you have changed the page ‘Index’ to 2, but the only person who “knows” this is your anonymous function handle. blockproc has no knowledge of the details of your function handle, so updating the ‘Index’ value in @blockread has no effect on the data that blockproc will send it. It only changes what you are *doing* to the data once blockproc has sent it to the function handle.
I’m actually not quite sure I follow your example. Your blockread function is actually discarding the data that it was passed by blockproc (via the block_struct) and returning unrelated data. How are you computing rows and cols inside of blockread? Are you using the location field of the block struct?
Hi Brendan,
Sorry about that. I’d removed quite a bit of code for clarity, which obviously backfired. Here’s the full version of my blockread function definition, which uses the block_struct passed by blockproc:
% Define block read function layer = 1; blockread = @(block_struct) imread([pathname filename],'Index',layer,... 'PixelRegion',... {[block_struct.location(1) ... block_struct.location(1)+block_struct.blockSize(1)],... [block_struct.location(2) ... block_struct.location(2)+block_struct.blockSize(2)]});To use this, I call imfinfo to pull the image header from the file, and calculate the rows and cols before passing to blockproc. I change the layer variable just before the call to blockproc:
% Get image information imageInfo = imfinfo('mytiff.tif'); % Read in 16MB blocks bytesPerRead = 16777216; % Assume image is stored conventionally in rows rowsPerStrip = imageInfo.RowsPerStrip; colsPerStrip = imageInfo.Width; bitsPerPixel = imageInfo.BitDepth; bytesPerStrip = rowsPerStrip*colsPerStrip*bitsPerPixel/8; stripsPerRead = ceil(bytesPerRead/bytesPerStrip); % always >0 % Read image layer = 2; myimage = blockproc('mytiff.tif',... [stripsPerRead*rowsPerStrip colsPerStrip],blockread);To make a long story short, I can’t seem to get it to read into the second layer. Is there a way to get it to work, by somehow passing the layer variable to imread via blockproc?
Oops. I missed your little sentence on blockproc, “In the case of a multi page TIFF file, blockproc uses the size of the first page as the image size.”
Absolutely no workaround for this?
Hi Doug,
Thanks for your detailed response. There is a workaround for your issue. When you can’t get exactly the right data you want into BLOCKPROC using one of the default syntaxes, we provided you with a way to make a totally custom input source, the ImageAdapter interface class. BLOCKPROC accepts as input MATLAB objects that inherit from a particular abstract interface, the ImageAdapter. There is a chapter in the IPT users’ guide describing how to write ImageAdapter objects, but it’s really quite simple in your case. I went ahead and wrote it, and it looks like this:
classdef PagedTiffAdapter < ImageAdapter properties Filename Info Page end methods function obj = PagedTiffAdapter(filename, page) obj.Filename = filename; obj.Info = imfinfo(filename); obj.Page = page; obj.ImageSize = [obj.Info(page).Height obj.Info(page).Width]; end function result = readRegion(obj, start, count) result = imread(obj.Filename,'Index',obj.Page,... 'Info',obj.Info,'PixelRegion', ... {[start(1), start(1) + count(1) - 1], ... [start(2), start(2) + count(2) - 1]}); end function result = close(obj) %#ok end end endIt’s easy to use! I modified your code to use the new adapter, thusly:
% Get image information filename = 'mytiff.tif'; page = 2; imageInfo = imfinfo(filename); imageInfo = imageInfo(page); % Read in 16MB blocks bytesPerRead = 16777216; % Assume image is stored conventionally in rows rowsPerStrip = imageInfo.RowsPerStrip; colsPerStrip = imageInfo.Width; bitsPerPixel = imageInfo.BitDepth; bytesPerStrip = rowsPerStrip*colsPerStrip*bitsPerPixel/8; stripsPerRead = ceil(bytesPerRead/bytesPerStrip); % always >0 % Read image my_adapter = PagedTiffAdapter(filename,page); no_op_fun = @(bs) bs.data; myimage = blockproc(my_adapter,... [stripsPerRead*rowsPerStrip colsPerStrip],no_op_fun);Your new image adapter object acts as an input source for blockproc, so now, instead of discarding the data in the block struct and reading new data from a different page (like you were doing originally), blockproc will read data from your ImageAdapter object, which will supply the correct data from the correct page that you specify when you construct it.
Your user function now, instead of reading new data, will be a no-op, and simply return the data it was given, unchanged.
Like I said before the Users’ Guide has a great chapter on how to write ImageAdapter classes and explains everything with a nice example. If you want to learn how about how these work you can check it out.
hope this helps!
-brendan
Brendan, thank you for showing the ImageAdapter class, I’m sure it will be very useful!
I’ve been encouraging Brendan to do a guest post on blockproc and ImageAdapter. OK, everyone, chant it with me:
Bren-dan … Bren-dan … Bren-dan … Bren-dan …
That is an incredible response. Thank you Brendan!
I noticed a bug in the “imwrite” function in conjunction with multipage TIFF files.
The following code is what I use in my code to store temporary results for later analysis:
Each single page is quite large, so 200 Pages result in approx. 6 GB.
The problem is: Viewers (XnView or a selfmade viewer written in MATLAB) can only access 64 Pages, and these pages are corrupted (horizontal lines with nonsense data, etc.)!
When the output file is less than 4 GB, the file is just fine, e.g. its possible to step through all pages, even pages after page 64, and the pages are not corrupted.
So there seems to be a serious problem with the imwrite-function for large multipage TIFF files.
(MATLAB 2011a x64, Win7 x64, TIFF file was initially created by MATLAB)
Karl—The TIFF format itself does not support file sizes larger than 4 GB. Take a look at the official TIFF specification and take note of the way 32-bit integers are used to indicate file locations throughout the spec.
There is a variant of TIFF called BigTIFF that supports larger files, but MATLAB does not yet support it.
Karl, have you tried a different compression scheme? Depending upon your data, you just might be able to bring the file size in under 4gb that way.
Hi there…
I am new in matlab processes and i have an issue how to handle tiff images. Actually the problem is that i have many tiff images and i am trying to make a 3d array from those.
I wrote this code but i am sure i have do it wrong :)
Thank you for your time and for any help!
Andreas—Try something like this:
Steve thank you for your response it was helpful!
Well when i use this code [info = imfinfo(nam(4).name);] i get this msg:
??? Index exceeds matrix dimensions.
i am doing something wrong?
Andreas—You’re the one who started your for-loop at k=4! I was just following your example. The error message suggests that dir returned fewer than four outputs.
Hi Steve
yes the loop is wrong it needs to start from the first image. I am still doing something wrong here. I stack in the info section.
I think i need lessons about coding in matlab :)
any ideas? :P
info = imfinfo(nam(1).name)
Error using ==> imfinfo at 96
Unable to open file “.” for reading.
Steve is there a way to contact u via email to solve me some issues in coding?
Andreas—The “.” refers to the current working folder (directory). Similarly, “..” refers to the parent folder. You’ll need to make sure that you filter out things returned by dir that are not the image files you are trying to read.
Hi. I’m a master student in remote sensing field and I have a problem. I want to read tif image from my computer but I want not to use “imread” command and I want to write this command myself. I mean that I want to read image from my computer without using this command. Can you help me ?
Saeed—You could spend your time in graduate school studying remote sensing, or you could spend your time writing file format code that duplicates what is already available to you. If you choose to do the latter, the best I can do is to point you to the TIFF specification.
Dear Seteve – at first I thank you for your help and responding to my reply. After that I agree whit you and I chose the first choice but unfortunately my image processing professor doesn’t understand this issue. This program is our first project and I’m really dissatisfied whit this project. Because instead of time I spend for this project I can do programming many of other useful image processing functions for image enhancement or etc.
I have a problem when I read large Tif file. I use “imread” function, but it always say:”Out of Memory.” I don’t know how to solve this problem which had troubled me for a few days.
A=imread('I:\2005波段数据\AF-2005\MOD09A1AF.A2005001.sur_refl_b02.tif'); ??? Error using ==> rtifc Out of memory. Type HELP MEMORY for your options. Error in ==> readtif at 52 [X, map, details] = rtifc(args); Error in ==> imread at 430 [X, map] = feval(fmt_s.read, filename, extraArgs{:});Hello again,
For quite some time I have been successfully using a code that invokes imwrite in a loop. The specific command is
imwrite(A,outfile,’tif’,'Compression’,'none’,'WriteMode’,'append’);
Suddenly I’m getting the following error in the middle of the writing process, seemingly at random (random file it crashes on, and in random spots in the stack):
??? Error using ==> imwrite at 453
Can’t open file “I:\Grant\PIFE\120129\rot_RT2_100nM_RT_5.tif ” for writing.
You may not have write permission.
Error in ==> tiffdir at 170
imwrite(A,outfile,’tif’,'Compression’,'none’,'WriteMode’,'append’);
Many times I can just restart the program and it will go without a hitch, however this is not desirable as I typically process large batches of large files overnight…
Any suggestions?
Grant—See my 09-Nov-2010 blog post and this tech support solution.
Thanks- Don’t know how I missed that.. I think I searched for “can’t open file for writing,” the output from 2010a, while these posts refer to “couldn’t open file for writing.”