<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">2014-11-04 15:31 GMT+01:00 Bradley Lowekamp <span dir="ltr"><<a href="mailto:blowekamp@mail.nih.gov" target="_blank">blowekamp@mail.nih.gov</a>></span>:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word">Discussion inline below:<div><br><div><span><div>On Nov 3, 2014, at 5:58 PM, Pol Monsó Purtí <<a href="mailto:lluna.nova@gmail.com" target="_blank">lluna.nova@gmail.com</a>> wrote:</div><div><br></div><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><div>I've read the article early this afternoon and I found it very good and encouraging. Specially <i>"The other difference is that the reader fully supports streaming and only reads the required region from the file."</i><br></div> <br></div><div>That is exactly what we need. I was just wondering wether it splitted the volume and took the chunks that intersected with the requested one or if it really just loaded the requested one. I replicated that code taking out the processing and just having a reader and a writer, but I didn't know how to test that indeed it was just loading that requested piece.<br></div></div></div></div></blockquote><div><br></div></span><div>For the ImageIO file formats I wrote which are just headers with binary data, they can read an arbitrary ImageIORegion and don't read any more. Practically however, its extremely in efficient to not read complete contiguous scanlines as there is to much random access and it slows down. For Tiff ( similar to some JPEG2000 formats), the image is encoded as strips or tiles. The underlying libtiff library can only easily decode these entire chunks, so the plan is that the requested region will be expanded by the image io to the union of the strips/tiles needed to read the requested region.</div><span><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span>
<br>
> 2.- Will the pipeline only load the requested region, including the reader?<br>
<br>
</span>The pipeline has a series of steps which include propagating a requested region, and giving each filter the opportunity to enlarge this region. I have frequently assemble pipelines which perform out-of-core processing as you desire. However all filters in the pipeline must support streaming, and not enlarging the requested region to the largest possible region.<br>
<br></blockquote><div><br></div><div>Yes, I've seen that not many support streaming. But in our case we would just need a reader that can read a specific region. <br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Additionally the ImageIO for the reader and writer must also support streaming. There are several ImageIO's which support streaming reading (MRC,MetaIO, VTK, Nift, JPEG2000, etc), I believe that only MRC, VTK and MetaIO fully support it for writing. So TIFF is currently not there.<br></blockquote><div><br></div><div>Yeah, we were thinking on using HDF5, but maybe you have some idea on what would be best. That's another topic thought.<br></div></div></div></div></blockquote><div><br></div></span><div>I find the the header + raw image data are rather efficient for random access of the whole image. These files are archived with compression, and decompressed when needed. </div></div></div></div></blockquote><div><br></div><div>Yes indeed.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><div><span><br><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Recently I have been working on improving the itkTIFFImageIO [2], and have performance gains of upto 3-5X from the prior version. However, I haven't gotten to adding streaming yet. It's is a back burner project for me to add support for stream reading to it, so that may be coming shortly ( this poking helps ). However, adding stream and paste writing is not planned.<br>
<br></blockquote><div><br></div><div>Good work! 3-5x! That might be of interest actually for something else we have. Can we pull your code?<br></div></div></div></div></blockquote><div><br></div></span><div>Its in ITK master please test :)</div></div></div></div></blockquote><div><br></div><div>Will do.<br> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><div><span><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
For this case you should add in an ExtractImageFilter, or a CropImageFilter.<br>
<br></blockquote><div><br></div><div>But if I use Extract or Crop, the reader is still loading the full image to disk, isn't it?<br></div></div></div></div></blockquote><div><br></div></span><div>Nope. The only the sub-region is requested to the reader, and if the reader supports streaming that region will be read.</div><div><br></div><div>I'd suggest reading section 13.3 of the ITK Software guide a couple times. And then assemble the pipeline Reader->Extract, and printing the output of each filter for each step in Extract->UpdateOutputInformation(), Extract->PropagateRequestedRegion() and then Extract->UpdateOutputData(). This will help you to under stand the step in the pipeline execution.</div><div><br></div></div></div></div></blockquote><div><br></div><div>So do readers (hdf5 for example) transparently do streaming? Or do I have to set the reader imageIO so that it know that it has to stream?<br><br></div><div>Section 13.3? There are 9 sections, which one do you refer to? I think I get what you mean and I'll split up the process to undrstand it better as you suggest.<br><br></div><div>Meanwhile, below I paste a piece of code more close to what we would like to do. The code results in exception because the ImageIO starts at index [0,0,0] while piece1 starts at 51,80,5 and therefore the Extract fails. I didn't understand how to set the index on a ImageIO. Then I saw that in your article you put the whole ImageIO and then you just select one slice with coronalSlice (which would be my piece1/2). I tried that too, although I wonder what's the difference with not specifying ImageIO and read normally. setting imageio1/2 to the full image failes on the reader, saying that it expected 24300094 bytes (that's the size of the image, so it's trying to load all I guess), but read 411866 bytes. <br><br></div><div>How can I do this right? And how would the code change if I were to use HDF5?<br> </div><br></div>the code:<br><br></div><div class="gmail_extra">int main( int argc, char* argv[] )<br>{<br><br> const unsigned int Dimension = 3;<br><br> typedef unsigned char PixelType;<br> typedef itk::Image< PixelType, Dimension > ImageType;<br><br> typedef itk::ImageFileReader< ImageType > ReaderFilterType;<br> ReaderFilterType::Pointer reader1 = ReaderFilterType::New();<br> reader1->SetFileName("../data/image.raw");<br><br> //size is not known until we load the image<br>// ImageType::RegionType largest = reader1->GetOutput()->GetLargestPossibleRegion();<br>// ImageType::SizeType size = largest.GetSize();<br>// ImageType::IndexType index = largest.GetIndex();<br><br> ImageType::IndexType start;<br> start.Fill(0);<br><br> ImageType::SizeType size;<br> size[0] = 511;<br> size[1] = 806;<br> size[2] = 59;<br><br> ImageType::RegionType largest(start,size);<br><br> ReaderFilterType::Pointer reader2 = ReaderFilterType::New();<br> reader2->SetFileName("../data/image.raw");<br><br> ImageType::RegionType piece1;<br> piece1.SetIndex(0,start[0] + 0.1*size[0]);<br> piece1.SetIndex(1,start[1] + 0.1*size[1]);<br> piece1.SetIndex(2,start[2] + 0.1*size[2]);<br><br> piece1.SetSize(0,0.1*size[0]);<br> piece1.SetSize(1,0.1*size[1]);<br> piece1.SetSize(2,0.1*size[2]);<br><br> ImageType::RegionType piece2;<br> piece2.SetIndex(0,start[0] + 0.5*size[0]);<br> piece2.SetIndex(1,start[1] + 0.5*size[1]);<br> piece2.SetIndex(2,start[2] + 0.5*size[2]);<br><br> piece2.SetSize(piece1.GetSize());<br><br> std::cout << "largest: " << largest << std::endl;<br> std::cout << "piece1: " << piece1 << std::endl;<br> std::cout << "piece2: " << piece2 << std::endl;<br><br> typedef itk::RawImageIO <PixelType, Dimension> ImageIOType;<br> ImageIOType::Pointer imageio1 = ImageIOType::New();<br> ImageIOType::Pointer imageio2 = ImageIOType::New();<br><br> imageio1->SetDimensions(0,piece1.GetSize()[0]);<br> imageio1->SetDimensions(1,piece1.GetSize()[1]);<br> imageio1->SetDimensions(2,piece1.GetSize()[2]);<br><br> imageio1->SetHeaderSize(piece1.GetSize()[0]*piece1.GetSize()[1]*piece1.GetSize()[2]);<br><br> imageio2->SetDimensions(0,piece2.GetSize()[0]);<br> imageio2->SetDimensions(1,piece2.GetSize()[1]);<br> imageio2->SetDimensions(2,piece2.GetSize()[2]);<br><br> imageio2->SetHeaderSize(piece2.GetSize()[0]*piece2.GetSize()[1]*piece2.GetSize()[2]);<br><br> reader1->SetImageIO(imageio1);<br> reader2->SetImageIO(imageio2);<br><br> //this should now work<br> reader1->UpdateOutputInformation();<br> reader2->UpdateOutputInformation();<br><br> std::cout << "reader largest " << reader1->GetOutput()->GetLargestPossibleRegion() << std::endl;<br><br> typedef itk::ExtractImageFilter< ImageType, ImageType > ExtractFilterType;<br> ExtractFilterType::Pointer extract1 = ExtractFilterType::New();<br> ExtractFilterType::Pointer extract2 = ExtractFilterType::New();<br> extract1->SetExtractionRegion(piece1);<br> extract2->SetExtractionRegion(piece2);<br> extract1->SetInput(reader1->GetOutput());<br> extract2->SetInput(reader2->GetOutput());<br>#if ITK_VERSION_MAJOR >= 4<br> extract1->SetDirectionCollapseToIdentity();<br> extract2->SetDirectionCollapseToIdentity();<br>#endif<br><br> try<br> {<br> extract1->Update();<br> extract2->Update();<br> }<br> catch( itk::ExceptionObject & error )<br> {<br> std::cerr << "Extract error: " << error << std::endl;<br> return EXIT_FAILURE;<br> }<br><br> typedef itk::SubtractImageFilter <ImageType, ImageType ><br> SubtractImageFilterType;<br><br> SubtractImageFilterType::Pointer subtractFilter<br> = SubtractImageFilterType::New ();<br> subtractFilter->SetInput1(extract1->GetOutput());<br> subtractFilter->SetInput2(extract2->GetOutput());<br><br><br> typedef itk::ImageFileWriter< ImageType > WriterFilterType;<br> WriterFilterType::Pointer writer = WriterFilterType::New();<br> writer->SetFileName("image_out.raw");<br> writer->SetInput(subtractFilter->GetOutput());<br><br> try<br> {<br> writer->Update();<br> }<br> catch( itk::ExceptionObject & error )<br> {<br> std::cerr << "Error: " << error << std::endl;<br> return EXIT_FAILURE;<br> }<br><br> return EXIT_SUCCESS;<br>}<br></div></div>