<commit_before>/*=========================================================================

Library:   TubeTK

Copyright 2010 Kitware Inc. 28 Corporate Drive,
Clifton Park, NY, 12065, USA.

All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

=========================================================================*/

#include "tubeCLIProgressReporter.h"
#include "tubeMessage.h"

#include "itkTimeProbesCollectorBase.h"

#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"

#include "itkResampleImageFilter.h"
#include "itkLinearInterpolateImageFunction.h"
#include "itkMinimumMaximumImageFilter.h"

#include "itkSpatialObjectReader.h"
#include "itkSpatialObjectWriter.h"
#include "itkGroupSpatialObject.h"
#include "itkTubeSpatialObject.h"
#include "itkTubeSpatialObjectPoint.h"
#include "itkVesselTubeSpatialObject.h"
#include "itkVesselTubeSpatialObjectPoint.h"

#include "ComputeTubeFlyThroughImageCLP.h"

#define tubePadSize 6

template< class TPixel, unsigned int TDimension >
int DoIt( int argc, char * argv[] );

// Must follow include of "<ModuleName>CLP.h"
//   and forward declaration of int DoIt( ... ).
#include "tubeCLIHelperFunctions.h"

template< class TPixel, unsigned int VDimension >
int DoIt( int argc, char * argv[] )
{

  PARSE_ARGS;

  // Ensure that the input image dimension is valid
  // We only support 2D and 3D Images due to the
  // limitation of itkTubeSpatialObject
  if( VDimension != 2 && VDimension != 3 )
    {
    tube::ErrorMessage( "Error: Only 2D and 3D Images are currently supported." );
    return EXIT_FAILURE;
    }

  // The timeCollector to perform basic profiling of algorithmic components
  itk::TimeProbesCollectorBase timeCollector;

  // Load Input Image
  //std::cout << "Loading Input Image" << std::endl;

  typedef TPixel                                        PixelType;
  typedef itk::Image< PixelType, VDimension >           ImageType;
  typedef itk::ImageFileReader< ImageType >             ImageReaderType;

  timeCollector.Start( "Loading Input Image" );

  typename ImageReaderType::Pointer pImageReader = ImageReaderType::New();

  try
    {
    pImageReader->SetFileName( inputImageFile.c_str() );
    pImageReader->Update();
    }
  catch( itk::ExceptionObject & err )
    {
    tube::ErrorMessage( "Error loading input image: "
                        + std::string(err.GetDescription()) );
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  typename ImageType::Pointer pInputImage = pImageReader->GetOutput();

  timeCollector.Stop( "Loading Input Image" );

  // Load TRE File
  //std::cout << "Loading TRE File" << std::endl;

  typedef itk::SpatialObjectReader< VDimension >        TubesReaderType;
  typedef itk::GroupSpatialObject< VDimension >         TubeGroupType;

  timeCollector.Start( "Loading Input TRE File" );

  typename TubesReaderType::Pointer pTubeFileReader = TubesReaderType::New();

  try
    {
    pTubeFileReader->SetFileName( inputTREFile.c_str() );
    pTubeFileReader->Update();
    }
  catch( itk::ExceptionObject & err )
    {
    tube::ErrorMessage( "Error loading TRE File: "
                        + std::string( err.GetDescription()) );
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  typename TubeGroupType::Pointer pTubeGroup = pTubeFileReader->GetGroup();

  timeCollector.Stop( "Loading Input TRE File" );

  // Find the user-specified tube
  //std::cout << "Finding user specified tube" << std::endl;

  typedef typename TubeGroupType::ChildrenListType      TubeListType;
  typedef itk::VesselTubeSpatialObject< VDimension >    TubeType;

  TubeType * pInputTube = NULL;
  bool blnTubeFound = false;

  timeCollector.Start( "Finding the user specified tube" );

  char tubeName[] = "Tube";
  TubeListType * pTubeList =
      pTubeGroup->GetChildren( pTubeGroup->GetMaximumDepth(), tubeName);

  typename TubeListType::const_iterator itTubes = pTubeList->begin();

  while( itTubes != pTubeList->end() )
    {
    //std::cout << (*itTubes)->GetId() << std::endl;

    if( (*itTubes)->GetId() == inputTubeId )
      {
      pInputTube = dynamic_cast< TubeType * >( itTubes->GetPointer() );

      if( pInputTube )
        {
        blnTubeFound = true;
        }

      break;
      }

    itTubes++;
    }

  delete pTubeList;

  if( !blnTubeFound )
    {
    tube::ErrorMessage( "Unable to find the specified tube" );
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  pInputTube->ComputeObjectToWorldTransform();
  pInputTube->RemoveDuplicatePoints();
  pInputTube->ComputeTangentAndNormals();

  //pInputTube->Print(std::cout);

  timeCollector.Stop( "Finding the user specified tube" );

  // Get list of tube points
  typedef typename TubeType::PointListType    TubePointListType;

  TubePointListType tubePointList = pInputTube->GetPoints();

  if( tubePointList.size() <= 0 )
    {
    tube::ErrorMessage("The specified tube does not contain any points");
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  std::cout << "Num Tube Points = " << tubePointList.size() << std::endl;

  // Determine maximum radius among all tube points
  typename TubePointListType::const_iterator itPts = tubePointList.begin();

  double maxTubeRadius = itPts->GetRadius();

  while( itPts != tubePointList.end() )
    {
    if( itPts->GetRadius() > maxTubeRadius )
      {
      maxTubeRadius = itPts->GetRadius();
      }
    itPts++;
    }

  std::cout << "Max Radius = " << maxTubeRadius << std::endl;

  // Determine the mean distance between consecutive tube points
  double meanTubePointDist = 0;

  for(unsigned int pid = 1; pid < tubePointList.size(); pid++)
    {
    // compute distance between current and previous tube point
    double curDist = 0;
    typename TubeType::PointType p1 = tubePointList[pid-1].GetPosition();
    typename TubeType::PointType p2 = tubePointList[pid].GetPosition();

    for(unsigned int i = 0; i < VDimension; i++)
      {
      curDist += ( p2[i] - p1[i] ) * ( p2[i] - p1[i] );
      }

    // update mean
    meanTubePointDist += curDist;
    }

  meanTubePointDist /= tubePointList.size();

  std::cout << "Mean Tube Point Distance = " << meanTubePointDist << std::endl;

  // Determine minimum spacing of the input image
  typename ImageType::SpacingType inputSpacing = pInputImage->GetSpacing();
  double minInputSpacing = inputSpacing[0];

  for(unsigned int i = 1; i < VDimension; i++)
    {
    if( inputSpacing[i] < minInputSpacing )
      {
      minInputSpacing = inputSpacing[i];
      }
    }

  std::cout << "Min Input Spacing = " << minInputSpacing << std::endl;

  // Setup the output fly through image and allocate space for it
  typename ImageType::Pointer pFlyThroughImage = ImageType::New();

  // set spacing
  // For last dimension its set to mean consecutive point distance
  // For other dimensions its set to the minimum input spacing
  typename ImageType::SpacingType outputSpacing;

  for(unsigned int i = 0; i < VDimension-1; i++)
    {
    outputSpacing[i] = minInputSpacing;
    }

  outputSpacing[VDimension-1] = meanTubePointDist;

  pFlyThroughImage->SetSpacing( outputSpacing );

  // set start index1
  typename ImageType::IndexType startIndex;
  startIndex.Fill(0);

  // set size
  typename ImageType::SizeType size;
  for(unsigned int i = 0; i < VDimension-1; i++)
    {

    size[i] = 2 * (typename ImageType::SizeValueType)
             (0.5 + (maxTubeRadius / outputSpacing[i])) + 1;
    }
  size[VDimension-1] = tubePointList.size();

  // set regions
  typename ImageType::RegionType region;
  region.SetIndex( startIndex );
  region.SetSize( size );
  pFlyThroughImage->SetRegions( region );

  // Allocate and initialize
  pFlyThroughImage->Allocate();
  pFlyThroughImage->FillBuffer( 0 );

  // Setup tube mask image and allocate space for it
  typedef unsigned char                                 MaskPixelType;
  typedef itk::Image< MaskPixelType, VDimension >       MaskImageType;

  typename MaskImageType::Pointer pTubeMaskFlyThroughImage
    = MaskImageType::New();

  pTubeMaskFlyThroughImage->SetRegions( region );
  pTubeMaskFlyThroughImage->CopyInformation( pFlyThroughImage );
  pTubeMaskFlyThroughImage->Allocate();
  pTubeMaskFlyThroughImage->FillBuffer( 0 );

  // For each tube point, extract normal plane image
  // and fill into corresponding slice in the output image
  typedef typename TubeType::TubePointType               TubePointType;
  typedef typename TubePointType::CovariantVectorType    TubeNormalType;
  typedef itk::ImageRegionIteratorWithIndex< ImageType > ImageIteratorType;
  typedef itk::ImageRegionIterator< MaskImageType >      MaskIteratorType;

  typedef itk::LinearInterpolateImageFunction< ImageType, double >
  InterpolatorType;

  typedef itk::MinimumMaximumImageFilter< ImageType > MinMaxImageFilterType;

  timeCollector.Start( "Generating fly through images" );

  typename TubeType::TransformType * pTubeIndexPhysTransform =
    pInputTube->GetIndexToWorldTransform();

  typename MinMaxImageFilterType::Pointer minmaxFilter =
  MinMaxImageFilterType::New();
  minmaxFilter->SetInput( pInputImage );
  minmaxFilter->Update();
  TPixel outsideVal = minmaxFilter->GetMinimum();

  typename InterpolatorType::Pointer pInterpolator = InterpolatorType::New();
  pInterpolator->SetInputImage( pInputImage );

  unsigned int ptInd = 0;
  unsigned long tubePixelCount = 0;

  for(  itPts = tubePointList.begin();
        itPts != tubePointList.end();
        itPts++, ptInd++
     )
    {
    // Get position, radius and frenet-serret basis of current tube point
    // in the world coordinate system
    typename TubeType::PointType curTubePosition =
      pTubeIndexPhysTransform->TransformPoint( itPts->GetPosition() );

    TubeNormalType curTubeNormal1 =
    pTubeIndexPhysTransform->TransformCovariantVector( itPts->GetNormal1() );
    curTubeNormal1.Normalize();

    TubeNormalType curTubeNormal2 =
    pTubeIndexPhysTransform->TransformCovariantVector( itPts->GetNormal2() );
    curTubeNormal2.Normalize();

    double curTubeRadius = (*itPts).GetRadius();

    //std::cout << curTubeNormal1.GetNorm() << std::endl;
    //std::cout << curTubeNormal2.GetNorm() << std::endl;

    // Define slice region in the output image
    typename ImageType::RegionType sliceRegion;

      typename ImageType::IndexType sliceStartIndex;
      sliceStartIndex.Fill(0);
      sliceStartIndex[VDimension-1] = ptInd;

      typename ImageType::SizeType sliceSize;
      sliceSize = pFlyThroughImage->GetLargestPossibleRegion().GetSize();
      sliceSize[VDimension-1] = 1;

    sliceRegion.SetIndex( sliceStartIndex );
    sliceRegion.SetSize( sliceSize );

    // Iterate through corresponding slice of output image and fill each pixel
    ImageIteratorType itOutSlice( pFlyThroughImage, sliceRegion );
    MaskIteratorType itMask( pTubeMaskFlyThroughImage, sliceRegion );

    for( itOutSlice.GoToBegin(), itMask.GoToBegin();
         !itOutSlice.IsAtEnd();
         ++itOutSlice, ++itMask
       )
      {
      // get index of the current output pixel
      typename ImageType::IndexType curOutIndex = itOutSlice.GetIndex();

      // compute corresponding position in the input image
      //typename InterpolatorType::ContinuousIndexType curInputPoint;
      typename ImageType::PointType curInputPoint;

      double distToCenter = 0;

      for(unsigned int i = 0; i < VDimension; i++)
        {
        curInputPoint[i] = curTubePosition[i];
        }

      if(VDimension == 2)
        {
        double stepN1 = ( curOutIndex[0] - 0.5 * sliceSize[0] )
                            * outputSpacing[0];

        for(unsigned int i = 0; i < VDimension; i++)
          {
          curInputPoint[i] += stepN1 * curTubeNormal1[i];
          }

          distToCenter = stepN1;
        }
      else if (VDimension == 3)
        {
        double stepN1 = ( curOutIndex[0] - 0.5 * sliceSize[0] )
                            * outputSpacing[0];

        double stepN2 = ( curOutIndex[1] - 0.5 * sliceSize[1] )
                            * outputSpacing[1];

        for(unsigned int i = 0; i < VDimension; i++)
          {
          curInputPoint[i] += stepN1 * curTubeNormal1[i];
          curInputPoint[i] += stepN2 * curTubeNormal2[i];
          }

          distToCenter = std::sqrt( stepN1 * stepN1 + stepN2 * stepN2  );
        }

      // set pixel values in the output images
      if( pInterpolator->IsInsideBuffer( curInputPoint ) )
        {
        // set intensity value by getting it from input image using
        // interpolation
        itOutSlice.Set( pInterpolator->Evaluate( curInputPoint ) );

        // if point is within the tube set tube mask pixel to on
        if( distToCenter <= curTubeRadius )
          {
          itMask.Set( 1.0 );
          }

        tubePixelCount++;
        }
      else
        {
        itOutSlice.Set( outsideVal );
        }
      }
    }

  std::cout << "Num pixels inside tube " << tubePixelCount << std::endl;

  timeCollector.Stop( "Generating fly through images" );

  // Write fly through image
  typedef itk::ImageFileWriter< ImageType > ImageWriterType;

  timeCollector.Start( "Writing fly through image" );

  typename ImageWriterType::Pointer imageWriter = ImageWriterType::New();

  try
    {
    imageWriter->SetFileName( outputImageFile.c_str() );
    imageWriter->SetInput( pFlyThroughImage );
    imageWriter->Update();
    }
  catch( itk::ExceptionObject & err )
    {
    tube::ErrorMessage( "Error writing fly through image: "
                        + std::string(err.GetDescription()) );
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  timeCollector.Stop( "Writing fly through image" );

  // Write tube mask fly through image
  typedef itk::ImageFileWriter< MaskImageType > MaskWriterType;

  timeCollector.Start( "Writing tube mask fly through image" );

  typename MaskWriterType::Pointer maskWriter = MaskWriterType::New();

  try
    {
    maskWriter->SetFileName( outputTubeMaskFile.c_str() );
    maskWriter->SetInput( pTubeMaskFlyThroughImage );
    maskWriter->Update();
    }
  catch( itk::ExceptionObject & err )
    {
    tube::ErrorMessage( "Error writing tube mask fly through image: "
                        + std::string(err.GetDescription()) );
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  timeCollector.Stop( "Writing tube mask fly through image" );

  // All done
  timeCollector.Report();
  return EXIT_SUCCESS;
}

// Main
int main( int argc, char * argv[] )
{
  try
    {
    PARSE_ARGS;
    }
  catch( const std::exception & err )
    {
    tube::ErrorMessage( err.what() );
    return EXIT_FAILURE;
    }

  PARSE_ARGS;

  return tube::ParseArgsAndCallDoIt( inputImageFile, argc, argv );
}
<commit_msg>Fixed style errors in ComputeTubeFlyThroughImage<commit_after>/*=========================================================================

Library:   TubeTK

Copyright 2010 Kitware Inc. 28 Corporate Drive,
Clifton Park, NY, 12065, USA.

All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

=========================================================================*/

#include "tubeCLIProgressReporter.h"
#include "tubeMessage.h"

#include "itkTimeProbesCollectorBase.h"

#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"

#include "itkResampleImageFilter.h"
#include "itkLinearInterpolateImageFunction.h"
#include "itkMinimumMaximumImageFilter.h"

#include "itkSpatialObjectReader.h"
#include "itkSpatialObjectWriter.h"
#include "itkGroupSpatialObject.h"
#include "itkTubeSpatialObject.h"
#include "itkTubeSpatialObjectPoint.h"
#include "itkVesselTubeSpatialObject.h"
#include "itkVesselTubeSpatialObjectPoint.h"

#include "ComputeTubeFlyThroughImageCLP.h"

#define tubePadSize 6

template< class TPixel, unsigned int TDimension >
int DoIt( int argc, char * argv[] );

// Must follow include of "<ModuleName>CLP.h"
//   and forward declaration of int DoIt( ... ).
#include "tubeCLIHelperFunctions.h"

template< class TPixel, unsigned int VDimension >
int DoIt( int argc, char * argv[] )
{

  PARSE_ARGS;

  // Ensure that the input image dimension is valid
  // We only support 2D and 3D Images due to the
  // limitation of itkTubeSpatialObject
  if( VDimension != 2 && VDimension != 3 )
    {
    tube::ErrorMessage( "Error: Only 2D and 3D Images are currently supported." );
    return EXIT_FAILURE;
    }

  // The timeCollector to perform basic profiling of algorithmic components
  itk::TimeProbesCollectorBase timeCollector;

  // Load Input Image
  //std::cout << "Loading Input Image" << std::endl;

  typedef TPixel                                        PixelType;
  typedef itk::Image< PixelType, VDimension >           ImageType;
  typedef itk::ImageFileReader< ImageType >             ImageReaderType;

  timeCollector.Start( "Loading Input Image" );

  typename ImageReaderType::Pointer pImageReader = ImageReaderType::New();

  try
    {
    pImageReader->SetFileName( inputImageFile.c_str() );
    pImageReader->Update();
    }
  catch( itk::ExceptionObject & err )
    {
    tube::ErrorMessage( "Error loading input image: "
      + std::string(err.GetDescription()) );
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  typename ImageType::Pointer pInputImage = pImageReader->GetOutput();

  timeCollector.Stop( "Loading Input Image" );

  // Load TRE File
  //std::cout << "Loading TRE File" << std::endl;

  typedef itk::SpatialObjectReader< VDimension >        TubesReaderType;
  typedef itk::GroupSpatialObject< VDimension >         TubeGroupType;

  timeCollector.Start( "Loading Input TRE File" );

  typename TubesReaderType::Pointer pTubeFileReader = TubesReaderType::New();

  try
    {
    pTubeFileReader->SetFileName( inputTREFile.c_str() );
    pTubeFileReader->Update();
    }
  catch( itk::ExceptionObject & err )
    {
    tube::ErrorMessage( "Error loading TRE File: "
      + std::string( err.GetDescription()) );
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  typename TubeGroupType::Pointer pTubeGroup = pTubeFileReader->GetGroup();

  timeCollector.Stop( "Loading Input TRE File" );

  // Find the user-specified tube
  //std::cout << "Finding user specified tube" << std::endl;

  typedef typename TubeGroupType::ChildrenListType      TubeListType;
  typedef itk::VesselTubeSpatialObject< VDimension >    TubeType;

  TubeType * pInputTube = NULL;
  bool blnTubeFound = false;

  timeCollector.Start( "Finding the user specified tube" );

  char tubeName[] = "Tube";
  TubeListType * pTubeList =
      pTubeGroup->GetChildren( pTubeGroup->GetMaximumDepth(), tubeName);

  typename TubeListType::const_iterator itTubes = pTubeList->begin();

  while( itTubes != pTubeList->end() )
    {
    //std::cout << (*itTubes)->GetId() << std::endl;

    if( (*itTubes)->GetId() == inputTubeId )
      {
      pInputTube = dynamic_cast< TubeType * >( itTubes->GetPointer() );

      if( pInputTube )
        {
        blnTubeFound = true;
        }

      break;
      }

    itTubes++;
    }

  delete pTubeList;

  if( !blnTubeFound )
    {
    tube::ErrorMessage( "Unable to find the specified tube" );
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  pInputTube->ComputeObjectToWorldTransform();
  pInputTube->RemoveDuplicatePoints();
  pInputTube->ComputeTangentAndNormals();

  //pInputTube->Print(std::cout);

  timeCollector.Stop( "Finding the user specified tube" );

  // Get list of tube points
  typedef typename TubeType::PointListType    TubePointListType;

  TubePointListType tubePointList = pInputTube->GetPoints();

  if( tubePointList.size() <= 0 )
    {
    tube::ErrorMessage("The specified tube does not contain any points");
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  std::cout << "Num Tube Points = " << tubePointList.size() << std::endl;

  // Determine maximum radius among all tube points
  typename TubePointListType::const_iterator itPts = tubePointList.begin();

  double maxTubeRadius = itPts->GetRadius();

  while( itPts != tubePointList.end() )
    {
    if( itPts->GetRadius() > maxTubeRadius )
      {
      maxTubeRadius = itPts->GetRadius();
      }
    itPts++;
    }

  std::cout << "Max Radius = " << maxTubeRadius << std::endl;

  // Determine the mean distance between consecutive tube points
  double meanTubePointDist = 0;

  for(unsigned int pid = 1; pid < tubePointList.size(); pid++)
    {
    // compute distance between current and previous tube point
    double curDist = 0;
    typename TubeType::PointType p1 = tubePointList[pid-1].GetPosition();
    typename TubeType::PointType p2 = tubePointList[pid].GetPosition();

    for(unsigned int i = 0; i < VDimension; i++)
      {
      curDist += ( p2[i] - p1[i] ) * ( p2[i] - p1[i] );
      }

    // update mean
    meanTubePointDist += curDist;
    }

  meanTubePointDist /= tubePointList.size();

  std::cout << "Mean Tube Point Distance = " << meanTubePointDist << std::endl;

  // Determine minimum spacing of the input image
  typename ImageType::SpacingType inputSpacing = pInputImage->GetSpacing();
  double minInputSpacing = inputSpacing[0];

  for(unsigned int i = 1; i < VDimension; i++)
    {
    if( inputSpacing[i] < minInputSpacing )
      {
      minInputSpacing = inputSpacing[i];
      }
    }

  std::cout << "Min Input Spacing = " << minInputSpacing << std::endl;

  // Setup the output fly through image and allocate space for it
  typename ImageType::Pointer pFlyThroughImage = ImageType::New();

  // set spacing
  // For last dimension its set to mean consecutive point distance
  // For other dimensions its set to the minimum input spacing
  typename ImageType::SpacingType outputSpacing;

  for(unsigned int i = 0; i < VDimension-1; i++)
    {
    outputSpacing[i] = minInputSpacing;
    }

  outputSpacing[VDimension-1] = meanTubePointDist;

  pFlyThroughImage->SetSpacing( outputSpacing );

  // set start index1
  typename ImageType::IndexType startIndex;
  startIndex.Fill(0);

  // set size
  typename ImageType::SizeType size;
  for(unsigned int i = 0; i < VDimension-1; i++)
    {

    size[i] = 2 * (typename ImageType::SizeValueType)
      (0.5 + (maxTubeRadius / outputSpacing[i])) + 1;
    }
  size[VDimension-1] = tubePointList.size();

  // set regions
  typename ImageType::RegionType region;
  region.SetIndex( startIndex );
  region.SetSize( size );
  pFlyThroughImage->SetRegions( region );

  // Allocate and initialize
  pFlyThroughImage->Allocate();
  pFlyThroughImage->FillBuffer( 0 );

  // Setup tube mask image and allocate space for it
  typedef unsigned char                                 MaskPixelType;
  typedef itk::Image< MaskPixelType, VDimension >       MaskImageType;

  typename MaskImageType::Pointer pTubeMaskFlyThroughImage
    = MaskImageType::New();

  pTubeMaskFlyThroughImage->SetRegions( region );
  pTubeMaskFlyThroughImage->CopyInformation( pFlyThroughImage );
  pTubeMaskFlyThroughImage->Allocate();
  pTubeMaskFlyThroughImage->FillBuffer( 0 );

  // For each tube point, extract normal plane image
  // and fill into corresponding slice in the output image
  typedef typename TubeType::TubePointType               TubePointType;
  typedef typename TubePointType::CovariantVectorType    TubeNormalType;
  typedef itk::ImageRegionIteratorWithIndex< ImageType > ImageIteratorType;
  typedef itk::ImageRegionIterator< MaskImageType >      MaskIteratorType;

  typedef itk::LinearInterpolateImageFunction< ImageType, double >
  InterpolatorType;

  typedef itk::MinimumMaximumImageFilter< ImageType > MinMaxImageFilterType;

  timeCollector.Start( "Generating fly through images" );

  typename TubeType::TransformType * pTubeIndexPhysTransform =
    pInputTube->GetIndexToWorldTransform();

  typename MinMaxImageFilterType::Pointer minmaxFilter =
  MinMaxImageFilterType::New();
  minmaxFilter->SetInput( pInputImage );
  minmaxFilter->Update();
  TPixel outsideVal = minmaxFilter->GetMinimum();

  typename InterpolatorType::Pointer pInterpolator = InterpolatorType::New();
  pInterpolator->SetInputImage( pInputImage );

  unsigned int ptInd = 0;
  unsigned long tubePixelCount = 0;

  for(  itPts = tubePointList.begin();
    itPts != tubePointList.end(); itPts++, ptInd++ )
    {
    // Get position, radius and frenet-serret basis of current tube point
    // in the world coordinate system
    typename TubeType::PointType curTubePosition =
      pTubeIndexPhysTransform->TransformPoint( itPts->GetPosition() );

    TubeNormalType curTubeNormal1 =
    pTubeIndexPhysTransform->TransformCovariantVector( itPts->GetNormal1() );
    curTubeNormal1.Normalize();

    TubeNormalType curTubeNormal2 =
    pTubeIndexPhysTransform->TransformCovariantVector( itPts->GetNormal2() );
    curTubeNormal2.Normalize();

    double curTubeRadius = (*itPts).GetRadius();

    //std::cout << curTubeNormal1.GetNorm() << std::endl;
    //std::cout << curTubeNormal2.GetNorm() << std::endl;

    // Define slice region in the output image
    typename ImageType::RegionType sliceRegion;

      typename ImageType::IndexType sliceStartIndex;
      sliceStartIndex.Fill(0);
      sliceStartIndex[VDimension-1] = ptInd;

      typename ImageType::SizeType sliceSize;
      sliceSize = pFlyThroughImage->GetLargestPossibleRegion().GetSize();
      sliceSize[VDimension-1] = 1;

    sliceRegion.SetIndex( sliceStartIndex );
    sliceRegion.SetSize( sliceSize );

    // Iterate through corresponding slice of output image and fill each pixel
    ImageIteratorType itOutSlice( pFlyThroughImage, sliceRegion );
    MaskIteratorType itMask( pTubeMaskFlyThroughImage, sliceRegion );

    for( itOutSlice.GoToBegin(), itMask.GoToBegin();
      !itOutSlice.IsAtEnd(); ++itOutSlice, ++itMask )
      {
      // get index of the current output pixel
      typename ImageType::IndexType curOutIndex = itOutSlice.GetIndex();

      // compute corresponding position in the input image
      //typename InterpolatorType::ContinuousIndexType curInputPoint;
      typename ImageType::PointType curInputPoint;

      double distToCenter = 0;

      for(unsigned int i = 0; i < VDimension; i++)
        {
        curInputPoint[i] = curTubePosition[i];
        }

      if(VDimension == 2)
        {
        double stepN1 = ( curOutIndex[0] - 0.5 * sliceSize[0] )
          * outputSpacing[0];

        for(unsigned int i = 0; i < VDimension; i++)
          {
          curInputPoint[i] += stepN1 * curTubeNormal1[i];
          }

          distToCenter = stepN1;
        }
      else if (VDimension == 3)
        {
        double stepN1 = ( curOutIndex[0] - 0.5 * sliceSize[0] )
          * outputSpacing[0];

        double stepN2 = ( curOutIndex[1] - 0.5 * sliceSize[1] )
          * outputSpacing[1];

        for(unsigned int i = 0; i < VDimension; i++)
          {
          curInputPoint[i] += stepN1 * curTubeNormal1[i];
          curInputPoint[i] += stepN2 * curTubeNormal2[i];
          }

          distToCenter = std::sqrt( stepN1 * stepN1 + stepN2 * stepN2  );
        }

      // set pixel values in the output images
      if( pInterpolator->IsInsideBuffer( curInputPoint ) )
        {
        // set intensity value by getting it from input image using
        // interpolation
        itOutSlice.Set( pInterpolator->Evaluate( curInputPoint ) );

        // if point is within the tube set tube mask pixel to on
        if( distToCenter <= curTubeRadius )
          {
          itMask.Set( 1.0 );
          }

        tubePixelCount++;
        }
      else
        {
        itOutSlice.Set( outsideVal );
        }
      }
    }

  std::cout << "Num pixels inside tube " << tubePixelCount << std::endl;

  timeCollector.Stop( "Generating fly through images" );

  // Write fly through image
  typedef itk::ImageFileWriter< ImageType > ImageWriterType;

  timeCollector.Start( "Writing fly through image" );

  typename ImageWriterType::Pointer imageWriter = ImageWriterType::New();

  try
    {
    imageWriter->SetFileName( outputImageFile.c_str() );
    imageWriter->SetInput( pFlyThroughImage );
    imageWriter->Update();
    }
  catch( itk::ExceptionObject & err )
    {
    tube::ErrorMessage( "Error writing fly through image: "
      + std::string(err.GetDescription()) );
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  timeCollector.Stop( "Writing fly through image" );

  // Write tube mask fly through image
  typedef itk::ImageFileWriter< MaskImageType > MaskWriterType;

  timeCollector.Start( "Writing tube mask fly through image" );

  typename MaskWriterType::Pointer maskWriter = MaskWriterType::New();

  try
    {
    maskWriter->SetFileName( outputTubeMaskFile.c_str() );
    maskWriter->SetInput( pTubeMaskFlyThroughImage );
    maskWriter->Update();
    }
  catch( itk::ExceptionObject & err )
    {
    tube::ErrorMessage( "Error writing tube mask fly through image: "
                        + std::string(err.GetDescription()) );
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  timeCollector.Stop( "Writing tube mask fly through image" );

  // All done
  timeCollector.Report();
  return EXIT_SUCCESS;
}

// Main
int main( int argc, char * argv[] )
{
  try
    {
    PARSE_ARGS;
    }
  catch( const std::exception & err )
    {
    tube::ErrorMessage( err.what() );
    return EXIT_FAILURE;
    }

  PARSE_ARGS;

  return tube::ParseArgsAndCallDoIt( inputImageFile, argc, argv );
}
<|endoftext|>


<commit_before>#include "gtest/gtest.h"
#include "BBE/BrotBoxEngine.h"
#include "TestUtils.h"

TEST(List, ConstructorDefault)
{
	bbe::List<SomeClass<int>> list;
	bbe::List<SomeClass<int>> const * pList = &list;
	ASSERT_EQ(list.getCapacity(), 0);
	ASSERT_EQ(list.getLength(), 0);
	ASSERT_EQ(list.getRaw(), nullptr);
	ASSERT_EQ(pList->getRaw(), nullptr);
	ASSERT_TRUE(list.isEmpty());
	ASSERT_FALSE(list.shrink());
}

TEST(List, ConstructorAmountOfData)
{
	bbe::List<SomeClass<int>> list(17);
	bbe::List<SomeClass<int>> const* pList = &list;
	ASSERT_EQ(list.getCapacity(), 17);
	ASSERT_EQ(list.getLength(), 0);
	ASSERT_NE(list.getRaw(), nullptr);
	ASSERT_NE(pList->getRaw(), nullptr);
	ASSERT_TRUE(list.isEmpty());
	ASSERT_TRUE(list.shrink());
	ASSERT_EQ(list.getCapacity(), 0);
}

TEST(List, ConstructorWithData)
{
	bbe::List<SomeClass<int>> list(17, 1337);
	bbe::List<SomeClass<int>> const* pList = &list;
	ASSERT_EQ(list.getCapacity(), 17);
	ASSERT_EQ(list.getLength(), 17);
	ASSERT_NE(list.getRaw(), nullptr);
	ASSERT_NE(pList->getRaw(), nullptr);
	ASSERT_FALSE(list.isEmpty());
	ASSERT_FALSE(list.shrink());
	for (size_t i = 0; i < list.getLength(); i++)
	{
		ASSERT_EQ(list[i].getLength(), 1337);
	}
}

TEST(List, ConstructorCopy)
{
	bbe::List<SomeClass<int>> list(100, 42);
	bbe::List<SomeClass<int>> copy(list);
	ASSERT_EQ(list.getCapacity(), 100);
	ASSERT_EQ(list.getLength(), 100);
	ASSERT_EQ(copy.getCapacity(), 100);
	ASSERT_EQ(copy.getLength(), 100);
	for (size_t i = 0; i < list.getLength(); i++)
	{
		ASSERT_EQ(list[i].getLength(), 42);
		ASSERT_EQ(copy[i].getLength(), 42);
	}

	copy[17].resize(13);
	ASSERT_EQ(copy[17].getLength(), 13);
	ASSERT_EQ(list[17].getLength(), 42);
}

TEST(List, ConstructorMove)
{
	bbe::List<SomeClass<int>> list(81, 123);
	bbe::List<SomeClass<int>> move(std::move(list));
	ASSERT_EQ(list.getCapacity(), 0);
	ASSERT_EQ(list.getLength(), 0);
	ASSERT_EQ(list.getRaw(), nullptr);
	ASSERT_EQ(move.getCapacity(), 81);
	ASSERT_EQ(move.getLength(), 81);
	ASSERT_NE(move.getRaw(), nullptr);
	for (size_t i = 0; i < move.getLength(); i++)
	{
		ASSERT_EQ(move[i].getLength(), 123);
	}
}

TEST(List, ConstructorInitializer)
{
	bbe::List<int> list = { 17, 32, 19, 2, 15 };
	ASSERT_EQ(list.getCapacity(), 5);
	ASSERT_EQ(list.getLength(), 5);
	ASSERT_NE(list.getRaw(), nullptr);
	ASSERT_EQ(list[0], 17);
	ASSERT_EQ(list[1], 32);
	ASSERT_EQ(list[2], 19);
	ASSERT_EQ(list[3], 2);
	ASSERT_EQ(list[4], 15);
}

TEST(List, OperatorAssignment)
{
	bbe::List<SomeClass<int>> list1(17, 100);
	bbe::List<SomeClass<int>> list2(32, 1000);

	list1 = list2;

	ASSERT_NE(list2.getLength(), 0);
	ASSERT_NE(list2.getCapacity(), 0);
	ASSERT_NE(list2.getRaw(), nullptr);

	ASSERT_EQ(list1.getLength(), 32);
	for (size_t i = 0; i < list1.getLength(); i++)
	{
		ASSERT_EQ(list1[i].getLength(), 1000);
	}
}

TEST(List, OperatorMove)
{
	bbe::List<SomeClass<int>> list1(17, 100);
	bbe::List<SomeClass<int>> list2(32, 1000);

	list1 = std::move(list2);

	ASSERT_EQ(list2.getLength(), 0);
	ASSERT_EQ(list2.getCapacity(), 0);
	ASSERT_EQ(list2.getRaw(), nullptr);

	ASSERT_EQ(list1.getLength(), 32);
	for (size_t i = 0; i < list1.getLength(); i++)
	{
		ASSERT_EQ(list1[i].getLength(), 1000);
	}
}

TEST(List, RecursiveList)
{
	bbe::List<SomeClass<int>> layer1_1(12, SomeClass<int>(18));
	bbe::List<SomeClass<int>> layer1_2(32, SomeClass<int>(20));

	bbe::List<bbe::List<SomeClass<int>>> layer2_1(3, layer1_1);
	bbe::List<bbe::List<SomeClass<int>>> layer2_2 = { layer1_1, layer1_2 };

	ASSERT_EQ(layer1_1.getLength(), 12);
	ASSERT_EQ(layer1_2.getLength(), 32);

	ASSERT_EQ(layer2_1.getLength(), 3);
	ASSERT_EQ(layer2_2.getLength(), 2);

	for (size_t i = 0; i < layer2_1.getLength(); i++)
	{
		ASSERT_EQ(layer2_1[i].getLength(), 12);
	}

	ASSERT_EQ(layer2_2[0].getLength(), 12);
	ASSERT_EQ(layer2_2[1].getLength(), 32);

	for (size_t i = 0; i < layer2_1.getLength(); i++)
	{
		for (size_t k = 0; k < layer2_1[i].getLength(); k++)
		{
			ASSERT_EQ(layer2_1[i][k].getLength(), 18);
		}
	}

	layer2_1[1][6].resize(1000);
	for (size_t i = 0; i < layer2_1.getLength(); i++)
	{
		for (size_t k = 0; k < layer2_1[i].getLength(); k++)
		{
			if(i == 1 && k == 6)
				ASSERT_EQ(layer2_1[i][k].getLength(), 1000);
			else
				ASSERT_EQ(layer2_1[i][k].getLength(), 18);
		}
	}
}

TEST(List, Add)
{
	bbe::List<SomeClass<int>> list;
	for (size_t i = 1; i < 52; i++)
	{
		list.add(SomeClass<int>(i));
	}
	bbe::List<SomeClass<int>> copy = list;
	list[13] = SomeClass<int>(1337);

	ASSERT_EQ(list.getLength(), copy.getLength());
	for (size_t i = 0; i < list.getLength(); i++)
	{
		ASSERT_EQ(copy[i].getLength(), i + 1);
		if (i != 13) ASSERT_EQ(list[i].getLength(), i + 1);
		else ASSERT_EQ(list[i].getLength(), 1337);
	}
}

TEST(List, CombineUnorderedLists)
{
	SomeClass<int> a = 17;
	SomeClass<int> b = 13;
	SomeClass<int> c = 1337;
	SomeClass<int> d = 2;
	SomeClass<int> e = 4;
	SomeClass<int> f = 2;

	bbe::List<SomeClass<int>> list1 = { a, b, c, d, e, f };
	ASSERT_EQ(list1[0].getLength(), 17);
	ASSERT_EQ(list1[1].getLength(), 13);
	ASSERT_EQ(list1[2].getLength(), 1337);
	ASSERT_EQ(list1[3].getLength(), 2);
	ASSERT_EQ(list1[4].getLength(), 4);
	ASSERT_EQ(list1[5].getLength(), 2);

	SomeClass<int> g = 18;
	SomeClass<int> h = 4;
	SomeClass<int> i = 100;
	SomeClass<int> j = 1;
	SomeClass<int> k = 1;
	SomeClass<int> l = 2;

	bbe::List<SomeClass<int>> list2 = { g, h, i, j, k, l };
	ASSERT_EQ(list2[0].getLength(), 18);
	ASSERT_EQ(list2[1].getLength(), 4);
	ASSERT_EQ(list2[2].getLength(), 100);
	ASSERT_EQ(list2[3].getLength(), 1);
	ASSERT_EQ(list2[4].getLength(), 1);
	ASSERT_EQ(list2[5].getLength(), 2);

	list1 += list2;
	ASSERT_EQ(list1[ 0].getLength(), 17);
	ASSERT_EQ(list1[ 1].getLength(), 13);
	ASSERT_EQ(list1[ 2].getLength(), 1337);
	ASSERT_EQ(list1[ 3].getLength(), 2);
	ASSERT_EQ(list1[ 4].getLength(), 4);
	ASSERT_EQ(list1[ 5].getLength(), 2);
	ASSERT_EQ(list1[ 6].getLength(), 18);
	ASSERT_EQ(list1[ 7].getLength(), 4);
	ASSERT_EQ(list1[ 8].getLength(), 100);
	ASSERT_EQ(list1[ 9].getLength(), 1);
	ASSERT_EQ(list1[10].getLength(), 1);
	ASSERT_EQ(list1[11].getLength(), 2);
}

TEST(List, CombineOrderedLists)
{
	SomeClass<int> a = 17;
	SomeClass<int> b = 13;
	SomeClass<int> c = 1337;
	SomeClass<int> d = 2;
	SomeClass<int> e = 4;
	SomeClass<int> f = 2;

	bbe::List<SomeClass<int>, true> list1 = { a, b, c, d, e, f };
	ASSERT_EQ(list1[0].getLength(),    2);
	ASSERT_EQ(list1[1].getLength(),    2);
	ASSERT_EQ(list1[2].getLength(),    4);
	ASSERT_EQ(list1[3].getLength(),   13);
	ASSERT_EQ(list1[4].getLength(),   17);
	ASSERT_EQ(list1[5].getLength(), 1337);

	SomeClass<int> g = 18;
	SomeClass<int> h = 4;
	SomeClass<int> i = 100;
	SomeClass<int> j = 1;
	SomeClass<int> k = 1;
	SomeClass<int> l = 2;

	bbe::List<SomeClass<int>, true> list2 = { g, h, i, j, k, l };
	ASSERT_EQ(list2[0].getLength(),   1);
	ASSERT_EQ(list2[1].getLength(),   1);
	ASSERT_EQ(list2[2].getLength(),   2);
	ASSERT_EQ(list2[3].getLength(),   4);
	ASSERT_EQ(list2[4].getLength(),  18);
	ASSERT_EQ(list2[5].getLength(), 100);

	list1 += list2;
	ASSERT_EQ(list1[ 0].getLength(),    1);
	ASSERT_EQ(list1[ 1].getLength(),    1);
	ASSERT_EQ(list1[ 2].getLength(),    2);
	ASSERT_EQ(list1[ 3].getLength(),    2);
	ASSERT_EQ(list1[ 4].getLength(),    2);
	ASSERT_EQ(list1[ 5].getLength(),    4);
	ASSERT_EQ(list1[ 6].getLength(),    4);
	ASSERT_EQ(list1[ 7].getLength(),   13);
	ASSERT_EQ(list1[ 8].getLength(),   17);
	ASSERT_EQ(list1[ 9].getLength(),   18);
	ASSERT_EQ(list1[10].getLength(),  100);
	ASSERT_EQ(list1[11].getLength(), 1337);
}
<commit_msg>Added Assertions to Test<commit_after>#include "gtest/gtest.h"
#include "BBE/BrotBoxEngine.h"
#include "TestUtils.h"

TEST(List, ConstructorDefault)
{
	bbe::List<SomeClass<int>> list;
	bbe::List<SomeClass<int>> const * pList = &list;
	ASSERT_EQ(list.getCapacity(), 0);
	ASSERT_EQ(list.getLength(), 0);
	ASSERT_EQ(list.getRaw(), nullptr);
	ASSERT_EQ(pList->getRaw(), nullptr);
	ASSERT_TRUE(list.isEmpty());
	ASSERT_FALSE(list.shrink());
}

TEST(List, ConstructorAmountOfData)
{
	bbe::List<SomeClass<int>> list(17);
	bbe::List<SomeClass<int>> const* pList = &list;
	ASSERT_EQ(list.getCapacity(), 17);
	ASSERT_EQ(list.getLength(), 0);
	ASSERT_NE(list.getRaw(), nullptr);
	ASSERT_NE(pList->getRaw(), nullptr);
	ASSERT_TRUE(list.isEmpty());
	ASSERT_TRUE(list.shrink());
	ASSERT_EQ(list.getCapacity(), 0);
}

TEST(List, ConstructorWithData)
{
	bbe::List<SomeClass<int>> list(17, 1337);
	bbe::List<SomeClass<int>> const* pList = &list;
	ASSERT_EQ(list.getCapacity(), 17);
	ASSERT_EQ(list.getLength(), 17);
	ASSERT_NE(list.getRaw(), nullptr);
	ASSERT_NE(pList->getRaw(), nullptr);
	ASSERT_FALSE(list.isEmpty());
	ASSERT_FALSE(list.shrink());
	for (size_t i = 0; i < list.getLength(); i++)
	{
		ASSERT_EQ(list[i].getLength(), 1337);
	}
}

TEST(List, ConstructorCopy)
{
	bbe::List<SomeClass<int>> list(100, 42);
	bbe::List<SomeClass<int>> copy(list);
	ASSERT_EQ(list.getCapacity(), 100);
	ASSERT_EQ(list.getLength(), 100);
	ASSERT_EQ(copy.getCapacity(), 100);
	ASSERT_EQ(copy.getLength(), 100);
	for (size_t i = 0; i < list.getLength(); i++)
	{
		ASSERT_EQ(list[i].getLength(), 42);
		ASSERT_EQ(copy[i].getLength(), 42);
	}

	copy[17].resize(13);
	ASSERT_EQ(copy[17].getLength(), 13);
	ASSERT_EQ(list[17].getLength(), 42);
}

TEST(List, ConstructorMove)
{
	bbe::List<SomeClass<int>> list(81, 123);
	bbe::List<SomeClass<int>> move(std::move(list));
	ASSERT_EQ(list.getCapacity(), 0);
	ASSERT_EQ(list.getLength(), 0);
	ASSERT_EQ(list.getRaw(), nullptr);
	ASSERT_EQ(move.getCapacity(), 81);
	ASSERT_EQ(move.getLength(), 81);
	ASSERT_NE(move.getRaw(), nullptr);
	for (size_t i = 0; i < move.getLength(); i++)
	{
		ASSERT_EQ(move[i].getLength(), 123);
	}
}

TEST(List, ConstructorInitializer)
{
	bbe::List<int> list = { 17, 32, 19, 2, 15 };
	ASSERT_EQ(list.getCapacity(), 5);
	ASSERT_EQ(list.getLength(), 5);
	ASSERT_NE(list.getRaw(), nullptr);
	ASSERT_EQ(list[0], 17);
	ASSERT_EQ(list[1], 32);
	ASSERT_EQ(list[2], 19);
	ASSERT_EQ(list[3], 2);
	ASSERT_EQ(list[4], 15);
}

TEST(List, OperatorAssignment)
{
	bbe::List<SomeClass<int>> list1(17, 100);
	bbe::List<SomeClass<int>> list2(32, 1000);

	list1 = list2;

	ASSERT_NE(list2.getLength(), 0);
	ASSERT_NE(list2.getCapacity(), 0);
	ASSERT_NE(list2.getRaw(), nullptr);

	ASSERT_EQ(list1.getLength(), 32);
	for (size_t i = 0; i < list1.getLength(); i++)
	{
		ASSERT_EQ(list1[i].getLength(), 1000);
	}
}

TEST(List, OperatorMove)
{
	bbe::List<SomeClass<int>> list1(17, 100);
	bbe::List<SomeClass<int>> list2(32, 1000);

	list1 = std::move(list2);

	ASSERT_EQ(list2.getLength(), 0);
	ASSERT_EQ(list2.getCapacity(), 0);
	ASSERT_EQ(list2.getRaw(), nullptr);

	ASSERT_EQ(list1.getLength(), 32);
	for (size_t i = 0; i < list1.getLength(); i++)
	{
		ASSERT_EQ(list1[i].getLength(), 1000);
	}
}

TEST(List, RecursiveList)
{
	bbe::List<SomeClass<int>> layer1_1(12, SomeClass<int>(18));
	bbe::List<SomeClass<int>> layer1_2(32, SomeClass<int>(20));

	bbe::List<bbe::List<SomeClass<int>>> layer2_1(3, layer1_1);
	bbe::List<bbe::List<SomeClass<int>>> layer2_2 = { layer1_1, layer1_2 };

	ASSERT_EQ(layer1_1.getLength(), 12);
	ASSERT_EQ(layer1_2.getLength(), 32);

	ASSERT_EQ(layer2_1.getLength(), 3);
	ASSERT_EQ(layer2_2.getLength(), 2);

	for (size_t i = 0; i < layer2_1.getLength(); i++)
	{
		ASSERT_EQ(layer2_1[i].getLength(), 12);
	}

	ASSERT_EQ(layer2_2[0].getLength(), 12);
	ASSERT_EQ(layer2_2[1].getLength(), 32);

	for (size_t i = 0; i < layer2_1.getLength(); i++)
	{
		for (size_t k = 0; k < layer2_1[i].getLength(); k++)
		{
			ASSERT_EQ(layer2_1[i][k].getLength(), 18);
		}
	}

	layer2_1[1][6].resize(1000);
	for (size_t i = 0; i < layer2_1.getLength(); i++)
	{
		for (size_t k = 0; k < layer2_1[i].getLength(); k++)
		{
			if(i == 1 && k == 6)
				ASSERT_EQ(layer2_1[i][k].getLength(), 1000);
			else
				ASSERT_EQ(layer2_1[i][k].getLength(), 18);
		}
	}
}

TEST(List, Add)
{
	bbe::List<SomeClass<int>> list;
	for (size_t i = 1; i < 52; i++)
	{
		list.add(SomeClass<int>(i));
	}
	bbe::List<SomeClass<int>> copy = list;
	list[13] = SomeClass<int>(1337);

	ASSERT_EQ(list.getLength(), copy.getLength());
	for (size_t i = 0; i < list.getLength(); i++)
	{
		ASSERT_EQ(copy[i].getLength(), i + 1);
		if (i != 13) ASSERT_EQ(list[i].getLength(), i + 1);
		else ASSERT_EQ(list[i].getLength(), 1337);
	}
}

TEST(List, CombineUnorderedLists)
{
	SomeClass<int> a = 17;
	SomeClass<int> b = 13;
	SomeClass<int> c = 1337;
	SomeClass<int> d = 2;
	SomeClass<int> e = 4;
	SomeClass<int> f = 2;

	bbe::List<SomeClass<int>> list1 = { a, b, c, d, e, f };
	ASSERT_EQ(list1[0].getLength(), 17);
	ASSERT_EQ(list1[1].getLength(), 13);
	ASSERT_EQ(list1[2].getLength(), 1337);
	ASSERT_EQ(list1[3].getLength(), 2);
	ASSERT_EQ(list1[4].getLength(), 4);
	ASSERT_EQ(list1[5].getLength(), 2);

	SomeClass<int> g = 18;
	SomeClass<int> h = 4;
	SomeClass<int> i = 100;
	SomeClass<int> j = 1;
	SomeClass<int> k = 1;
	SomeClass<int> l = 2;

	bbe::List<SomeClass<int>> list2 = { g, h, i, j, k, l };
	ASSERT_EQ(list2[0].getLength(), 18);
	ASSERT_EQ(list2[1].getLength(), 4);
	ASSERT_EQ(list2[2].getLength(), 100);
	ASSERT_EQ(list2[3].getLength(), 1);
	ASSERT_EQ(list2[4].getLength(), 1);
	ASSERT_EQ(list2[5].getLength(), 2);

	list1 += list2;
	ASSERT_EQ(list1[ 0].getLength(), 17);
	ASSERT_EQ(list1[ 1].getLength(), 13);
	ASSERT_EQ(list1[ 2].getLength(), 1337);
	ASSERT_EQ(list1[ 3].getLength(), 2);
	ASSERT_EQ(list1[ 4].getLength(), 4);
	ASSERT_EQ(list1[ 5].getLength(), 2);
	ASSERT_EQ(list1[ 6].getLength(), 18);
	ASSERT_EQ(list1[ 7].getLength(), 4);
	ASSERT_EQ(list1[ 8].getLength(), 100);
	ASSERT_EQ(list1[ 9].getLength(), 1);
	ASSERT_EQ(list1[10].getLength(), 1);
	ASSERT_EQ(list1[11].getLength(), 2);
}

TEST(List, CombineOrderedLists)
{
	SomeClass<int> a = 17;
	SomeClass<int> b = 13;
	SomeClass<int> c = 1337;
	SomeClass<int> d = 2;
	SomeClass<int> e = 4;
	SomeClass<int> f = 2;

	bbe::List<SomeClass<int>, true> list1 = { a, b, c, d, e, f };
	ASSERT_EQ(list1[0].getLength(),    2);
	ASSERT_EQ(list1[1].getLength(),    2);
	ASSERT_EQ(list1[2].getLength(),    4);
	ASSERT_EQ(list1[3].getLength(),   13);
	ASSERT_EQ(list1[4].getLength(),   17);
	ASSERT_EQ(list1[5].getLength(), 1337);
	ASSERT_EQ(list1.getLength(), 6);

	SomeClass<int> g = 18;
	SomeClass<int> h = 4;
	SomeClass<int> i = 100;
	SomeClass<int> j = 1;
	SomeClass<int> k = 1;
	SomeClass<int> l = 2;

	bbe::List<SomeClass<int>, true> list2 = { g, h, i, j, k, l };
	ASSERT_EQ(list2[0].getLength(),   1);
	ASSERT_EQ(list2[1].getLength(),   1);
	ASSERT_EQ(list2[2].getLength(),   2);
	ASSERT_EQ(list2[3].getLength(),   4);
	ASSERT_EQ(list2[4].getLength(),  18);
	ASSERT_EQ(list2[5].getLength(), 100);
	ASSERT_EQ(list2.getLength(), 6);

	list1 += list2;
	ASSERT_EQ(list1[ 0].getLength(),    1);
	ASSERT_EQ(list1[ 1].getLength(),    1);
	ASSERT_EQ(list1[ 2].getLength(),    2);
	ASSERT_EQ(list1[ 3].getLength(),    2);
	ASSERT_EQ(list1[ 4].getLength(),    2);
	ASSERT_EQ(list1[ 5].getLength(),    4);
	ASSERT_EQ(list1[ 6].getLength(),    4);
	ASSERT_EQ(list1[ 7].getLength(),   13);
	ASSERT_EQ(list1[ 8].getLength(),   17);
	ASSERT_EQ(list1[ 9].getLength(),   18);
	ASSERT_EQ(list1[10].getLength(),  100);
	ASSERT_EQ(list1[11].getLength(), 1337);
	ASSERT_EQ(list1.getLength(), 12);
}
<|endoftext|>


<commit_before>// Copyright(C) 2019 Intel Corporation
// Licensed under the MIT License

#include <map>
#include <string>
#include <memory>
#include <sstream>
#include <fstream>

#include <inference_engine.hpp>

#include "core/session/onnxruntime_cxx_api.h"
#include "core/graph/graph.h"
#include "core/common/logging/logging.h"

#include "../contexts.h"
#include "../backend_utils.h"
#include "vadm_backend.h"
#include <vpu/hddl_plugin_config.hpp>

namespace onnxruntime {
namespace openvino_ep {

using namespace backend_utils;

VADMBackend::VADMBackend(const ONNX_NAMESPACE::ModelProto& model_proto,
                         GlobalContext& global_context,
                         const SubGraphContext& subgraph_context)
    : global_context_(global_context), subgraph_context_(subgraph_context) {
  // Infer Request class represents OpenVINO's logical hardware instance. These logical
  // instances are bound to physical hardware instances at runtime depending
  // on the physical hardware availability. If multiple Infer Requests are mapped to
  // the same physical hardware instance, then the inference operations requests from
  // the Infer Requests are serialized before they are scheduled on the physical hardware.
  // If the different Infer Requests are scheduled on different hardware instances, inference
  // operations associated with the Infer Requests may be scheduled in parallel.
  // Infer Requests hold resources representing the entire network on their target hardware. So,
  // having more Infer Requests than needed would waste system resources.
  // In VAD-M (HDDL) accelerator, there are 8 parallel execution units. So, creating 8 instances
  // of Infer Requests only if the VAD-M accelerator is being used.
  // sets number of maximum parallel inferences
  num_inf_reqs_ = 8;

  ie_cnn_network_ = CreateCNNNetwork(model_proto, subgraph_context_.device_id, subgraph_context_.precision);

  SetIODefs(model_proto, ie_cnn_network_);
  std::map<std::string, std::string> config;

  int i = 0;
  // Loading model to the plugin
  //If graph is fully supported and batching is enabled, load the network onto all VPU's and infer
  std::vector<InferenceEngine::ExecutableNetwork> exe_networks;
  if(global_context_.is_wholly_supported_graph && subgraph_context_.enable_batching){
    for(int j = 0; j < 8; j++){
      InferenceEngine::ExecutableNetwork exe_network;
      config[VPU_HDDL_CONFIG_KEY(DEVICE_TAG)] = global_context_.deviceTags[j];
      try {
        exe_network = global_context_.ie_core.LoadNetwork(*ie_cnn_network_, "HDDL", config);
      } catch (InferenceEngine::details::InferenceEngineException e) {
        ORT_THROW(log_tag + " Exception while Loading Network for graph: " + subgraph_context_.subgraph_name + e.what());
      } catch (...) {
        ORT_THROW(log_tag + " Exception while Loading Network for graph " + subgraph_context_.subgraph_name);
      }
      exe_networks.push_back(exe_network);
    }
    LOGS_DEFAULT(INFO) << log_tag << "Loaded model to the plugin";
    for(size_t i = 0; i < num_inf_reqs_; i++) {
      InferenceEngine::InferRequest::Ptr infRequest;
      try {
        infRequest = exe_networks[i].CreateInferRequestPtr();
      } catch(InferenceEngine::details::InferenceEngineException e) {
        ORT_THROW(log_tag + "Exception while creating InferRequest object: " + e.what());
      } catch (...) {
        ORT_THROW(log_tag + "Exception while creating InferRequest object.");
      }
      infer_requests_.push_back(infRequest);
    }
    LOGS_DEFAULT(INFO) << log_tag << "Infer Requests created: " << num_inf_reqs_ << std::endl;
  }
  //If the graph is not fully supported, need to schedule each subgraph on different VPU
  //If batching is disabled just schedule on the first VPU
  else {
    i = GetFirstAvailableDevice(global_context);
    LOGS_DEFAULT(INFO) << log_tag << "Device Tag is: " << i;
    config[VPU_HDDL_CONFIG_KEY(DEVICE_TAG)] = global_context_.deviceTags[i];
    InferenceEngine::ExecutableNetwork exe_network;
    try {
      exe_network = global_context_.ie_core.LoadNetwork(*ie_cnn_network_, "HDDL", config);
    } catch (InferenceEngine::details::InferenceEngineException e) {
      ORT_THROW(log_tag + " Exception while Loading Network for graph: " + subgraph_context_.subgraph_name + e.what());
    } catch (...) {
      ORT_THROW(log_tag + " Exception while Loading Network for graph " + subgraph_context_.subgraph_name);
    }
    LOGS_DEFAULT(INFO) << log_tag << "Loaded model to the plugin";
    InferenceEngine::InferRequest::Ptr infRequest;
    try{
      infRequest = exe_network.CreateInferRequestPtr();
    } catch(InferenceEngine::details::InferenceEngineException e) {
      ORT_THROW(log_tag + "Exception while creating InferRequest object: " + e.what());
    } catch (...) {
      ORT_THROW(log_tag + "Exception while creating InferRequest object.");
    }
    infer_requests_.push_back(infRequest);
    LOGS_DEFAULT(INFO) << log_tag << "Infer Requests created: 1" << std::endl;
  }
}

// Starts an asynchronous inference request for data in slice indexed by batch_slice_idx on
// an Infer Request indexed by infer_req_idx
void VADMBackend::StartAsyncInference(Ort::CustomOpApi& ort, std::vector<const OrtValue*> input_tensors,
                                      size_t batch_slice_idx, size_t infer_req_idx,
                                      std::vector<InferenceEngine::InferRequest::Ptr>& infer_requests,
                                      std::shared_ptr<InferenceEngine::CNNNetwork> ie_cnn_network) {
  auto infer_request = infer_requests[infer_req_idx];
  auto graph_input_info = ie_cnn_network->getInputsInfo();

  size_t i = 0;
  for (auto input_info_iter = graph_input_info.begin();
       input_info_iter != graph_input_info.end(); ++input_info_iter, ++i) {
    // Get OpenVINO's input buffer
    InferenceEngine::Blob::Ptr graph_input_blob;
    try {
      graph_input_blob = infer_request->GetBlob(input_info_iter->first);
    } catch (InferenceEngine::details::InferenceEngineException e) {
      ORT_THROW(log_tag + " Cannot access IE Blob for input: " + input_info_iter->first + e.what());
    } catch (...) {
      ORT_THROW(log_tag + " Cannot access IE Blob for input: " + input_info_iter->first);
    }

    auto graph_input_buffer =
        graph_input_blob->buffer().as<InferenceEngine::PrecisionTrait<InferenceEngine::Precision::FP32>::value_type*>();

    size_t input_data_size = graph_input_blob->byteSize();
    const char* tensor_data = ort.GetTensorData<char>(input_tensors[i]);
    const char* batch_memory_offset = tensor_data + input_data_size * batch_slice_idx;

    // Copy input data into OpenVINO's input buffer
    std::memcpy(graph_input_buffer, batch_memory_offset, input_data_size);
  }

  // Start Async inference
  try {
    infer_request->StartAsync();
  } catch (InferenceEngine::details::InferenceEngineException e) {
    ORT_THROW(log_tag + " Couldn't start Inference: " + e.what());
  } catch (...) {
    ORT_THROW(log_tag + " Couldn't start Inference");
  }
}

// Wait for asynchronous inference completion on an Infer Request object indexed by infer_req_idx
// and copy the results into a slice location within the batched output buffer indexed by batch_slice_idx
void VADMBackend::CompleteAsyncInference(Ort::CustomOpApi& ort, std::vector<OrtValue*> output_tensors,
                                         size_t batch_slice_idx,
                                         size_t infer_req_idx, std::vector<InferenceEngine::InferRequest::Ptr>& infer_requests,
                                         std::shared_ptr<InferenceEngine::CNNNetwork> ie_cnn_network) {
  auto infer_request = infer_requests[infer_req_idx];

  // Wait for Async inference completion
  try {
    infer_request->Wait(InferenceEngine::IInferRequest::WaitMode::RESULT_READY);
  } catch (InferenceEngine::details::InferenceEngineException e) {
    ORT_THROW(log_tag + " Exception with completing Inference: " + e.what());
  } catch (...) {
    ORT_THROW(log_tag + " Exception with completing Inference");
  }
  auto graph_output_info = ie_cnn_network->getOutputsInfo();

  size_t i = 0;
  for (auto output_info_iter = graph_output_info.begin();
       output_info_iter != graph_output_info.end(); ++output_info_iter, ++i) {
    // Get OpenVINO's output blob
    InferenceEngine::Blob::Ptr graph_output_blob;
    try {
      graph_output_blob = infer_request->GetBlob(output_info_iter->first);
    } catch (InferenceEngine::details::InferenceEngineException e) {
      ORT_THROW(log_tag + " Cannot access IE Blob for output: " + output_info_iter->first + e.what());
    } catch (...) {
      ORT_THROW(log_tag + " Cannot access IE Blob for output: " + output_info_iter->first);
    }
    auto graph_output_buffer =
        graph_output_blob->buffer().as<InferenceEngine::PrecisionTrait<InferenceEngine::Precision::FP32>::value_type*>();

    size_t output_data_size = graph_output_blob->byteSize();
    char* tensor_data = ort.GetTensorMutableData<char>(output_tensors[i]);
    char* batch_memory_offset = tensor_data + output_data_size * batch_slice_idx;

    // Copy output results back to ONNX-RT's output buffers
    std::memcpy(batch_memory_offset, graph_output_buffer, output_data_size);
  }
}
size_t DeduceBatchSize(Ort::CustomOpApi ort, const OrtValue* input_tensor,
                       InferenceEngine::SizeVector graph_dims) {
  size_t batch_size = 1;

  // All the inputs and outputs are batched the same way.
  // So it is sufficient to use any one of these tensors to deduce the batch size.
  const auto& input_shape = ort.GetTensorShape(ort.GetTensorTypeAndShape(input_tensor));

  if ((input_shape.size() == graph_dims.size() && input_shape[0] > 1 && graph_dims[0] == 1) || (input_shape.size() == graph_dims.size() + 1)) {
    batch_size = input_shape[0];
  }

  LOGS_DEFAULT(INFO) << log_tag << "Deduced batch size: " << batch_size;

  return batch_size;
}

void VADMBackend::Infer(Ort::CustomOpApi& ort, OrtKernelContext* context) {
  // Preliminary Thread safety mechanism
  // Currently allows only one Infer execution at a time
  LOGS_DEFAULT(INFO) << log_tag << "Running graph " << subgraph_context_.subgraph_name;
  LOGS_DEFAULT(INFO) << log_tag << "In Infer";
  std::lock_guard<std::mutex> lock(compute_lock_);

  // Get Input and Output tensors
  auto input_tensors = GetInputTensors(ort, context, ie_cnn_network_, subgraph_context_.input_indexes);

  size_t batch_size = 1;

  if (subgraph_context_.enable_batching) {
    // Calculate the batch_size from the input tensor shape.
    batch_size = DeduceBatchSize(ort, input_tensors[0],
                                 ie_cnn_network_->getInputsInfo().begin()->second->getTensorDesc().getDims());
  }

  size_t full_parallel_runs = batch_size / num_inf_reqs_;
  size_t remainder_parallel_runs = batch_size % num_inf_reqs_;

  // All infer_requests process identical tensor slices from the batch.
  // So using info from first infer_request to allocate all output tensors.
  auto output_tensors = GetOutputTensors(ort, context, batch_size, infer_requests_[0], ie_cnn_network_, subgraph_context_.output_names);

  // Distribute the batched inputs among available Infer Requests
  // for parallel inference.

  // Run parallel inferences as sets of num_inf_reqs_
  for (size_t set = 0; set < full_parallel_runs; set++) {
    for (size_t inf_req_idx = 0; inf_req_idx < num_inf_reqs_; inf_req_idx++) {
      size_t batch_slice_idx = set * num_inf_reqs_ + inf_req_idx;
      StartAsyncInference(ort, input_tensors, batch_slice_idx, inf_req_idx, infer_requests_, ie_cnn_network_);
    }
    for (size_t inf_req_idx = 0; inf_req_idx < num_inf_reqs_; inf_req_idx++) {
      size_t batch_slice_idx = set * num_inf_reqs_ + inf_req_idx;
      CompleteAsyncInference(ort, output_tensors, batch_slice_idx, inf_req_idx, infer_requests_, ie_cnn_network_);
    }
  }

  // Run parallel inferences for remaining batch slices
  for (size_t inf_req_idx = 0; inf_req_idx < remainder_parallel_runs; inf_req_idx++) {
    size_t batch_slice_idx = full_parallel_runs * num_inf_reqs_ + inf_req_idx;
    StartAsyncInference(ort, input_tensors, batch_slice_idx, inf_req_idx, infer_requests_, ie_cnn_network_);
  }
  for (size_t inf_req_idx = 0; inf_req_idx < remainder_parallel_runs; inf_req_idx++) {
    size_t batch_slice_idx = full_parallel_runs * num_inf_reqs_ + inf_req_idx;
    CompleteAsyncInference(ort, output_tensors, batch_slice_idx, inf_req_idx, infer_requests_, ie_cnn_network_);
  }

  LOGS_DEFAULT(INFO) << log_tag << "Inference successful";
}

}  // namespace openvino_ep
}  // namespace onnxruntime<commit_msg>Rename inner-scoped variable to avoid MSVC warning (#4587)<commit_after>// Copyright(C) 2019 Intel Corporation
// Licensed under the MIT License

#include <map>
#include <string>
#include <memory>
#include <sstream>
#include <fstream>

#include <inference_engine.hpp>

#include "core/session/onnxruntime_cxx_api.h"
#include "core/graph/graph.h"
#include "core/common/logging/logging.h"

#include "../contexts.h"
#include "../backend_utils.h"
#include "vadm_backend.h"
#include <vpu/hddl_plugin_config.hpp>

namespace onnxruntime {
namespace openvino_ep {

using namespace backend_utils;

VADMBackend::VADMBackend(const ONNX_NAMESPACE::ModelProto& model_proto,
                         GlobalContext& global_context,
                         const SubGraphContext& subgraph_context)
    : global_context_(global_context), subgraph_context_(subgraph_context) {
  // Infer Request class represents OpenVINO's logical hardware instance. These logical
  // instances are bound to physical hardware instances at runtime depending
  // on the physical hardware availability. If multiple Infer Requests are mapped to
  // the same physical hardware instance, then the inference operations requests from
  // the Infer Requests are serialized before they are scheduled on the physical hardware.
  // If the different Infer Requests are scheduled on different hardware instances, inference
  // operations associated with the Infer Requests may be scheduled in parallel.
  // Infer Requests hold resources representing the entire network on their target hardware. So,
  // having more Infer Requests than needed would waste system resources.
  // In VAD-M (HDDL) accelerator, there are 8 parallel execution units. So, creating 8 instances
  // of Infer Requests only if the VAD-M accelerator is being used.
  // sets number of maximum parallel inferences
  num_inf_reqs_ = 8;

  ie_cnn_network_ = CreateCNNNetwork(model_proto, subgraph_context_.device_id, subgraph_context_.precision);

  SetIODefs(model_proto, ie_cnn_network_);
  std::map<std::string, std::string> config;

  int i = 0;
  // Loading model to the plugin
  //If graph is fully supported and batching is enabled, load the network onto all VPU's and infer
  std::vector<InferenceEngine::ExecutableNetwork> exe_networks;
  if(global_context_.is_wholly_supported_graph && subgraph_context_.enable_batching){
    for(int j = 0; j < 8; j++){
      InferenceEngine::ExecutableNetwork exe_network;
      config[VPU_HDDL_CONFIG_KEY(DEVICE_TAG)] = global_context_.deviceTags[j];
      try {
        exe_network = global_context_.ie_core.LoadNetwork(*ie_cnn_network_, "HDDL", config);
      } catch (InferenceEngine::details::InferenceEngineException e) {
        ORT_THROW(log_tag + " Exception while Loading Network for graph: " + subgraph_context_.subgraph_name + e.what());
      } catch (...) {
        ORT_THROW(log_tag + " Exception while Loading Network for graph " + subgraph_context_.subgraph_name);
      }
      exe_networks.push_back(exe_network);
    }
    LOGS_DEFAULT(INFO) << log_tag << "Loaded model to the plugin";
    for(size_t j = 0; j < num_inf_reqs_; j++) {
      InferenceEngine::InferRequest::Ptr infRequest;
      try {
        infRequest = exe_networks[j].CreateInferRequestPtr();
      } catch(InferenceEngine::details::InferenceEngineException e) {
        ORT_THROW(log_tag + "Exception while creating InferRequest object: " + e.what());
      } catch (...) {
        ORT_THROW(log_tag + "Exception while creating InferRequest object.");
      }
      infer_requests_.push_back(infRequest);
    }
    LOGS_DEFAULT(INFO) << log_tag << "Infer Requests created: " << num_inf_reqs_ << std::endl;
  }
  //If the graph is not fully supported, need to schedule each subgraph on different VPU
  //If batching is disabled just schedule on the first VPU
  else {
    i = GetFirstAvailableDevice(global_context);
    LOGS_DEFAULT(INFO) << log_tag << "Device Tag is: " << i;
    config[VPU_HDDL_CONFIG_KEY(DEVICE_TAG)] = global_context_.deviceTags[i];
    InferenceEngine::ExecutableNetwork exe_network;
    try {
      exe_network = global_context_.ie_core.LoadNetwork(*ie_cnn_network_, "HDDL", config);
    } catch (InferenceEngine::details::InferenceEngineException e) {
      ORT_THROW(log_tag + " Exception while Loading Network for graph: " + subgraph_context_.subgraph_name + e.what());
    } catch (...) {
      ORT_THROW(log_tag + " Exception while Loading Network for graph " + subgraph_context_.subgraph_name);
    }
    LOGS_DEFAULT(INFO) << log_tag << "Loaded model to the plugin";
    InferenceEngine::InferRequest::Ptr infRequest;
    try{
      infRequest = exe_network.CreateInferRequestPtr();
    } catch(InferenceEngine::details::InferenceEngineException e) {
      ORT_THROW(log_tag + "Exception while creating InferRequest object: " + e.what());
    } catch (...) {
      ORT_THROW(log_tag + "Exception while creating InferRequest object.");
    }
    infer_requests_.push_back(infRequest);
    LOGS_DEFAULT(INFO) << log_tag << "Infer Requests created: 1" << std::endl;
  }
}

// Starts an asynchronous inference request for data in slice indexed by batch_slice_idx on
// an Infer Request indexed by infer_req_idx
void VADMBackend::StartAsyncInference(Ort::CustomOpApi& ort, std::vector<const OrtValue*> input_tensors,
                                      size_t batch_slice_idx, size_t infer_req_idx,
                                      std::vector<InferenceEngine::InferRequest::Ptr>& infer_requests,
                                      std::shared_ptr<InferenceEngine::CNNNetwork> ie_cnn_network) {
  auto infer_request = infer_requests[infer_req_idx];
  auto graph_input_info = ie_cnn_network->getInputsInfo();

  size_t i = 0;
  for (auto input_info_iter = graph_input_info.begin();
       input_info_iter != graph_input_info.end(); ++input_info_iter, ++i) {
    // Get OpenVINO's input buffer
    InferenceEngine::Blob::Ptr graph_input_blob;
    try {
      graph_input_blob = infer_request->GetBlob(input_info_iter->first);
    } catch (InferenceEngine::details::InferenceEngineException e) {
      ORT_THROW(log_tag + " Cannot access IE Blob for input: " + input_info_iter->first + e.what());
    } catch (...) {
      ORT_THROW(log_tag + " Cannot access IE Blob for input: " + input_info_iter->first);
    }

    auto graph_input_buffer =
        graph_input_blob->buffer().as<InferenceEngine::PrecisionTrait<InferenceEngine::Precision::FP32>::value_type*>();

    size_t input_data_size = graph_input_blob->byteSize();
    const char* tensor_data = ort.GetTensorData<char>(input_tensors[i]);
    const char* batch_memory_offset = tensor_data + input_data_size * batch_slice_idx;

    // Copy input data into OpenVINO's input buffer
    std::memcpy(graph_input_buffer, batch_memory_offset, input_data_size);
  }

  // Start Async inference
  try {
    infer_request->StartAsync();
  } catch (InferenceEngine::details::InferenceEngineException e) {
    ORT_THROW(log_tag + " Couldn't start Inference: " + e.what());
  } catch (...) {
    ORT_THROW(log_tag + " Couldn't start Inference");
  }
}

// Wait for asynchronous inference completion on an Infer Request object indexed by infer_req_idx
// and copy the results into a slice location within the batched output buffer indexed by batch_slice_idx
void VADMBackend::CompleteAsyncInference(Ort::CustomOpApi& ort, std::vector<OrtValue*> output_tensors,
                                         size_t batch_slice_idx,
                                         size_t infer_req_idx, std::vector<InferenceEngine::InferRequest::Ptr>& infer_requests,
                                         std::shared_ptr<InferenceEngine::CNNNetwork> ie_cnn_network) {
  auto infer_request = infer_requests[infer_req_idx];

  // Wait for Async inference completion
  try {
    infer_request->Wait(InferenceEngine::IInferRequest::WaitMode::RESULT_READY);
  } catch (InferenceEngine::details::InferenceEngineException e) {
    ORT_THROW(log_tag + " Exception with completing Inference: " + e.what());
  } catch (...) {
    ORT_THROW(log_tag + " Exception with completing Inference");
  }
  auto graph_output_info = ie_cnn_network->getOutputsInfo();

  size_t i = 0;
  for (auto output_info_iter = graph_output_info.begin();
       output_info_iter != graph_output_info.end(); ++output_info_iter, ++i) {
    // Get OpenVINO's output blob
    InferenceEngine::Blob::Ptr graph_output_blob;
    try {
      graph_output_blob = infer_request->GetBlob(output_info_iter->first);
    } catch (InferenceEngine::details::InferenceEngineException e) {
      ORT_THROW(log_tag + " Cannot access IE Blob for output: " + output_info_iter->first + e.what());
    } catch (...) {
      ORT_THROW(log_tag + " Cannot access IE Blob for output: " + output_info_iter->first);
    }
    auto graph_output_buffer =
        graph_output_blob->buffer().as<InferenceEngine::PrecisionTrait<InferenceEngine::Precision::FP32>::value_type*>();

    size_t output_data_size = graph_output_blob->byteSize();
    char* tensor_data = ort.GetTensorMutableData<char>(output_tensors[i]);
    char* batch_memory_offset = tensor_data + output_data_size * batch_slice_idx;

    // Copy output results back to ONNX-RT's output buffers
    std::memcpy(batch_memory_offset, graph_output_buffer, output_data_size);
  }
}
size_t DeduceBatchSize(Ort::CustomOpApi ort, const OrtValue* input_tensor,
                       InferenceEngine::SizeVector graph_dims) {
  size_t batch_size = 1;

  // All the inputs and outputs are batched the same way.
  // So it is sufficient to use any one of these tensors to deduce the batch size.
  const auto& input_shape = ort.GetTensorShape(ort.GetTensorTypeAndShape(input_tensor));

  if ((input_shape.size() == graph_dims.size() && input_shape[0] > 1 && graph_dims[0] == 1) || (input_shape.size() == graph_dims.size() + 1)) {
    batch_size = input_shape[0];
  }

  LOGS_DEFAULT(INFO) << log_tag << "Deduced batch size: " << batch_size;

  return batch_size;
}

void VADMBackend::Infer(Ort::CustomOpApi& ort, OrtKernelContext* context) {
  // Preliminary Thread safety mechanism
  // Currently allows only one Infer execution at a time
  LOGS_DEFAULT(INFO) << log_tag << "Running graph " << subgraph_context_.subgraph_name;
  LOGS_DEFAULT(INFO) << log_tag << "In Infer";
  std::lock_guard<std::mutex> lock(compute_lock_);

  // Get Input and Output tensors
  auto input_tensors = GetInputTensors(ort, context, ie_cnn_network_, subgraph_context_.input_indexes);

  size_t batch_size = 1;

  if (subgraph_context_.enable_batching) {
    // Calculate the batch_size from the input tensor shape.
    batch_size = DeduceBatchSize(ort, input_tensors[0],
                                 ie_cnn_network_->getInputsInfo().begin()->second->getTensorDesc().getDims());
  }

  size_t full_parallel_runs = batch_size / num_inf_reqs_;
  size_t remainder_parallel_runs = batch_size % num_inf_reqs_;

  // All infer_requests process identical tensor slices from the batch.
  // So using info from first infer_request to allocate all output tensors.
  auto output_tensors = GetOutputTensors(ort, context, batch_size, infer_requests_[0], ie_cnn_network_, subgraph_context_.output_names);

  // Distribute the batched inputs among available Infer Requests
  // for parallel inference.

  // Run parallel inferences as sets of num_inf_reqs_
  for (size_t set = 0; set < full_parallel_runs; set++) {
    for (size_t inf_req_idx = 0; inf_req_idx < num_inf_reqs_; inf_req_idx++) {
      size_t batch_slice_idx = set * num_inf_reqs_ + inf_req_idx;
      StartAsyncInference(ort, input_tensors, batch_slice_idx, inf_req_idx, infer_requests_, ie_cnn_network_);
    }
    for (size_t inf_req_idx = 0; inf_req_idx < num_inf_reqs_; inf_req_idx++) {
      size_t batch_slice_idx = set * num_inf_reqs_ + inf_req_idx;
      CompleteAsyncInference(ort, output_tensors, batch_slice_idx, inf_req_idx, infer_requests_, ie_cnn_network_);
    }
  }

  // Run parallel inferences for remaining batch slices
  for (size_t inf_req_idx = 0; inf_req_idx < remainder_parallel_runs; inf_req_idx++) {
    size_t batch_slice_idx = full_parallel_runs * num_inf_reqs_ + inf_req_idx;
    StartAsyncInference(ort, input_tensors, batch_slice_idx, inf_req_idx, infer_requests_, ie_cnn_network_);
  }
  for (size_t inf_req_idx = 0; inf_req_idx < remainder_parallel_runs; inf_req_idx++) {
    size_t batch_slice_idx = full_parallel_runs * num_inf_reqs_ + inf_req_idx;
    CompleteAsyncInference(ort, output_tensors, batch_slice_idx, inf_req_idx, infer_requests_, ie_cnn_network_);
  }

  LOGS_DEFAULT(INFO) << log_tag << "Inference successful";
}

}  // namespace openvino_ep
}  // namespace onnxruntime<|endoftext|>


<commit_before>// Hossein Moein
// Juine 4, 2019
// Copyright (C) 2019-2022 Hossein Moein
// Distributed under the BSD Software License (see file License)

#include <DataFrame/FixedSizeString.h>
#include <DataFrame/MMap/ObjectVector.h>

#include <exception>
#include <sys/types.h>
#include <sys/mman.h>

#ifndef _WIN32

//
// There must be a nice concise language inside C++ trying to get out
//

// ----------------------------------------------------------------------------

namespace hmdf
{

template<typename T, typename B>
ObjectVector<T, B>::
ObjectVector(const char *name, ACCESS_MODE access_mode, size_type buffer_size)
    : BaseClass (name, BaseClass::_bappend_, buffer_size)  {

    const bool  just_created = BaseClass::get_file_size () == 0;

    if (just_created)  {
        const _internal_header_type meta_data (0, time (nullptr));

       // Create the meta data record
       //
        if (! BaseClass::write (&meta_data, sizeof(MetaDate), 1))
            throw std::runtime_error ("ObjectVector::ObjectVector<<(): Cannot"
                                      " write(). internal header record");

        flush ();
    }
    else  {
        if (BaseClass::get_file_size () < _DATA_START_POINT)  {
            String1K    err;

            err.printf ("ObjectVector::ObjectVector(): "
                        "ObjectVector seems to be in an inconsistent "
                        "state (%d).",
                        BaseClass::get_file_size ());
            throw std::runtime_error (err.c_str ());
        }
    }

    cached_object_count_ = mdata_ptr->object_count;
    seek (cached_object_count_);
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
typename ObjectVector<T, B>::size_type
ObjectVector<T, B>::tell () const noexcept  {

    const size_type pos = BaseClass::tell ();

    return ((pos - _DATA_START_POINT) / sizeof(value_type));
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
ObjectVector<T, B>::~ObjectVector ()  {

    if (BaseClass::get_device_type () == BaseClass::_shared_memory_ ||
        BaseClass::get_device_type () == BaseClass::_mmap_file_)
        flush ();
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
inline typename ObjectVector<T, B>::data_type &
ObjectVector<T, B>::operator [] (size_type index)  {

    data_type   &this_item =
        *(reinterpret_cast<data_type *>
              (reinterpret_cast<char *>(BaseClass::_get_base_ptr ()) +
               _DATA_START_POINT) +
          index);

    return (this_item);
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
inline const typename ObjectVector<T, B>::data_type &
ObjectVector<T, B>::operator [] (size_type index) const noexcept  {

    const data_type &this_item =
        *(reinterpret_cast<const data_type *>
              (reinterpret_cast<const char *>(BaseClass::_get_base_ptr ()) +
               _DATA_START_POINT) +
          index);

    return (this_item);
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
time_t ObjectVector<T, B>::creation_time () const noexcept  {

    const _internal_header_type &meta_data =
        *reinterpret_cast<const _internal_header_type *>
            (reinterpret_cast<const char *>(BaseClass::_get_base_ptr ());

    return (meta_data.creation_time);
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
void ObjectVector<T, B>::set_access_mode (ACCESS_MODE am) const  {

    if (empty ())
        return;

    const   int rc =
        ::posix_madvise (BaseClass::_get_base_ptr (),
                         BaseClass::get_mmap_size (),
                         (am == _normal_) ? POSIX_MADV_NORMAL
                             : (am == _need_now_) ? POSIX_MADV_WILLNEED
                             : (am == _random_) ? POSIX_MADV_RANDOM
                             : (am == _sequential_) ? POSIX_MADV_SEQUENTIAL
                             : (am == _dont_need_) ? POSIX_MADV_DONTNEED
                             : -1);

    if (rc)  {
        String1K    err;

        err.printf ("ObjectVector::set_access_mode(): ::posix_madvise(): "
                    "(%d) %s",
                    errno, strerror (errno));
        throw std::runtime_error (err.c_str ());
    }

    return;
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
bool ObjectVector<T, B>::seek_ (size_type obj_num) const noexcept  {

    BaseClass   *nc_ptr =
        const_cast<BaseClass *>(static_cast<const BaseClass *>(this));

    return (nc_ptr->seek (_DATA_START_POINT + (obj_num * sizeof(value_type)),
                          BaseClass::_seek_set_) == 0 ? true : false);
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
int ObjectVector<T, B>::write_ (const data_type *data_ele, size_type count)  {

    const   int rc = BaseClass::write (data_ele, sizeof(value_type), count);

    if (rc != count)  {
        String1K    err;

        err.printf ("ObjectVector::write(): Cannot write %llu elements. "
                    "Instead wrote %d elements.",
                    count, rc);
        throw std::runtime_error (err.c_str ());
    }

    _internal_header_type   &meta_data =
        *reinterpret_cast<_internal_header_type *>
            (reinterpret_cast<char *>(BaseClass::_get_base_ptr ());

    meta_data.object_count += count;
    cached_object_count_ += count;
    return (rc);
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
typename ObjectVector<T, B>::iterator
ObjectVector<T, B>::erase (iterator first, iterator last)  {

    const size_type lnum = &(*last) - &(*begin ());

    ::memmove (&(*first), &(*last),
               (&(*end ()) - &(*last)) * sizeof (data_type));

    const size_type s = &(*last) - &(*first);

    BaseClass::truncate (BaseClass::_file_size - s * sizeof (data_type));

    _internal_header_type   &meta_data =
        *reinterpret_cast<_internal_header_type *>
            (reinterpret_cast<char *>(BaseClass::_get_base_ptr ());

    meta_data.object_count -= s;
    cached_object_count_ -= s;
    seek (cached_object_count_);

    return (iterator_at (lnum - s));
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
template<typename I>
void ObjectVector<T, B>::insert (iterator pos, I first, I last)  {

    const long      int to_add = &(*last) - &(*first);
    const size_type pos_index = &(*pos) - &(*begin ());

    BaseClass::truncate (BaseClass::_file_size + to_add * sizeof (data_type));

    const iterator  new_pos = iterator_at (pos_index);

    ::memmove (&(*(new_pos + to_add)), &(*new_pos),
               (&(*end ()) - &(*new_pos)) * sizeof (data_type));
    ::memcpy (&(*new_pos), &(*first), to_add * sizeof (data_type));

    _internal_header_type   &meta_data =
        *reinterpret_cast<_internal_header_type *>
            (reinterpret_cast<char *>(BaseClass::_get_base_ptr ());

    meta_data.object_count += to_add;
    cached_object_count_ += to_add;
    seek (cached_object_count_);

    return;
}

} // namespace hmdf

#endif // _WIN32

// ----------------------------------------------------------------------------

// Local Variables:
// mode:C++
// tab-width:4
// c-basic-offset:4
// End:
<commit_msg>Fixed mmap files problems<commit_after>// Hossein Moein
// Juine 4, 2019
// Copyright (C) 2019-2022 Hossein Moein
// Distributed under the BSD Software License (see file License)

#include <DataFrame/FixedSizeString.h>
#include <DataFrame/MMap/ObjectVector.h>

#include <exception>
#include <sys/types.h>
#include <sys/mman.h>

#ifndef _WIN32

//
// There must be a nice concise language inside C++ trying to get out
//

// ----------------------------------------------------------------------------

namespace hmdf
{

template<typename T, typename B>
ObjectVector<T, B>::
ObjectVector(const char *name, ACCESS_MODE access_mode, size_type buffer_size)
    : BaseClass (name, BaseClass::_bappend_, buffer_size)  {

    const bool  just_created = BaseClass::get_file_size () == 0;

    if (just_created)  {
        const _internal_header_type meta_data (0, time (nullptr));

       // Create the meta data record
       //
        if (! BaseClass::write (&meta_data, sizeof(MetaDate), 1))
            throw std::runtime_error ("ObjectVector::ObjectVector<<(): Cannot"
                                      " write(). internal header record");

        flush ();
    }
    else  {
        if (BaseClass::get_file_size () < sizeof(MetaData))  {
            String1K    err;

            err.printf ("ObjectVector::ObjectVector(): "
                        "ObjectVector seems to be in an inconsistent "
                        "state (%d).",
                        BaseClass::get_file_size ());
            throw std::runtime_error (err.c_str ());
        }
    }

    cached_object_count_ = mdata_ptr->object_count;
    seek (cached_object_count_);
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
typename ObjectVector<T, B>::size_type
ObjectVector<T, B>::tell () const noexcept  {

    const size_type pos = BaseClass::tell ();

    return ((pos - sizeof(MetaData)) / sizeof(value_type));
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
ObjectVector<T, B>::~ObjectVector ()  {

    if (BaseClass::get_device_type () == BaseClass::_shared_memory_ ||
        BaseClass::get_device_type () == BaseClass::_mmap_file_)
        flush ();
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
inline typename ObjectVector<T, B>::data_type &
ObjectVector<T, B>::operator [] (size_type index)  {

    data_type   &this_item =
        *(reinterpret_cast<data_type *>
              (reinterpret_cast<char *>(BaseClass::_get_base_ptr ()) +
               sizeof(MetaData)) +
          index);

    return (this_item);
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
inline const typename ObjectVector<T, B>::data_type &
ObjectVector<T, B>::operator [] (size_type index) const noexcept  {

    const data_type &this_item =
        *(reinterpret_cast<const data_type *>
              (reinterpret_cast<const char *>(BaseClass::_get_base_ptr ()) +
               sizeof(MetaData)) +
          index);

    return (this_item);
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
time_t ObjectVector<T, B>::creation_time () const noexcept  {

    const _internal_header_type &meta_data =
        *reinterpret_cast<const _internal_header_type *>
            (reinterpret_cast<const char *>(BaseClass::_get_base_ptr ());

    return (meta_data.creation_time);
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
void ObjectVector<T, B>::set_access_mode (ACCESS_MODE am) const  {

    if (empty ())
        return;

    const   int rc =
        ::posix_madvise (BaseClass::_get_base_ptr (),
                         BaseClass::get_mmap_size (),
                         (am == _normal_) ? POSIX_MADV_NORMAL
                             : (am == _need_now_) ? POSIX_MADV_WILLNEED
                             : (am == _random_) ? POSIX_MADV_RANDOM
                             : (am == _sequential_) ? POSIX_MADV_SEQUENTIAL
                             : (am == _dont_need_) ? POSIX_MADV_DONTNEED
                             : -1);

    if (rc)  {
        String1K    err;

        err.printf ("ObjectVector::set_access_mode(): ::posix_madvise(): "
                    "(%d) %s",
                    errno, strerror (errno));
        throw std::runtime_error (err.c_str ());
    }

    return;
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
bool ObjectVector<T, B>::seek_ (size_type obj_num) const noexcept  {

    BaseClass   *nc_ptr =
        const_cast<BaseClass *>(static_cast<const BaseClass *>(this));

    return (nc_ptr->seek (sizeof(MetaData) + (obj_num * sizeof(value_type)),
                          BaseClass::_seek_set_) == 0 ? true : false);
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
int ObjectVector<T, B>::write_ (const data_type *data_ele, size_type count)  {

    const   int rc = BaseClass::write (data_ele, sizeof(value_type), count);

    if (rc != count)  {
        String1K    err;

        err.printf ("ObjectVector::write(): Cannot write %llu elements. "
                    "Instead wrote %d elements.",
                    count, rc);
        throw std::runtime_error (err.c_str ());
    }

    _internal_header_type   &meta_data =
        *reinterpret_cast<_internal_header_type *>
            (reinterpret_cast<char *>(BaseClass::_get_base_ptr ());

    meta_data.object_count += count;
    cached_object_count_ += count;
    return (rc);
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
typename ObjectVector<T, B>::iterator
ObjectVector<T, B>::erase (iterator first, iterator last)  {

    const size_type lnum = &(*last) - &(*begin ());

    ::memmove (&(*first), &(*last),
               (&(*end ()) - &(*last)) * sizeof (data_type));

    const size_type s = &(*last) - &(*first);

    BaseClass::truncate (BaseClass::_file_size - s * sizeof (data_type));

    _internal_header_type   &meta_data =
        *reinterpret_cast<_internal_header_type *>
            (reinterpret_cast<char *>(BaseClass::_get_base_ptr ());

    meta_data.object_count -= s;
    cached_object_count_ -= s;
    seek (cached_object_count_);

    return (iterator_at (lnum - s));
}

// ----------------------------------------------------------------------------

template<typename T, typename B>
template<typename I>
void ObjectVector<T, B>::insert (iterator pos, I first, I last)  {

    const long      int to_add = &(*last) - &(*first);
    const size_type pos_index = &(*pos) - &(*begin ());

    BaseClass::truncate (BaseClass::_file_size + to_add * sizeof (data_type));

    const iterator  new_pos = iterator_at (pos_index);

    ::memmove (&(*(new_pos + to_add)), &(*new_pos),
               (&(*end ()) - &(*new_pos)) * sizeof (data_type));
    ::memcpy (&(*new_pos), &(*first), to_add * sizeof (data_type));

    _internal_header_type   &meta_data =
        *reinterpret_cast<_internal_header_type *>
            (reinterpret_cast<char *>(BaseClass::_get_base_ptr ());

    meta_data.object_count += to_add;
    cached_object_count_ += to_add;
    seek (cached_object_count_);

    return;
}

} // namespace hmdf

#endif // _WIN32

// ----------------------------------------------------------------------------

// Local Variables:
// mode:C++
// tab-width:4
// c-basic-offset:4
// End:
<|endoftext|>