1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 #include "QuickView.h"
19 
20 #include "itkRescaleIntensityImageFilter.h"
21 #include "itkVectorRescaleIntensityImageFilter.h"
22 #include "itkRGBToVectorImageAdaptor.h"
23 #include "itkFlipImageFilter.h"
24 
25 #include "vtkVersion.h"
26 
27 #include "vtkRenderWindowInteractor.h"
28 #include "vtkImageMapper3D.h"
29 #include "vtkImageActor.h"
30 #include "vtkActor2D.h"
31 #include "vtkInteractorStyleImage.h"
32 #include "vtkRenderer.h"
33 #include "vtkCamera.h"
34 #include "vtkTextProperty.h"
35 #include "vtkTextMapper.h"
36 
37 #include "vtkCaptureScreen.h"
38 #include "vtkPNGWriter.h"
39 #include "vtkJPEGWriter.h"
40 #include "vtkBMPWriter.h"
41 #include "vtkTIFFWriter.h"
42 
43 #include "itkImageToVTKImageFilter.h"
44 
45 
46 using UnsignedCharRGBImageType = itk::Image<itk::RGBPixel<unsigned char>, 2>;
47 using FloatRGBImageType = itk::Image<itk::RGBPixel<float>, 2>;
48 
49 using UnsignedCharImageType = itk::Image<unsigned char, 2>;
50 using CharImageType = itk::Image<char, 2>;
51 using UnsignedShortImageType = itk::Image<unsigned short, 2>;
52 using ShortImageType = itk::Image<short, 2>;
53 using UnsignedIntImageType = itk::Image<unsigned int, 2>;
54 using IntImageType = itk::Image<int, 2>;
55 using UnsignedLongImageType = itk::Image<unsigned long, 2>;
56 using LongImageType = itk::Image<long, 2>;
57 using FloatImageType = itk::Image<float, 2>;
58 using DoubleImageType = itk::Image<double, 2>;
59 
60 template void ITKVtkGlue_EXPORT QuickView::AddImage<CharImageType>(
61   CharImageType *image,
62   bool FlipVertical,
63   std::string Description);
64 template void ITKVtkGlue_EXPORT QuickView::AddImage<UnsignedShortImageType>(
65   UnsignedShortImageType *image,
66   bool FlipVertical,
67   std::string Description);
68 template void ITKVtkGlue_EXPORT QuickView::AddImage<ShortImageType>(
69   ShortImageType *image,
70   bool FlipVertical,
71   std::string Description);
72 template void ITKVtkGlue_EXPORT QuickView::AddImage<UnsignedIntImageType>(
73   UnsignedIntImageType *image,
74   bool FlipVertical,
75   std::string Description);
76 template void ITKVtkGlue_EXPORT QuickView::AddImage<IntImageType>(
77   IntImageType *image,
78   bool FlipVertical,
79   std::string Description);
80 template void ITKVtkGlue_EXPORT QuickView::AddImage<UnsignedLongImageType>(
81   UnsignedLongImageType *image,
82   bool FlipVertical,
83   std::string Description);
84 template void ITKVtkGlue_EXPORT QuickView::AddImage<LongImageType>(
85   LongImageType *image,
86   bool FlipVertical,
87   std::string Description);
88 template void ITKVtkGlue_EXPORT QuickView::AddImage<FloatImageType>(
89   FloatImageType *image,
90   bool FlipVertical,
91   std::string Description);
92 template void ITKVtkGlue_EXPORT QuickView::AddImage<DoubleImageType>(
93   DoubleImageType *image,
94   bool FlipVertical,
95   std::string Description);
96 
97 template< >
AddImage(UnsignedCharImageType * image,bool FlipVertical,std::string Description)98 void ITKVtkGlue_EXPORT QuickView::AddImage<UnsignedCharImageType>(
99   UnsignedCharImageType *image,
100   bool FlipVertical,
101   std::string Description)
102 {
103   if (FlipVertical)
104     {
105     using FlipFilterType = itk::FlipImageFilter< UnsignedCharImageType>;
106     FlipFilterType::Pointer flipper = FlipFilterType::New();
107     bool flipAxes[3] = { false, true, false };
108     flipper = FlipFilterType::New();
109     flipper->SetFlipAxes(flipAxes);
110     flipper->SetInput(image);
111     flipper->Update();
112     ImageInfo myImage(flipper->GetOutput(), Description);
113     this->Images.push_back(myImage);
114     }
115   else
116     {
117     ImageInfo myImage(image, Description);
118     this->Images.push_back(myImage);
119     }
120 }
121 
122 template<typename TImage>
AddImage(TImage * image,bool FlipVertical,std::string Description)123 void ITKVtkGlue_EXPORT QuickView::AddImage(
124   TImage *image,
125   bool FlipVertical,
126   std::string Description)
127 {
128   using rescaleFilterType =
129       itk::RescaleIntensityImageFilter<TImage, UnsignedCharImageType >;
130 
131   typename rescaleFilterType::Pointer rescaler = rescaleFilterType::New();
132   rescaler->SetOutputMinimum(0);
133   rescaler->SetOutputMaximum(255);
134   rescaler->SetInput(image);
135   rescaler->Update();
136 
137   this->AddImage(rescaler->GetOutput(), FlipVertical, Description);
138 }
139 
140 template< >
AddImage(UnsignedCharRGBImageType * image,bool FlipVertical,std::string Description)141 void ITKVtkGlue_EXPORT QuickView::AddImage<UnsignedCharRGBImageType>(
142   UnsignedCharRGBImageType *image,
143   bool FlipVertical,
144   std::string Description)
145 {
146   if (FlipVertical)
147     {
148     using FlipFilterType = itk::FlipImageFilter< UnsignedCharRGBImageType>;
149     FlipFilterType::Pointer flipper = FlipFilterType::New();
150     bool flipAxes[3] = { false, true, false };
151     flipper = FlipFilterType::New();
152     flipper->SetFlipAxes(flipAxes);
153     flipper->SetInput(image);
154     flipper->Update();
155     RGBImageInfo myImage(flipper->GetOutput(), Description);
156     this->RGBImages.push_back(myImage);
157     }
158   else
159     {
160     RGBImageInfo myImage(image, Description);
161     this->RGBImages.push_back(myImage);
162     }
163 }
164 
165 template< >
AddRGBImage(UnsignedCharRGBImageType * image,bool FlipVertical,std::string Description)166 void ITKVtkGlue_EXPORT QuickView::AddRGBImage<UnsignedCharRGBImageType>(
167   UnsignedCharRGBImageType *image,
168   bool FlipVertical,
169   std::string Description)
170 {
171   if (FlipVertical)
172     {
173     using FlipFilterType = itk::FlipImageFilter< UnsignedCharRGBImageType>;
174     FlipFilterType::Pointer flipper = FlipFilterType::New();
175     bool flipAxes[3] = { false, true, false };
176     flipper = FlipFilterType::New();
177     flipper->SetFlipAxes(flipAxes);
178     flipper->SetInput(image);
179     flipper->Update();
180     RGBImageInfo myImage(flipper->GetOutput(), Description);
181     this->RGBImages.push_back(myImage);
182     }
183   else
184     {
185     RGBImageInfo myImage(image, Description);
186     this->RGBImages.push_back(myImage);
187     }
188 }
189 
190 template< >
AddRGBImage(FloatRGBImageType * image,bool FlipVertical,std::string Description)191 void ITKVtkGlue_EXPORT QuickView::AddRGBImage<FloatRGBImageType>(
192   FloatRGBImageType *image,
193   bool FlipVertical,
194   std::string Description)
195 {
196   using AdaptorType = itk::RGBToVectorImageAdaptor<FloatRGBImageType>;
197   AdaptorType::Pointer adaptor = AdaptorType::New();
198   adaptor->SetImage(image);
199 
200   using rescaleFilterType = itk::VectorRescaleIntensityImageFilter<AdaptorType, UnsignedCharRGBImageType >;
201   rescaleFilterType::Pointer rescaler = rescaleFilterType::New();
202   rescaler->SetOutputMaximumMagnitude(255);
203   rescaler->SetInput(adaptor);
204   rescaler->Update();
205   this->AddRGBImage(rescaler->GetOutput(), FlipVertical, Description);
206 }
207 
208 template< >
AddImage(FloatRGBImageType * image,bool FlipVertical,std::string Description)209 void ITKVtkGlue_EXPORT QuickView::AddImage<FloatRGBImageType>(
210   FloatRGBImageType *image,
211   bool FlipVertical,
212   std::string Description)
213 {
214   using AdaptorType = itk::RGBToVectorImageAdaptor<FloatRGBImageType>;
215   AdaptorType::Pointer adaptor = AdaptorType::New();
216   adaptor->SetImage(image);
217 
218   using rescaleFilterType = itk::VectorRescaleIntensityImageFilter<AdaptorType, UnsignedCharRGBImageType >;
219   rescaleFilterType::Pointer rescaler = rescaleFilterType::New();
220   rescaler->SetOutputMaximumMagnitude(255);
221   rescaler->SetInput(adaptor);
222   rescaler->Update();
223   this->AddRGBImage(rescaler->GetOutput(), FlipVertical, Description);
224 }
225 
Visualize(bool interact)226 void QuickView::Visualize(bool interact)
227 {
228   unsigned int rendererSize = this->m_ViewPortSize;
229   unsigned int numberOfImages = this->Images.size() + this->RGBImages.size();
230   unsigned int numberOfRows = (numberOfImages - 1) / this->m_NumberOfColumns + 1;
231   unsigned int numberOfColumns = numberOfImages / (numberOfRows) + 1;
232   if (numberOfColumns > numberOfImages)
233     {
234     numberOfColumns = numberOfImages;
235     }
236 
237   // Setup the render window and interactor
238   vtkSmartPointer<vtkRenderWindow> renderWindow =
239     vtkSmartPointer<vtkRenderWindow>::New();
240   renderWindow->SetSize(rendererSize * numberOfColumns,
241                         rendererSize * numberOfRows);
242 
243   vtkSmartPointer<vtkRenderWindowInteractor> interactor =
244     vtkSmartPointer<vtkRenderWindowInteractor>::New();
245   interactor->SetRenderWindow(renderWindow);
246 
247   // Render all of the images
248   std::vector<double*> viewports;
249 
250   using ConnectorType =
251       itk::ImageToVTKImageFilter<itk::Image<unsigned char, 2> >;
252   using RGBConnectorType =
253       itk::ImageToVTKImageFilter<itk::Image<itk::RGBPixel<unsigned char>, 2> >;
254   std::vector<ConnectorType::Pointer>    connectors; // Force the connectors to persist (not lose scope) after each iteration of the loop
255   std::vector<RGBConnectorType::Pointer> RGBconnectors; // Force the connectors to persist after each iteration of the loop
256 
257   double background[6] = {.4, .5, .6, .6, .5, .4};
258 
259   vtkSmartPointer<vtkCamera> sharedCamera =
260     vtkSmartPointer<vtkCamera>::New();
261 
262   for(unsigned int row = 0; row < numberOfRows; ++row)
263     {
264     for(unsigned int col = 0; col < numberOfColumns; ++col)
265       {
266       size_t i = row * numberOfColumns + col;
267       // (xmin, ymin, xmax, ymax)
268       double viewport[4] =
269         {static_cast<double>(col) * rendererSize / (numberOfColumns * rendererSize),
270          static_cast<double>(numberOfRows - (row+1)) * rendererSize / (numberOfRows * rendererSize),
271          static_cast<double>(col+1)*rendererSize / (numberOfColumns * rendererSize),
272          static_cast<double>(numberOfRows - row) * rendererSize / (numberOfRows * rendererSize)};
273       viewports.push_back(viewport);
274 
275       // Grayscale images
276       if (i < this->Images.size())
277         {
278         ConnectorType::Pointer connector = ConnectorType::New();
279         connectors.push_back(connector);
280         connector->SetInput(this->Images[i].m_Image);
281 
282         vtkSmartPointer<vtkImageActor> actor =
283           vtkSmartPointer<vtkImageActor>::New();
284 #if VTK_MAJOR_VERSION <= 5
285         actor->SetInput(connector->GetOutput());
286 #else
287         connector->Update();
288         actor->GetMapper()->SetInputData(connector->GetOutput());
289 #endif
290 
291         // Setup renderer
292         vtkSmartPointer<vtkRenderer> renderer =
293           vtkSmartPointer<vtkRenderer>::New();
294         renderWindow->AddRenderer(renderer);
295         renderer->SetViewport(viewports[i]);
296         renderer->SetBackground(background);
297         if (m_ShareCamera)
298           {
299           renderer->SetActiveCamera(sharedCamera);
300           }
301         else
302           {
303           vtkSmartPointer<vtkCamera> aCamera =
304             vtkSmartPointer<vtkCamera>::New();
305           renderer->SetActiveCamera(aCamera);
306           }
307         std::rotate(background, background + 1, background + 6);
308 
309         if (this->Images[i].m_Description != "")
310           {
311           vtkSmartPointer<vtkTextProperty> textProperty =
312             vtkSmartPointer<vtkTextProperty>::New();
313           textProperty->SetFontSize(10);
314           textProperty->SetFontFamilyToCourier();
315           textProperty->SetJustificationToCentered();
316 
317           vtkSmartPointer<vtkTextMapper> textMapper =
318             vtkSmartPointer<vtkTextMapper>::New();
319           textMapper->SetTextProperty(textProperty);
320           textMapper->SetInput(this->Images[i].m_Description.c_str());
321 
322           vtkSmartPointer<vtkActor2D> textActor =
323             vtkSmartPointer<vtkActor2D>::New();
324           textActor->SetMapper(textMapper);
325           textActor->SetPosition(rendererSize/2, 16);
326           renderer->AddActor(textActor);
327           }
328         if (m_Interpolate)
329           {
330           actor->InterpolateOn();
331           }
332         else
333           {
334           actor->InterpolateOff();
335           }
336         renderer->AddActor(actor);
337         renderer->ResetCamera();
338         }
339       // RGB Images
340       else if (i >= this->Images.size() && i < numberOfImages)
341         {
342         unsigned int j = row * numberOfColumns + col - this->Images.size();
343         RGBConnectorType::Pointer connector = RGBConnectorType::New();
344         RGBconnectors.push_back(connector);
345         connector->SetInput(this->RGBImages[j].m_Image);
346 
347         vtkSmartPointer<vtkImageActor> actor =
348           vtkSmartPointer<vtkImageActor>::New();
349 #if VTK_MAJOR_VERSION <= 5
350         actor->SetInput(connector->GetOutput());
351 #else
352         connector->Update();
353         actor->GetMapper()->SetInputData(connector->GetOutput());
354 #endif
355 
356         // Setup renderer
357         vtkSmartPointer<vtkRenderer> renderer =
358           vtkSmartPointer<vtkRenderer>::New();
359         renderWindow->AddRenderer(renderer);
360         renderer->SetViewport(viewports[i]);
361         renderer->SetBackground(background);
362         if (m_ShareCamera)
363           {
364           renderer->SetActiveCamera(sharedCamera);
365           }
366         else
367           {
368           vtkSmartPointer<vtkCamera> aCamera =
369             vtkSmartPointer<vtkCamera>::New();
370           renderer->SetActiveCamera(aCamera);
371           }
372         std::rotate(background, background + 1, background + 6);
373 
374         if (this->RGBImages[j].m_Description != "")
375           {
376           vtkSmartPointer<vtkTextProperty> textProperty =
377             vtkSmartPointer<vtkTextProperty>::New();
378           textProperty->SetFontSize(10);
379           textProperty->SetFontFamilyToCourier();
380           textProperty->SetJustificationToCentered();
381 
382           vtkSmartPointer<vtkTextMapper> textMapper =
383             vtkSmartPointer<vtkTextMapper>::New();
384           textMapper->SetTextProperty(textProperty);
385           textMapper->SetInput(this->RGBImages[j].m_Description.c_str());
386 
387           vtkSmartPointer<vtkActor2D> textActor =
388             vtkSmartPointer<vtkActor2D>::New();
389           textActor->SetMapper(textMapper);
390           textActor->SetPosition(rendererSize/2, 16);
391           renderer->AddActor(textActor);
392           }
393 
394         if (m_Interpolate)
395           {
396           actor->InterpolateOn();
397           }
398         else
399           {
400           actor->InterpolateOff();
401           }
402         renderer->AddActor(actor);
403         renderer->ResetCamera();
404         }
405       else
406         {
407         // Fill empty viewports
408         vtkSmartPointer<vtkRenderer> renderer =
409           vtkSmartPointer<vtkRenderer>::New();
410         renderWindow->AddRenderer(renderer);
411         renderer->SetViewport(viewports[i]);
412         renderer->SetBackground(background);
413         continue;
414         }
415       }
416     }
417     renderWindow->Render();
418 
419     if( m_Snapshot )
420     {
421     std::string filename;
422     std::stringstream temp;
423     temp << m_SnapshotPath << m_SnapshotPrefix << m_Counter << ".";
424     filename = temp.str();
425     filename.append( m_SnapshotExtension );
426 
427     if( m_SnapshotExtension == "png" )
428       {
429       vtkCaptureScreen< vtkPNGWriter > capture( renderWindow );
430       capture( filename );
431       }
432     if( ( m_SnapshotExtension == "jpg" ) || ( m_SnapshotExtension == "jpeg" ) )
433       {
434       vtkCaptureScreen< vtkJPEGWriter > capture( renderWindow );
435       capture( filename );
436       }
437     if( m_SnapshotExtension == "bmp" )
438       {
439       vtkCaptureScreen< vtkBMPWriter > capture( renderWindow );
440       capture( filename );
441       }
442     if( ( m_SnapshotExtension == "tif" ) || ( m_SnapshotExtension == "tiff" ) )
443       {
444       vtkCaptureScreen< vtkTIFFWriter > capture( renderWindow );
445       capture( filename );
446       }
447     m_Counter++;
448     }
449 
450   vtkSmartPointer<vtkInteractorStyleImage> style =
451     vtkSmartPointer<vtkInteractorStyleImage>::New();
452   interactor->SetInteractorStyle(style);
453 
454   if (interact)
455     {
456     interactor->Start();
457     }
458 }
459