From aec8abdf5fb2069e0af3db0c66e8ae3fd26033f9 Mon Sep 17 00:00:00 2001
From: Julien Michel <julien.michel@c-s.fr>
Date: Fri, 16 Feb 2007 17:15:28 +0000
Subject: [PATCH] Nouvelle visu.

---
 Code/Visu/otbImageViewer.h              | 165 ++++++++
 Code/Visu/otbImageViewer.txx            | 528 ++++++++++++++++++++++++
 Code/Visu/otbImageViewerScrollWidget.h  | 138 +++++++
 Code/Visu/otbImageViewerZoomWidget.h    | 157 +++++++
 Testing/Code/Visu/otbImageViewer.cxx    |  66 +++
 Testing/Code/Visu/otbImageViewerNew.cxx |  47 +++
 6 files changed, 1101 insertions(+)
 create mode 100644 Code/Visu/otbImageViewer.h
 create mode 100644 Code/Visu/otbImageViewer.txx
 create mode 100644 Code/Visu/otbImageViewerScrollWidget.h
 create mode 100644 Code/Visu/otbImageViewerZoomWidget.h
 create mode 100644 Testing/Code/Visu/otbImageViewer.cxx
 create mode 100644 Testing/Code/Visu/otbImageViewerNew.cxx

diff --git a/Code/Visu/otbImageViewer.h b/Code/Visu/otbImageViewer.h
new file mode 100644
index 0000000000..0b4b1798a6
--- /dev/null
+++ b/Code/Visu/otbImageViewer.h
@@ -0,0 +1,165 @@
+/*=========================================================================
+
+Program:   ORFEO Toolbox
+Language:  C++
+Date:      $Date$
+Version:   $Revision$
+
+
+Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+See OTBCopyright.txt for details.
+
+
+This software is distributed WITHOUT ANY WARRANTY; without even 
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef __otbImageViewer_h
+#define __otbImageViewer_h
+
+#include <string>
+#include "itkObject.h"
+#include "otbImageViewerScrollWidget.h"
+#include "otbImageViewerZoomWidget.h"
+#include "otbImageViewerFullWidget.h"
+#include "otbStreamingShrinkImageFilter.h"
+#include "otbImageWidgetBoxForm.h"
+#include "itkListSample.h"
+#include "itkCovarianceCalculator.h"
+#include "itkMacro.h"
+#include <FL/Fl_Output.H>
+
+
+namespace otb
+{
+  /** 
+   *
+   */
+template <class TPixel>
+class ITK_EXPORT ImageViewer 
+  : public itk::ProcessObject            
+{
+ public:
+  /** Standard class typedefs */
+  typedef ImageViewer    Self;
+  typedef itk::ProcessObject                 Superclass;
+  typedef itk::SmartPointer<Self>            Pointer;
+  typedef itk::SmartPointer<const Self>      ConstPointer;
+
+  /** Method for creation through the object factory. */
+  itkNewMacro(Self);
+
+  /** Run-time type information (and related methods). */
+  itkTypeMacro(ImageViewer,itk::ProcessObject);
+  
+  /// Template pixel type
+  typedef TPixel InputPixelType;
+  typedef otb::ImageViewerScrollWidget<InputPixelType> ScrollWidgetType;
+  typedef otb::ImageViewerZoomWidget<InputPixelType> ZoomWidgetType;
+  typedef otb::ImageViewerFullWidget<InputPixelType> FullWidgetType;
+
+  typedef typename ScrollWidgetType::ImageType ImageType;
+  typedef typename ImageType::Pointer ImagePointerType;
+  typedef typename ImageType::IndexType IndexType;
+  typedef typename ImageType::SizeType SizeType;
+  typedef typename ImageType::PixelType PixelType;
+  typedef typename ImageType::RegionType RegionType;
+  typedef typename ScrollWidgetType::VectorPixelType VectorPixelType;
+  typedef typename ScrollWidgetType::Pointer ScrollWidgetPointerType;
+  typedef typename ZoomWidgetType::Pointer ZoomWidgetPointerType;
+  typedef typename FullWidgetType::Pointer FullWidgetPointerType;
+
+
+  typedef itk::Vector<InputPixelType,3> MeasurementVectorType;
+  typedef itk::Statistics::ListSample<VectorPixelType> ListSampleType;
+  typedef itk::Statistics::CovarianceCalculator<ListSampleType> CovarianceCalculatorType;
+
+  typedef otb::StreamingShrinkImageFilter<ImageType,ImageType> ShrinkFilterType;
+  typedef typename ShrinkFilterType::Pointer ShrinkFilterPointerType;
+
+  typedef otb::ImageWidgetBoxForm BoxType;
+  typedef typename BoxType::Pointer BoxPointerType;
+  typedef typename BoxType::ColorType ColorType;
+  
+  itkGetMacro(Built,bool);
+  itkGetMacro(ShrinkFactor,unsigned int);
+  itkSetMacro(RedChannelIndex,unsigned int);
+  itkGetMacro(RedChannelIndex,unsigned int);
+  itkSetMacro(GreenChannelIndex,unsigned int);
+  itkGetMacro(GreenChannelIndex,unsigned int);
+  itkSetMacro(BlueChannelIndex,unsigned int);
+  itkGetMacro(BlueChannelIndex,unsigned int);
+ 
+
+   /** Set the input image */
+  virtual void SetImage(itk::ImageBase<2> * img);
+  /** Show the viewer (Update) */
+  virtual void Show(void);
+
+  virtual void ComputeNormalizationFactors(void);
+
+  void Build(void);
+  virtual void Update(void);
+
+  virtual void UpdateFullWidget(void);
+  virtual void UpdateScrollWidget(void);
+  virtual void UpdateZoomWidget(void);
+
+  virtual void ChangeZoomViewedRegion(IndexType clickedIndex);
+  virtual void ChangeFullViewedRegion(IndexType clickedIndex);
+  virtual RegionType ComputeConstrainedRegion(RegionType smallRegion, RegionType bigRegion);
+
+  virtual void PrintPixLocVal(std::string str);
+
+protected:
+  // Constructor and destructor
+  ImageViewer();
+  ~ImageViewer();
+ 
+  /** Hide all Image View Windows */
+  virtual void Hide(void);
+ 		
+ private:
+  /// zoom widget component
+  ZoomWidgetPointerType m_ZoomWidget;
+  /// scroll widget component
+  ScrollWidgetPointerType m_ScrollWidget;
+  /// full widget component
+  FullWidgetPointerType m_FullWidget;
+  /// The image to view
+  ImagePointerType m_InputImage;
+  bool m_UseScroll;
+  unsigned int m_ScrollMaxInitialSize;
+  unsigned int m_FullMaxInitialSize;
+  unsigned m_ZoomMaxInitialSize;
+  double m_ImageGeometry;
+  unsigned int m_ScrollLimitSize;
+  Fl_Window * m_FullWindow;
+  Fl_Window * m_ScrollWindow;
+  Fl_Window * m_ZoomWindow;
+  Fl_Window * m_PixLocWindow;
+  Fl_Output * m_PixLocOutput;
+
+  ShrinkFilterPointerType m_Shrink;
+  unsigned int m_ShrinkFactor;
+  ColorType m_Color;
+  bool m_Built;
+
+  VectorPixelType m_MinComponentValue;
+  VectorPixelType m_MaxComponentValue;
+
+  unsigned int m_RedChannelIndex;
+  unsigned int m_GreenChannelIndex;
+  unsigned int m_BlueChannelIndex;
+
+};
+
+
+} // end namespace otb
+
+#ifndef OTB_MANUAL_INSTANTIATION
+#include "otbImageViewer.txx"
+#endif
+
+#endif
diff --git a/Code/Visu/otbImageViewer.txx b/Code/Visu/otbImageViewer.txx
new file mode 100644
index 0000000000..b2791dbe1e
--- /dev/null
+++ b/Code/Visu/otbImageViewer.txx
@@ -0,0 +1,528 @@
+/*=========================================================================
+
+Program:   ORFEO Toolbox
+Language:  C++
+Date:      $Date$
+Version:   $Revision$
+
+
+Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+See OTBCopyright.txt for details.
+
+
+This software is distributed WITHOUT ANY WARRANTY; without even 
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef _otbImageViewer_txx
+#define _otbImageViewer_txx
+
+
+#include "otbImageViewer.h"
+#include "otbFltkFilterWatcher.h"
+#include "otbMacro.h"
+#include "itkMacro.h"
+#include <sstream>
+#include "itkImageRegionIterator.h"
+
+namespace otb
+{
+  /// Constructor
+  template <class TPixel>
+  ImageViewer<TPixel>
+  ::ImageViewer()
+  {
+    m_UseScroll = false;
+    m_ScrollMaxInitialSize = 300;
+    m_FullMaxInitialSize = 600;
+    m_ZoomMaxInitialSize = 200;
+    m_ImageGeometry = 1.0;
+    m_ScrollLimitSize = 600;
+    m_Color[0]=1.0;
+    m_Color[1]=0;
+    m_Color[2]=0;
+    m_Color[3]=1.0;
+    m_ShrinkFactor=1;
+    m_RedChannelIndex = 0;
+    m_GreenChannelIndex = 1;
+    m_BlueChannelIndex = 2;
+    m_Built=false;   
+    m_Shrink = ShrinkFilterType::New();  
+    m_FullWindow=NULL;
+    m_ZoomWindow=NULL;
+    m_ScrollWindow=NULL;
+    m_PixLocWindow=NULL;
+    m_PixLocOutput=NULL;
+  }
+  /// Destructor
+  template <class TPixel>
+  ImageViewer<TPixel>
+  ::~ImageViewer()
+  {
+    if (m_FullWindow!=NULL)
+      delete m_FullWindow;
+    if(m_ZoomWindow!=NULL)
+      delete m_ZoomWindow;
+    if(m_ScrollWindow!=NULL)
+      delete m_ScrollWindow;
+    if(m_PixLocWindow!=NULL)
+      delete m_PixLocWindow;
+    if(m_PixLocOutput!=NULL)
+      delete m_PixLocOutput;
+  }
+
+  /// Compute the normalization factor
+  template <class TPixel>
+  void
+  ImageViewer<TPixel>
+  ::ComputeNormalizationFactors(void)
+  {
+    typedef itk::ImageRegionIterator<ImageType> IteratorType;
+    typename ListSampleType::Pointer listSample = ListSampleType::New();
+    unsigned int nbComponents = m_InputImage->GetNumberOfComponentsPerPixel();
+    listSample->SetMeasurementVectorSize(nbComponents);
+    m_MinComponentValue.SetSize(nbComponents);
+    m_MaxComponentValue.SetSize(nbComponents);
+    VectorPixelType absolutMax;
+    VectorPixelType absolutMin;
+    absolutMax.SetSize(nbComponents);
+    absolutMin.SetSize(nbComponents);
+    absolutMax.Fill(0);
+    absolutMin.Fill(0);
+    IteratorType it;
+    // if scroll is activated, compute the factors from the quicklook
+    if(m_UseScroll)
+      {
+	it = IteratorType(m_Shrink->GetOutput(),m_Shrink->GetOutput()->GetLargestPossibleRegion());
+	it.GoToBegin();
+      }
+    // else, compute the factors from the full viewed region
+    else
+      {
+	m_InputImage->SetRequestedRegion(m_FullWidget->GetViewedRegion());
+	m_InputImage->PropagateRequestedRegion();
+	m_InputImage->UpdateOutputData();
+	it = IteratorType(m_InputImage,m_FullWidget->GetViewedRegion());
+	it.GoToBegin();
+      }
+    while(!it.IsAtEnd())
+      {
+	listSample->PushBack(it.Get()); 
+	for(unsigned int i = 0; i<nbComponents;++i)
+	  {
+	    if(it.Get()[i]>absolutMax[i])
+	      absolutMax[i]=it.Get()[i];
+	    if(it.Get()[i]<absolutMin[i])
+	      absolutMin[i]=it.Get()[i];
+	  } 
+	++it;
+      }
+    otbMsgDebugMacro(<<"Sample list generated.");	       
+    typename CovarianceCalculatorType::Pointer calc = CovarianceCalculatorType::New();
+    calc->SetInputSample(listSample);
+    calc->Update();
+    otbMsgDebugMacro(<<"Statistics computed.");
+    typename CovarianceCalculatorType::OutputType cov = *(calc->GetOutput());
+    for(unsigned int i = 0; i<nbComponents;++i)
+      {
+	m_MinComponentValue[i] = static_cast<InputPixelType>((calc->GetMean())->GetElement(i)-2.8*vcl_sqrt(cov(i,i)));
+	m_MaxComponentValue[i] = static_cast<InputPixelType>((calc->GetMean())->GetElement(i)+2.8*vcl_sqrt(cov(i,i)));
+	if(m_MinComponentValue[i]<absolutMin[i])
+	  m_MinComponentValue[i]=absolutMin[i];
+	if(m_MaxComponentValue[i]>absolutMax[i])
+	  m_MaxComponentValue[i]=absolutMax[i];
+      }
+    
+   //  InputPixelType min,max;
+//     max = (m_MaxComponentValue[m_RedChannelIndex]
+// 	   +m_MaxComponentValue[m_GreenChannelIndex]
+// 	   +m_MaxComponentValue[m_BlueChannelIndex])/2;
+//     min = (m_MinComponentValue[m_RedChannelIndex]
+// 	   +m_MinComponentValue[m_GreenChannelIndex]
+// 	   +m_MinComponentValue[m_BlueChannelIndex])/2;
+
+
+    // otbMsgDebugMacro(<<"Normalization between: "<<m_MinComponentValue<<" and "<<m_MaxComponentValue);  
+//     for(unsigned int i = 1; i<nbComponents;++i)
+//       {
+// 	if(min>m_MinComponentValue[i])
+// 	  min=m_MinComponentValue[i];
+// 	if(max<m_MaxComponentValue[i])
+// 	  max=m_MaxComponentValue[i];
+//       }
+
+   //  m_MinComponentValue.Fill(min);
+//     m_MaxComponentValue.Fill(max);
+
+    otbMsgDebugMacro(<<"Data min: "<<absolutMin<<", Data max: "<<absolutMax);
+    otbMsgDebugMacro(<<"Normalization between: "<<m_MinComponentValue<<" and "<<m_MaxComponentValue);  
+  }
+  
+  
+  /// Build the HMI
+  template <class TPixel>
+  void
+  ImageViewer<TPixel>
+  ::Build(void)
+  {
+    otbMsgDebugMacro(<<"Entering build method");
+    if(!m_InputImage)
+      {
+	itkExceptionMacro(<<"No input image !");
+      } 
+    // Get the image dimension
+    typename ImageType::SizeType size = m_InputImage->GetLargestPossibleRegion().GetSize();
+    m_ImageGeometry = static_cast<double>(size[0])/static_cast<double>(size[1]);
+    
+    // initiate windows dimensions
+    int wscroll=0;
+    int hscroll=0;
+    int wfull = (size[0]<m_FullMaxInitialSize ? size[0] : m_FullMaxInitialSize);
+    int hfull = (size[1]<m_FullMaxInitialSize ? size[1] : m_FullMaxInitialSize);
+
+    // Create full windows
+    m_FullWindow = new Fl_Window(0,0,wfull,hfull,"Full Resolution Window");
+    m_FullWidget = FullWidgetType::New();
+    m_FullWindow->resizable(m_FullWidget);
+    m_FullWidget->SetParent(this);
+    m_FullWindow->size_range(0,0,size[0],size[1]);
+    m_FullWindow->end();
+    m_FullWidget->SetInput(m_InputImage);
+    m_FullWidget->Init(0,0,wfull,hfull,"Full Resolution Window");
+    m_FullWidget->box( FL_EMBOSSED_BOX );
+    m_FullWidget->SetFormOverlayVisible(true);
+    
+    // decide wether to use scroll view or not
+    if(size[0]<m_ScrollLimitSize&&size[1]<m_ScrollLimitSize)
+      {
+	m_UseScroll=false;
+      }
+    else 
+      {
+	m_UseScroll=true;
+	// Compute scroll size :
+	if(m_ImageGeometry<1)
+	  {
+	    hscroll = m_ScrollMaxInitialSize;
+	    wscroll = static_cast<int>(static_cast<double>(m_ScrollMaxInitialSize)*m_ImageGeometry);
+	  }
+	else
+	  {
+	    wscroll = m_ScrollMaxInitialSize;
+	    hscroll = static_cast<int>(static_cast<double>(m_ScrollMaxInitialSize)/m_ImageGeometry);
+	  }
+	// Create the quicklook
+	m_Shrink->SetInput(m_InputImage);
+	m_ShrinkFactor = (size[0]/hscroll < size[1]/wscroll ? size[0]/hscroll : size[1]/wscroll)/2;
+	otbMsgDebugMacro("Shrink factor: "<<m_ShrinkFactor);
+	m_Shrink->SetShrinkFactor(m_ShrinkFactor);
+	typedef otb::FltkFilterWatcher WatcherType;
+	WatcherType watcher(m_Shrink,wfull-200,hfull/2,200,20, "Generating Quicklook ...");
+	m_Shrink->Update();
+	
+	// Create the scroll windows
+	m_ScrollWindow = new Fl_Window(wfull+15,0,wscroll,hscroll,"Scroll Window");
+	m_ScrollWidget = ScrollWidgetType::New();
+	m_ScrollWindow->resizable(m_ScrollWidget);
+	m_ScrollWindow->size_range(wscroll,hscroll,size[0],size[1],0,0,1);
+	m_ScrollWindow->end(); 
+	m_ScrollWidget->SetInput(m_Shrink->GetOutput());
+	m_ScrollWidget->SetParent(this);
+	m_ScrollWidget->Init(0,0,wscroll,hscroll,"Scroll window");
+	m_ScrollWidget->box( FL_EMBOSSED_BOX );
+	m_ScrollWidget->SetFormOverlayVisible(true);
+	
+	// Create the scroll selection box
+	BoxPointerType box = BoxType::New();
+	SizeType scrollBoxSize;
+	IndexType scrollBoxIndex;
+	scrollBoxSize[0]=(m_FullWidget->GetViewedRegion().GetSize()[0]/m_ShrinkFactor)+1;
+	scrollBoxSize[1]=(m_FullWidget->GetViewedRegion().GetSize()[1]/m_ShrinkFactor)+1;
+	scrollBoxIndex[0]=(m_FullWidget->GetViewedRegion().GetIndex()[0]/m_ShrinkFactor)+1;
+	scrollBoxIndex[1]=(m_FullWidget->GetViewedRegion().GetIndex()[1]/m_ShrinkFactor)+1;
+	otbMsgDebugMacro(<<"Scroll box: "<<scrollBoxIndex<<" "<<scrollBoxSize);
+	box->SetSize(scrollBoxSize);
+	box->SetIndex(scrollBoxIndex);
+	box->SetColor(m_Color);
+	m_ScrollWidget->GetFormList()->PushBack(box);
+	
+	// Set the view model
+	if(m_InputImage->GetNumberOfComponentsPerPixel()<=2)
+	  {
+		m_ScrollWidget->SetViewModelToGrayscale();
+		 m_ScrollWidget->SetRedChannelIndex(m_RedChannelIndex);
+	  }
+	else
+	  {
+	    m_ScrollWidget->SetRedChannelIndex(m_RedChannelIndex);
+	    m_ScrollWidget->SetGreenChannelIndex(m_GreenChannelIndex);
+	    m_ScrollWidget->SetBlueChannelIndex(m_BlueChannelIndex);
+	  }
+      }
+    // Create the zoom window
+    m_ZoomWindow = new Fl_Window(wfull+15,hscroll+45,m_ZoomMaxInitialSize,m_ZoomMaxInitialSize,"Zoom Window");
+    m_ZoomWidget = ZoomWidgetType::New();
+    m_ZoomWidget->SetParent(this);
+    m_ZoomWindow->resizable(m_ZoomWidget);
+    m_ZoomWindow->size_range(0,0,size[0],size[1]);
+    m_ZoomWindow->end();
+    m_ZoomWidget->SetZoomFactor(4.0);
+    m_ZoomWidget->SetInput(m_InputImage);
+    m_ZoomWidget->Init(0,0,m_ZoomMaxInitialSize,m_ZoomMaxInitialSize,"Zoom Window");
+    m_ZoomWidget->box( FL_EMBOSSED_BOX );
+    m_ZoomWidget->SetFormOverlayVisible(true);
+
+    // Create the zoom selection mode
+    BoxPointerType zoomBox = BoxType::New();
+    SizeType zoomBoxSize;
+    IndexType zoomBoxIndex;
+    zoomBoxSize[0]=(m_ZoomWidget->GetViewedRegion().GetSize()[0])+1;
+    zoomBoxSize[1]=(m_ZoomWidget->GetViewedRegion().GetSize()[1])+1;
+    zoomBoxIndex[0]=(m_ZoomWidget->GetViewedRegion().GetIndex()[0])+1;
+    zoomBoxIndex[1]=(m_ZoomWidget->GetViewedRegion().GetIndex()[1])+1;
+    zoomBox->SetIndex(zoomBoxIndex);
+    zoomBox->SetSize(zoomBoxSize);
+    zoomBox->SetColor(m_Color);
+    m_FullWidget->GetFormList()->PushBack(zoomBox);
+
+    // Set the view model
+    if(m_InputImage->GetNumberOfComponentsPerPixel()<=2)
+      {
+	m_FullWidget->SetViewModelToGrayscale();
+	m_ZoomWidget->SetViewModelToGrayscale();
+	otbMsgDebugMacro(<<"View model set to grayscale. Channel: "<<m_RedChannelIndex);
+	m_ZoomWidget->SetRedChannelIndex(m_RedChannelIndex);
+	m_FullWidget->SetRedChannelIndex(m_RedChannelIndex);
+      }
+    else
+      {
+	otbMsgDebugMacro(<<"View model set to RGB Composition.  R: "<<m_RedChannelIndex<<", G: "<<m_GreenChannelIndex<<", B: "<<m_BlueChannelIndex);
+	m_ZoomWidget->SetRedChannelIndex(m_RedChannelIndex);
+	m_ZoomWidget->SetGreenChannelIndex(m_GreenChannelIndex);
+	m_ZoomWidget->SetBlueChannelIndex(m_BlueChannelIndex);
+	m_FullWidget->SetRedChannelIndex(m_RedChannelIndex);
+	m_FullWidget->SetGreenChannelIndex(m_GreenChannelIndex);
+	m_FullWidget->SetBlueChannelIndex(m_BlueChannelIndex);
+
+      }
+ 
+    // Compute the normalization factors
+    ComputeNormalizationFactors();
+
+    // Set the normalization factors
+    m_ZoomWidget->SetMinComponentValues(m_MinComponentValue);
+    m_ZoomWidget->SetMaxComponentValues(m_MaxComponentValue);
+    m_FullWidget->SetMinComponentValues(m_MinComponentValue);
+    m_FullWidget->SetMaxComponentValues(m_MaxComponentValue);
+    if(m_UseScroll)
+      {
+	m_ScrollWidget->SetMinComponentValues(m_MinComponentValue);
+	m_ScrollWidget->SetMaxComponentValues(m_MaxComponentValue);
+      }
+
+     m_PixLocWindow= new Fl_Window(0,hfull+15,250,30,"Pixel location & values");
+     m_PixLocOutput = new Fl_Output(0,0,250,30,"Pixel location & values");
+     m_PixLocWindow->resizable(m_PixLocOutput);
+     m_PixLocOutput->box(FL_EMBOSSED_BOX );
+     m_PixLocWindow->end();
+
+    // Built done
+    m_Built=true;
+    otbMsgDebugMacro(<<"Leaving build method");
+  }
+  /// Set the left image
+  template <class TPixel>
+  void
+  ImageViewer<TPixel>
+  ::SetImage(itk::ImageBase<2> * img)
+  {
+    m_InputImage = dynamic_cast<ImageType *>( img );
+  } 
+  /// Show the app
+  template <class TPixel>
+  void 
+  ImageViewer<TPixel>
+  ::Show(void)
+  {
+    otbMsgDebugMacro(<<"Entering show method.");
+    Fl::check();
+    if(m_UseScroll)
+      {
+	otbMsgDebugMacro(<<"Showing scroll widget.");
+ 	m_ScrollWindow->show();
+ 	m_ScrollWidget->Show();
+      }
+    otbMsgDebugMacro(<<"Showing full widget.");
+    m_FullWindow->show();
+    m_FullWidget->Show();
+    otbMsgDebugMacro(<<"Showing zoom widget.");
+    m_ZoomWindow->show();
+    m_ZoomWidget->Show();
+    otbMsgDebugMacro(<<"Between show and check");
+    m_PixLocWindow->show();
+    m_PixLocOutput->show();
+    Fl::check();
+    otbMsgDebugMacro(<<"Leaving Show method.");
+  }
+  /// Hide the app
+  template <class TPixel>
+  void 
+  ImageViewer<TPixel>
+  ::Hide(void)
+  {} 
+  /// Update the display
+  template <class TPixel>
+  void 
+  ImageViewer<TPixel>
+  ::Update(void)
+  {
+    Fl::check();
+    UpdateScrollWidget();
+    UpdateFullWidget();
+    UpdateZoomWidget();    
+    Fl::check();
+  }
+
+  template <class TPixel>
+  void 
+  ImageViewer<TPixel>
+  ::PrintPixLocVal(std::string str)
+  {
+    m_PixLocOutput->value(str.c_str());
+    m_PixLocOutput->redraw();
+    Fl::check();
+  }
+
+ template <class TPixel>
+  void 
+  ImageViewer<TPixel>
+  ::UpdateZoomWidget(void)
+  {
+    std::stringstream oss;
+    oss<<"Zoom Window (X"<<m_ZoomWidget->GetOpenGlIsotropicZoom()<<")";
+    m_ZoomWindow->label(oss.str().c_str());
+    m_ZoomWindow->redraw();
+    m_ZoomWidget->redraw();
+  }
+
+ template <class TPixel>
+  void 
+  ImageViewer<TPixel>
+  ::UpdateFullWidget(void)
+  {
+    BoxPointerType zoomBox = BoxType::New();
+    SizeType zoomBoxSize;
+    IndexType zoomBoxIndex;
+    zoomBoxSize[0]=(m_ZoomWidget->GetViewedRegion().GetSize()[0]);
+    zoomBoxSize[1]=(m_ZoomWidget->GetViewedRegion().GetSize()[1]);
+    zoomBoxIndex[0]=(m_ZoomWidget->GetViewedRegion().GetIndex()[0])+1;
+    zoomBoxIndex[1]=(m_ZoomWidget->GetViewedRegion().GetIndex()[1])+1;
+    zoomBox->SetIndex(zoomBoxIndex);
+    zoomBox->SetSize(zoomBoxSize);
+    zoomBox->SetColor(m_Color);
+    m_FullWidget->GetFormList()->SetNthElement(0,zoomBox);
+    m_FullWidget->redraw();
+  }
+
+   template <class TPixel>
+  void 
+  ImageViewer<TPixel>
+  ::UpdateScrollWidget(void)
+  {
+    if(m_UseScroll)
+      {
+	BoxPointerType box = BoxType::New();
+	SizeType scrollBoxSize;
+	IndexType scrollBoxIndex;
+	scrollBoxSize[0]=(m_FullWidget->GetViewedRegion().GetSize()[0]/m_ShrinkFactor)-1;
+	scrollBoxSize[1]=(m_FullWidget->GetViewedRegion().GetSize()[1]/m_ShrinkFactor)-1;
+	scrollBoxIndex[0]=(m_FullWidget->GetViewedRegion().GetIndex()[0]/m_ShrinkFactor)+1;
+	scrollBoxIndex[1]=(m_FullWidget->GetViewedRegion().GetIndex()[1]/m_ShrinkFactor)+1;
+	box->SetSize(scrollBoxSize);
+	box->SetIndex(scrollBoxIndex);
+	box->SetColor(m_Color);
+	m_ScrollWidget->GetFormList()->SetNthElement(0,box);
+	m_ScrollWidget->redraw();
+      }
+  }
+
+template <class TPixel>
+typename ImageViewer<TPixel>
+::RegionType
+ImageViewer<TPixel>
+::ComputeConstrainedRegion(RegionType smallRegion, RegionType bigRegion)
+{
+  // This function assumes that smallRegion is inside huge region
+  if(smallRegion.GetSize()[0]>bigRegion.GetSize()[0]
+     ||smallRegion.GetSize()[1]>bigRegion.GetSize()[1])
+    {
+      itkExceptionMacro("Small region not inside big region !");
+    }
+  else
+    {
+      RegionType resp;
+      IndexType index = smallRegion.GetIndex();
+      SizeType size = smallRegion.GetSize();
+      
+      if(smallRegion.GetIndex()[0]<bigRegion.GetIndex()[0])
+	{
+	  index[0]=bigRegion.GetIndex()[0];
+	}
+      if(smallRegion.GetIndex()[1]<bigRegion.GetIndex()[1])
+	{
+	  index[1]=bigRegion.GetIndex()[1];
+	}
+      if(index[0]+size[0]>=bigRegion.GetIndex()[0]+bigRegion.GetSize()[0])
+	{
+	  index[0]=bigRegion.GetIndex()[0]+bigRegion.GetSize()[0]-size[0]-2;
+	}
+      if(index[1]+size[1]>=bigRegion.GetIndex()[1]+bigRegion.GetSize()[1])
+	{
+	  index[1]=bigRegion.GetIndex()[1]+bigRegion.GetSize()[1]-size[1]-2;
+	}
+      resp.SetSize(size);
+      resp.SetIndex(index);
+      return resp;
+    }
+}
+
+template <class TPixel>
+  void 
+  ImageViewer<TPixel>
+::ChangeFullViewedRegion(IndexType clickedIndex)
+{
+  RegionType region = m_FullWidget->GetViewedRegion();
+  IndexType newIndex;
+  newIndex[0]=clickedIndex[0]-region.GetSize()[0]/2;
+  newIndex[1]=clickedIndex[1]-region.GetSize()[1]/2;
+  
+ 
+  region.SetIndex(newIndex);
+  otbMsgDebugMacro(<<"New region: "<<region);
+
+  RegionType newRegion = ComputeConstrainedRegion(region,m_InputImage->GetLargestPossibleRegion());
+  otbMsgDebugMacro(<<"Constrained region: "<<newRegion);
+  m_FullWidget->SetUpperLeftCorner(newRegion.GetIndex());
+  this->UpdateScrollWidget();
+}
+
+template <class TPixel>
+  void 
+  ImageViewer<TPixel>
+::ChangeZoomViewedRegion(IndexType clickedIndex)
+{
+  RegionType region = m_ZoomWidget->GetViewedRegion();
+  IndexType newIndex;
+  newIndex[0]=clickedIndex[0]-region.GetSize()[0]/2;
+  newIndex[1]=clickedIndex[1]-region.GetSize()[1]/2;
+ 
+  region.SetIndex(newIndex);
+  RegionType newRegion = ComputeConstrainedRegion(region,m_FullWidget->GetViewedRegion());
+  m_ZoomWidget->SetZoomUpperLeftCorner(newRegion.GetIndex());
+  m_ZoomWidget->redraw();
+  this->UpdateFullWidget();
+}
+} // end namespace otb
+#endif
+
diff --git a/Code/Visu/otbImageViewerScrollWidget.h b/Code/Visu/otbImageViewerScrollWidget.h
new file mode 100644
index 0000000000..06cdad1c5d
--- /dev/null
+++ b/Code/Visu/otbImageViewerScrollWidget.h
@@ -0,0 +1,138 @@
+/*=========================================================================
+
+Program:   ORFEO Toolbox
+Language:  C++
+Date:      $Date$
+Version:   $Revision$
+
+
+Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+See OTBCopyright.txt for details.
+
+
+This software is distributed WITHOUT ANY WARRANTY; without even 
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef _otbImageViewerScrollWidget_h
+#define _otbImageViewerScrollWidget_h
+
+#include "otbFixedSizeFullImageWidget.h"
+#include "otbImageWidgetRectangleForm.h"
+#include "otbImageWidgetBoxForm.h"
+#include "otbImageWidgetPointForm.h"
+#include <FL/Fl.H>
+
+namespace otb
+{
+template <class TPixel> class ImageViewer;
+
+
+template <class TPixel>
+class ITK_EXPORT ImageViewerScrollWidget
+  : public FixedSizeFullImageWidget<TPixel>
+{
+ public:
+  /** Standard class typedefs */
+  typedef ImageViewerScrollWidget            Self;
+  typedef FixedSizeFullImageWidget<TPixel>   Superclass;
+  typedef itk::SmartPointer<Self>            Pointer;
+  typedef itk::SmartPointer<const Self>      ConstPointer;
+
+  /** Method for creation through the object factory. */
+  itkNewMacro(Self);
+
+  /** Run-time type information (and related methods). */
+  itkTypeMacro(ImageViewerScrollWidget, FixedSizeFullImageWidget);
+
+  typedef TPixel PixelType;
+  typedef typename Superclass::IndexType IndexType;
+  typedef typename Superclass::SizeType SizeType;
+
+  typedef ImageViewer<PixelType> ParentType;
+  typedef typename ParentType::Pointer ParentPointerType;
+
+  typedef otb::ImageWidgetBoxForm BoxType;
+  typedef BoxType::ColorType ColorType;
+  
+  itkSetObjectMacro(Parent,ParentType);
+  itkGetObjectMacro(Parent,ParentType);
+  /** Handle method */
+  virtual int handle(int event)
+    {
+      switch(event)
+	{
+	case FL_PUSH:
+	  {
+	    int x = Fl::event_x();
+	    int y = Fl::event_y();
+	    IndexType clickedIndex;
+	    clickedIndex[0]=x;
+	    clickedIndex[1]=y;
+	    clickedIndex=this->WindowToImageCoordinates(clickedIndex);
+	    clickedIndex[0]=clickedIndex[0]*m_Parent->GetShrinkFactor();
+	    clickedIndex[1]=clickedIndex[1]*m_Parent->GetShrinkFactor();
+	    m_Parent->ChangeFullViewedRegion(clickedIndex);
+	    m_Parent->ChangeZoomViewedRegion(clickedIndex);
+	    return 1;
+	  }
+	case FL_ENTER:
+	  {
+	    m_MouseIn = true;
+	    return 1;
+	  }
+	case FL_LEAVE:
+	  {
+	  m_MouseIn = false;
+	  m_Parent->PrintPixLocVal("");
+	  return 1;
+	  }
+	case FL_MOVE:
+	  {
+	    m_MouseIn=true;
+	    if(m_MouseMoveCount%m_ValueUpdateFrequency==0)
+	      {
+		m_MousePos[0]=Fl::event_x();
+		m_MousePos[1]=Fl::event_y();
+		IndexType newIndex = this->WindowToImageCoordinates(m_MousePos);
+		IndexType realIndex;
+		realIndex[0]=newIndex[0]*m_Parent->GetShrinkFactor();
+		realIndex[1]=newIndex[1]*m_Parent->GetShrinkFactor();
+		std::stringstream oss;
+		oss<<"Location: "<<realIndex<<", Values:  "<<this->GetInput()->GetPixel(newIndex);
+		m_Parent->PrintPixLocVal(oss.str());
+		m_MouseMoveCount=0;
+	      }
+	  m_MouseMoveCount++;
+	  return 1;
+	  }
+	}	
+      return 0; 
+    }
+ protected:
+  /**
+   * Constructor.
+   */
+  ImageViewerScrollWidget()
+    {
+      m_MouseIn = false;
+      m_MousePos.Fill(0);
+      m_MouseMoveCount = 0;
+      m_ValueUpdateFrequency = 5;
+    };
+  /**
+   * Destructor.
+   */
+  ~ImageViewerScrollWidget(){};
+
+ private:
+  ParentPointerType m_Parent;
+  IndexType m_MousePos;
+  bool m_MouseIn;
+  unsigned int m_MouseMoveCount;
+  unsigned int  m_ValueUpdateFrequency;
+
+};
+} // end namespace otb
+#endif
diff --git a/Code/Visu/otbImageViewerZoomWidget.h b/Code/Visu/otbImageViewerZoomWidget.h
new file mode 100644
index 0000000000..ca3e425304
--- /dev/null
+++ b/Code/Visu/otbImageViewerZoomWidget.h
@@ -0,0 +1,157 @@
+/*=========================================================================
+
+Program:   ORFEO Toolbox
+Language:  C++
+Date:      $Date$
+Version:   $Revision$
+
+
+Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+See OTBCopyright.txt for details.
+
+
+This software is distributed WITHOUT ANY WARRANTY; without even 
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef _otbImageViewerZoomWidget_h
+#define _otbImageViewerZoomWidget_h
+
+#include "otbZoomableImageWidget.h"
+#include "otbImageWidgetRectangleForm.h"
+#include "otbImageWidgetBoxForm.h"
+#include "otbImageWidgetPointForm.h"
+#include <Fl/fl_draw.H>
+
+namespace otb
+{
+template <class TPixel> class ImageViewer;
+
+
+template <class TPixel>
+class ITK_EXPORT ImageViewerZoomWidget
+  : public ZoomableImageWidget<TPixel>
+{
+ public:
+  /** Standard class typedefs */
+  typedef ImageViewerZoomWidget              Self;
+  typedef ZoomableImageWidget<TPixel>   Superclass;
+  typedef itk::SmartPointer<Self>            Pointer;
+  typedef itk::SmartPointer<const Self>      ConstPointer;
+
+  /** Method for creation through the object factory. */
+  itkNewMacro(Self);
+
+  /** Run-time type information (and related methods). */
+  itkTypeMacro(ImageViewerZoomWidget, FullResolutionImageWidget);
+
+  typedef TPixel PixelType;
+  typedef typename Superclass::IndexType IndexType;
+  typedef typename Superclass::SizeType SizeType;
+
+  typedef ImageViewer<PixelType> ParentType;
+  typedef typename ParentType::Pointer ParentPointerType;
+ 
+  itkSetObjectMacro(Parent,ParentType);
+  itkGetObjectMacro(Parent,ParentType);
+  /** Handle method */
+  virtual int  handle(int event)
+    {
+      switch(event)
+	{
+	case FL_ENTER:
+	  {
+	    m_MouseIn = true;
+	    // otbMsgDebugMacro(<<"Mouse in");
+	    return 1;
+	  }
+	case FL_LEAVE:
+	  {
+	    m_MouseIn = false;
+	    m_Parent->UpdateZoomWidget();
+	    m_Parent->PrintPixLocVal("");
+	    // otbMsgDebugMacro(<<"Mouse out");
+	    return 1;
+	  }
+	case FL_MOVE:
+	  {
+	    m_MouseIn=true;
+	    if(m_MouseMoveCount%m_ValueUpdateFrequency==0)
+	      {
+		m_MousePos[0]=Fl::event_x();
+		m_MousePos[1]=Fl::event_y();
+		IndexType newIndex = this->WindowToImageCoordinates(m_MousePos);
+		std::stringstream oss;
+		oss<<"Location: "<<newIndex<<", Values:  "<<this->GetInput()->GetPixel(newIndex);
+		m_Parent->PrintPixLocVal(oss.str());
+		m_MouseMoveCount=0;
+	      }
+	    m_MouseMoveCount++;
+	    return 1;
+	  }
+	case FL_MOUSEWHEEL:
+	  {
+	    int dy = Fl::event_dy();
+	    if(dy<0)
+	    {
+	      this->SetZoomFactor(this->GetOpenGlIsotropicZoom()+m_ZoomStep);
+	    }
+	    else
+	      {
+		if(this->GetOpenGlIsotropicZoom()-m_ZoomStep>=1)
+		  {
+		    this->SetZoomFactor(this->GetOpenGlIsotropicZoom()-m_ZoomStep);
+		  }
+		else
+		  {
+		    this->SetZoomFactor(1.0);
+		  }
+	      }
+	    m_Parent->UpdateFullWidget(); 
+	    m_Parent->UpdateZoomWidget();
+	    return 1;
+	  }
+   } 
+      return 0; 
+    }
+  
+
+  virtual void resize(int x,int y, int w, int h) 
+     { 
+       Superclass::resize(x,y,w,h); 
+       if(m_Parent->GetBuilt()) 
+	 m_Parent->UpdateFullWidget(); 
+     } 
+
+
+  
+ protected:
+  /**
+   * Constructor.
+   */
+  ImageViewerZoomWidget()
+    {
+      m_ZoomStep = 0.2;
+      m_MouseIn = false;
+      m_MousePos.Fill(0);
+      m_MouseMoveCount = 0;
+      m_ValueUpdateFrequency = 5;
+    };
+  /**
+   * Destructor.
+   */
+  ~ImageViewerZoomWidget(){};
+
+ private:
+  ParentPointerType m_Parent;
+  double m_ZoomStep;
+  IndexType m_MousePos;
+  bool m_MouseIn;
+  unsigned int m_MouseMoveCount;
+  unsigned int  m_ValueUpdateFrequency;
+
+};
+
+} // end namespace otb
+#endif
diff --git a/Testing/Code/Visu/otbImageViewer.cxx b/Testing/Code/Visu/otbImageViewer.cxx
new file mode 100644
index 0000000000..bf1cfe6372
--- /dev/null
+++ b/Testing/Code/Visu/otbImageViewer.cxx
@@ -0,0 +1,66 @@
+/*=========================================================================
+
+  Program:   ORFEO Toolbox
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+
+  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+  See OTBCopyright.txt for details.
+
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#include "itkExceptionObject.h"
+#include "otbImageFileReader.h"
+#include "otbImageViewer.h"
+#include "otbMacro.h"
+
+int otbImageViewer( int argc, char * argv[] )
+{
+  try 
+    { 
+      char * filename = argv[1];
+
+      // Parse command line parameters
+      typedef double PixelType;
+      typedef otb::ImageViewer<PixelType>  ImageViewerType;
+      typedef ImageViewerType::ImageType ImageType;
+      typedef otb::ImageFileReader<ImageType> ReaderType;
+      
+      // instantiation
+      ImageViewerType::Pointer viewer = ImageViewerType::New();
+      
+      // check for input images
+      ReaderType::Pointer reader = ReaderType::New();
+      reader->SetFileName(filename);
+      reader->GenerateOutputInformation();
+      viewer->SetImage(reader->GetOutput());      
+	
+      // build the app
+      otbMsgDebugMacro(<<"Build.");
+      viewer->Build();
+      otbMsgDebugMacro(<<"Show");
+      viewer->Show();
+      viewer->Update();
+      Fl::check();
+    } 
+  catch( itk::ExceptionObject & err ) 
+    { 
+    std::cout << "Exception itk::ExceptionObject levee !" << std::endl; 
+    std::cout << err << std::endl; 
+    return EXIT_FAILURE;
+    } 
+ catch( ... ) 
+     { 
+       std::cout << "Exception levee inconnue !" << std::endl; 
+       return EXIT_FAILURE;
+     } 
+  return EXIT_SUCCESS;
+}
+
+
diff --git a/Testing/Code/Visu/otbImageViewerNew.cxx b/Testing/Code/Visu/otbImageViewerNew.cxx
new file mode 100644
index 0000000000..76754e526b
--- /dev/null
+++ b/Testing/Code/Visu/otbImageViewerNew.cxx
@@ -0,0 +1,47 @@
+/*=========================================================================
+
+  Program:   ORFEO Toolbox
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+
+  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+  See OTBCopyright.txt for details.
+
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#include "itkExceptionObject.h"
+#include "otbImageFileReader.h"
+#include "otbImageViewer.h"
+#include "otbMacro.h"
+
+int otbImageViewerNew( int argc, char * argv[] )
+{
+  try 
+    { 
+      // Parse command line parameters
+      typedef double PixelType;
+      typedef otb::ImageViewer<PixelType>  ImageViewerType;
+      // instantiation
+      ImageViewerType::Pointer viewer = ImageViewerType::New();
+    } 
+  catch( itk::ExceptionObject & err ) 
+    { 
+    std::cout << "Exception itk::ExceptionObject levee !" << std::endl; 
+    std::cout << err << std::endl; 
+    return EXIT_FAILURE;
+    } 
+ catch( ... ) 
+     { 
+       std::cout << "Exception levee inconnue !" << std::endl; 
+       return EXIT_FAILURE;
+     } 
+  return EXIT_SUCCESS;
+}
+
+
-- 
GitLab