Beispiel #1
0
   static void VNGThreaded( Image& target, const GenericImage<P>& source, const DebayerInstance& instance )
   {
      int target_w = source.Width();
      int target_h = source.Height();

      target.AllocateData( target_w, target_h, 3, ColorSpace::RGB );

      target.Status().Initialize( "VNG debayering", target_h-4 );

      int numberOfThreads = Thread::NumberOfThreads( target_h-4, 1 );
      int rowsPerThread = (target_h - 4)/numberOfThreads;

      AbstractImage::ThreadData data( target, target_h-4 );

      ReferenceArray<VNGThread<P> > threads;
      for ( int i = 0, j = 1; i < numberOfThreads; ++i, ++j )
         threads.Add( new VNGThread<P>( data, target, source, instance,
                                        i*rowsPerThread + 2,
                                        (j < numberOfThreads) ? j*rowsPerThread + 2 : target_h-2 ) );

      AbstractImage::RunThreads( threads, data );
      threads.Destroy();

      // copy top and bottom two rows from the adjecent ones
      for ( int col = 0; col < target_w; col++ )
         for ( int i = 0; i < 3; i++ )
         {
            target.Pixel( col, 0, i ) = target.Pixel( col, 1, i ) = target.Pixel( col, 2, i );
            target.Pixel( col, target_h-1, i ) = target.Pixel( col, target_h-2, i ) = target.Pixel( col, target_h-3, i );
         }

      target.Status() = data.status;
   }
   template <class P> static
   void Apply( GenericImage<P>& image, const HistogramTransformation& H )
   {
      if ( image.IsEmptySelection() )
         return;

      image.EnsureUnique();

      Rect r = image.SelectedRectangle();
      int h = r.Height();

      int numberOfThreads = H.IsParallelProcessingEnabled() ? Min( H.MaxProcessors(), pcl::Thread::NumberOfThreads( h, 1 ) ) : 1;
      int rowsPerThread = h/numberOfThreads;

      size_type N = image.NumberOfSelectedSamples();
      if ( image.Status().IsInitializationEnabled() )
         image.Status().Initialize( "Histogram transformation", N );

      ThreadData<P> data( image, H, N );

      ReferenceArray<Thread<P> > threads;
      for ( int i = 0, j = 1; i < numberOfThreads; ++i, ++j )
         threads.Add( new Thread<P>( data, i*rowsPerThread, (j < numberOfThreads) ? j*rowsPerThread : h ) );

      AbstractImage::RunThreads( threads, data );
      threads.Destroy();

      image.Status() = data.status;
   }
Beispiel #3
0
   static void SuperPixelThreaded( Image& target, const GenericImage<P>& source, const DebayerInstance& instance )
   {
      int target_w = source.Width() >> 1;
      int target_h = source.Height() >> 1;

      target.AllocateData( target_w, target_h, 3, ColorSpace::RGB );

      target.Status().Initialize( "SuperPixel debayering", target_h );

      int numberOfThreads = Thread::NumberOfThreads( target_h, 1 );
      int rowsPerThread = target_h/numberOfThreads;

      AbstractImage::ThreadData data( target, target_h );

      ReferenceArray<SuperPixelThread<P> > threads;
      for ( int i = 0, j = 1; i < numberOfThreads; ++i, ++j )
         threads.Add( new SuperPixelThread<P>( data, target, source, instance,
                                               i*rowsPerThread,
                                               (j < numberOfThreads) ? j*rowsPerThread : target_h ) );

      AbstractImage::RunThreads( threads, data );
      threads.Destroy();

      target.Status() = data.status;
   }
   static void Apply( GenericImage<P>& image, const ColorSaturationInstance& instance, bool useLUT = false )
   {
      if ( instance.Curve().IsIdentity() )
      {
         Console().WriteLn( "<end><cbr>&lt;* Identity *&gt;" );
         return;
      }

      size_type N = image.NumberOfPixels();

      int numberOfThreads = Thread::NumberOfThreads( N, 16 );
      size_type pixelsPerThread = N/numberOfThreads;

      image.Status().Initialize( "Color saturation transformation, HSVL space", N );

      ThreadData data( image, N );
      if ( useLUT )
         data.lut = MakeLUT( instance );

      ReferenceArray<ColorSaturationThread<P> > threads;
      for ( int i = 0, j = 1; i < numberOfThreads; ++i, ++j )
         threads.Add( new ColorSaturationThread<P>( instance, data, image,
                                                    i*pixelsPerThread,
                                                    (j < numberOfThreads) ? j*pixelsPerThread : N ) );
      AbstractImage::RunThreads( threads, data );

      threads.Destroy();

      image.Status() = data.status;
   }
void HistogramTransformation::Make24BitLUT( uint16* lut ) const
{
   if ( lut == 0 )
      return;

   int numberOfThreads = m_parallel ? Min( int( m_maxProcessors ), Thread::NumberOfThreads( uint24_max+1, 256 ) ) : 1;
   int itemsPerThread = (uint24_max + 1)/numberOfThreads;
   bool useAffinity = m_parallel && Thread::IsRootThread();

   ReferenceArray<LUT2416Thread> threads;
   for ( int i = 0, j = 1; i < numberOfThreads; ++i, ++j )
      threads.Add( new LUT2416Thread( lut, *this,
                                      i*itemsPerThread, (j < numberOfThreads) ? j*itemsPerThread : uint24_max+1 ) );
   if ( numberOfThreads > 1 )
   {
      for ( int i = 0; i < numberOfThreads; ++i )
         threads[i].Start( ThreadPriority::DefaultMax, useAffinity ? i : -1 );
      for ( int i = 0; i < numberOfThreads; ++i )
         threads[i].Wait();
   }
   else
      threads[0].Run();

   threads.Destroy();
}
Beispiel #6
0
   static void EvaluateNoise( FVector& noiseEstimates,
                              FVector& noiseFractions,
                              StringList& noiseAlgorithms,
                              const Image& image, int algorithm )
   {
      SpinStatus spin;
      image.SetStatusCallback( &spin );
      image.Status().Initialize( "Noise evaluation" );
      image.Status().DisableInitialization();

      Console console;

      int numberOfThreads = Thread::NumberOfThreads( image.NumberOfPixels(), 1 );
      if ( numberOfThreads >= 3 )
      {
         int numberOfSubthreads = RoundI( numberOfThreads/3.0 );
         ReferenceArray<NoiseEvaluationThread> threads;
         threads.Add( new NoiseEvaluationThread( image, 0, algorithm, numberOfSubthreads ) );
         threads.Add( new NoiseEvaluationThread( image, 1, algorithm, numberOfSubthreads ) );
         threads.Add( new NoiseEvaluationThread( image, 2, algorithm, numberOfThreads - 2*numberOfSubthreads ) );

         AbstractImage::ThreadData data( image, 0 ); // unbounded
         AbstractImage::RunThreads( threads, data );

         for ( int i = 0; i < 3; ++i )
         {
            noiseEstimates[i]  = threads[i].noiseEstimate;
            noiseFractions[i]  = threads[i].noiseFraction;
            noiseAlgorithms[i] = String( threads[i].noiseAlgorithm );
         }

         threads.Destroy();
      }
      else
      {
         for ( int i = 0; i < 3; ++i )
         {
            NoiseEvaluationThread thread( image, i, algorithm, 1 );
            thread.Run();
            noiseEstimates[i]  = thread.noiseEstimate;
            noiseFractions[i]  = thread.noiseFraction;
            noiseAlgorithms[i] = String( thread.noiseAlgorithm );
         }
      }

      image.ResetSelections();
      image.Status().Complete();

      console.WriteLn( "<end><cbr>Gaussian noise estimates:" );
      for ( int i = 0; i < 3; ++i )
         console.WriteLn( String().Format( "s%d = %.3e, n%d = %.4f ",
                              i, noiseEstimates[i], i, noiseFractions[i] ) + '(' + noiseAlgorithms[i] + ')' );
   }
   static void Apply( GenericImage<P>& image, const CurvesTransformationInstance& instance, bool useLUT = false )
   {
      int numberOfCurves = 0;

      if ( !instance[CurveIndex::RGBK].IsIdentity() )
         numberOfCurves = image.NumberOfNominalChannels();

      if ( image.IsColor() )
      {
         for ( int c = 0; c < image.NumberOfNominalChannels(); ++c )
            if ( !instance[c].IsIdentity() )
               ++numberOfCurves;
         if ( !instance[CurveIndex::L].IsIdentity() || !instance[CurveIndex::a].IsIdentity() || !instance[CurveIndex::b].IsIdentity() || !instance[CurveIndex::c].IsIdentity() )
            ++numberOfCurves;
         if ( !instance[CurveIndex::H].IsIdentity() || !instance[CurveIndex::S].IsIdentity() )
            ++numberOfCurves;
      }

      if ( image.HasAlphaChannels() && !instance[CurveIndex::A].IsIdentity() )
         ++numberOfCurves;

      if ( numberOfCurves == 0 )
      {
         Console().WriteLn( "<end><cbr>&lt;* Identity *&gt;" );
         return;
      }

      size_type N = image.NumberOfPixels();

      int numberOfThreads = Thread::NumberOfThreads( N, 256 );
      size_type pixelsPerThread = N/numberOfThreads;

      image.Status().Initialize( "Curves transformation", numberOfCurves*N );

      ThreadData data( image, numberOfCurves*N );
      if ( useLUT )
         data.lut.Generate( image, instance );

      ReferenceArray<CurvesThread<P> > threads;
      for ( int i = 0, j = 1; i < numberOfThreads; ++i, ++j )
         threads.Add( new CurvesThread<P>( instance, data, image,
                                           i*pixelsPerThread,
                                           (j < numberOfThreads) ? j*pixelsPerThread : N ) );

      AbstractImage::RunThreads( threads, data );
      threads.Destroy();

      image.Status() = data.status;
   }
   static void Apply( GenericImage<P>& image, const LocalHistogramEqualizationInstance& instance )
   {
      if ( image.IsColor() )
      {
         Image L;
         image.GetLightness( L );
         L.Status() = image.Status();
         Apply( L, instance );
         image.Status() = L.Status();
         image.SetLightness( L );
         return;
      }

      // create copy of the luminance to evaluate histogram from
      GenericImage<P> imageCopy( image );
      imageCopy.EnsureUnique(); // really not necessary, but we'll be safer if this is done

      size_type N = image.NumberOfPixels();

      int numberOfThreads = Thread::NumberOfThreads( image.Height(), 1 );
      int rowsPerThread = image.Height()/numberOfThreads;

      image.Status().Initialize( "CLAHE", N );

      AbstractImage::ThreadData data( image, N );

      // create processing threads
      ReferenceArray<LocalHistogramEqualizationThread<P> > threads;
      for ( int i = 0, j = 1; i < numberOfThreads; ++i, ++j )
         threads.Add( new LocalHistogramEqualizationThread<P>( data,
                                 instance,
                                 image,
                                 imageCopy,
                                 i*rowsPerThread,
                                 (j < numberOfThreads) ? j*rowsPerThread : image.Height() ) );

      AbstractImage::RunThreads( threads, data );

      threads.Destroy();

      image.Status() = data.status;
   }
   template <class P, class S> static
   void Apply( GenericImage<P>& image, S*,
               const ICCProfileTransformation& T,
               ICCProfileTransformation::transformation_handle transformation )
   {
      if ( image.IsEmptySelection() || T.Profiles().IsEmpty() )
         return;

      if ( image.ColorSpace() != ColorSpace::RGB && image.ColorSpace() != ColorSpace::Gray )
         throw Error( String().Format( "Unsupported color space %X in ICC color transformation.", image.ColorSpace() ) );

      image.EnsureUnique();

      Rect r = image.SelectedRectangle();
      int h = r.Height();

      int numberOfThreads = T.IsParallelProcessingEnabled() ? Min( T.MaxProcessors(), pcl::Thread::NumberOfThreads( h, 1 ) ) : 1;
      int rowsPerThread = h/numberOfThreads;

      size_type N = image.NumberOfSelectedPixels();
      if ( image.Status().IsInitializationEnabled() )
         image.Status().Initialize( "In-place ICC color profile transformation", N );

      ThreadData<P> data( image, T, transformation, N );

      ReferenceArray<Thread<P,S> > threads;
      for ( int i = 0, j = 1; i < numberOfThreads; ++i, ++j )
         threads.Add( new Thread<P,S>( data,
                                       i*rowsPerThread,
                                       (j < numberOfThreads) ? j*rowsPerThread : h ) );

      AbstractImage::RunThreads( threads, data );

      threads.Destroy();

      image.Status() = data.status;
   }
   template <class P> static
   void Apply( GenericImage<P>& image, const MorphologicalTransformation& transformation )
   {
      if ( image.IsEmptySelection() )
         return;

      image.EnsureUnique();

      int n = transformation.OverlappingDistance();
      if ( n > image.Height() || n > image.Width() )
      {
         image.Zero();
         return;
      }

      /*
       * Dilation requires a reflected structure. We'll unreflect it once the
       * transformation has finished.
       */
      bool didReflect = false;
      if ( transformation.Operator().IsDilation() != transformation.Structure().IsReflected() )
      {
         const_cast<StructuringElement&>( transformation.Structure() ).Reflect();
         didReflect = true;
      }

      int numberOfRows = image.SelectedRectangle().Height();
      int numberOfThreads = transformation.IsParallelProcessingEnabled() ?
               Min( transformation.MaxProcessors(), pcl::Thread::NumberOfThreads( numberOfRows, n ) ) : 1;
      int rowsPerThread = numberOfRows/numberOfThreads;

      size_type N = image.NumberOfSelectedSamples();
      if ( image.Status().IsInitializationEnabled() )
         image.Status().Initialize( "Morphological transformation, " + transformation.Operator().Description(), N );

      ThreadData<P> data( image, transformation, N );

      ReferenceArray<Thread<P> > threads;
      for ( int i = 0, j = 1, y0 = image.SelectedRectangle().y0; i < numberOfThreads; ++i, ++j )
         threads.Add( new Thread<P>( data,
                                     y0 + i*rowsPerThread,
                                     y0 + ((j < numberOfThreads) ? j*rowsPerThread : numberOfRows),
                                     i > 0,
                                     j < numberOfThreads ) );
      try
      {
         AbstractImage::RunThreads( threads, data );
         if ( didReflect )
            const_cast<StructuringElement&>( transformation.Structure() ).Reflect();
      }
      catch ( ... )
      {
         if ( didReflect )
            const_cast<StructuringElement&>( transformation.Structure() ).Reflect();
         throw;
      }

      image.SetStatusCallback( nullptr );

      int c0 = image.SelectedChannel();
      Point p0 = image.SelectedRectangle().LeftTop();

      for ( int i = 0, j = 1; i < numberOfThreads; ++i, ++j )
      {
         if ( i > 0 )
            image.Mov( threads[i].UpperOverlappingRegion(),
                       Point( p0.x, p0.y + i*rowsPerThread ), c0 );
         if ( j < numberOfThreads )
            image.Mov( threads[i].LowerOverlappingRegion(),
                       Point( p0.x, p0.y + j*rowsPerThread - threads[i].LowerOverlappingRegion().Height() ), c0 );
      }

      image.Status() = data.status;

      threads.Destroy();
   }
Beispiel #11
0
   template <class P> static
   void Apply( GenericImage<P>& image, const Translation& translation )
   {
      if ( translation.Delta() == 0.0 )
         return;

      int width = image.Width();
      int height = image.Height();
      if ( width == 0 || height == 0 )
         return;

      image.EnsureUnique();

      typename P::sample* f = nullptr;
      typename P::sample** f0 = nullptr;

      int n = image.NumberOfChannels();
      typename GenericImage<P>::color_space cs0 = image.ColorSpace();

      StatusMonitor status = image.Status();

      int numberOfThreads = translation.IsParallelProcessingEnabled() ?
               Min( translation.MaxProcessors(), pcl::Thread::NumberOfThreads( height, 1 ) ) : 1;
      int rowsPerThread = height/numberOfThreads;

      try
      {
         size_type N = size_type( width )*size_type( height );
         if ( status.IsInitializationEnabled() )
            status.Initialize( String().Format( "Translate dx=%.3lf, dy=%.3lf, ",
                        translation.Delta().x, translation.Delta().y ) + translation.Interpolation().Description(),
                        size_type( n )*N );

         f0 = image.ReleaseData();

         for ( int c = 0; c < n; ++c )
         {
            ThreadData<P> data( translation.Delta(), width, height, status, N );

            data.f = f = image.Allocator().AllocatePixels( size_type( width )*size_type( height ) );
            data.fillValue = (c < translation.FillValues().Length()) ? P::ToSample( translation.FillValues()[c] ) : P::MinSampleValue();

            ReferenceArray<Thread<P> > threads;
            for ( int i = 0, j = 1; i < numberOfThreads; ++i, ++j )
               threads.Add( new Thread<P>( data,
                                           translation.Interpolation().NewInterpolator<P>( f0[c], width, height ),
                                           i*rowsPerThread,
                                           (j < numberOfThreads) ? j*rowsPerThread : height ) );

            AbstractImage::RunThreads( threads, data );

            threads.Destroy();

            image.Allocator().Deallocate( f0[c] );
            f0[c] = f;
            f = nullptr;

            status = data.status;
         }

         image.ImportData( f0, width, height, n, cs0 ).Status() = status;
      }
      catch ( ... )
      {
         if ( f != nullptr )
            image.Allocator().Deallocate( f );
         if ( f0 != nullptr )
         {
            for ( int c = 0; c < n; ++c )
               if ( f0[c] != nullptr )
                  image.Allocator().Deallocate( f0[c] );
            image.Allocator().Deallocate( f0 );
         }
         image.FreeData();
         throw;
      }
   }
void ComposeDeploymentUpgradeProgress::ToPublicApi(
    __in ScopedHeap & heap,
    __out FABRIC_COMPOSE_DEPLOYMENT_UPGRADE_PROGRESS & publicResult) const
{
    publicResult.DeploymentName = heap.AddString(deploymentName_);
    publicResult.ApplicationName = heap.AddString(applicationName_.ToString());

    switch (upgradeType_)
    {
        case UpgradeType::Rolling:
        case UpgradeType::Rolling_NotificationOnly:
        case UpgradeType::Rolling_ForceRestart:
            publicResult.UpgradeKind = FABRIC_APPLICATION_UPGRADE_KIND_ROLLING;
            break;
        default:
            publicResult.UpgradeKind = FABRIC_APPLICATION_UPGRADE_KIND_INVALID;
    }

    if (publicResult.UpgradeKind == FABRIC_APPLICATION_UPGRADE_KIND_ROLLING)
    {
        auto policyDescription = heap.AddItem<FABRIC_ROLLING_UPGRADE_POLICY_DESCRIPTION>();

        switch (rollingUpgradeMode_)
        {
            case RollingUpgradeMode::UnmonitoredAuto:
                policyDescription->RollingUpgradeMode = FABRIC_ROLLING_UPGRADE_MODE_UNMONITORED_AUTO;
                break;

            case RollingUpgradeMode::UnmonitoredManual:
                policyDescription->RollingUpgradeMode = FABRIC_ROLLING_UPGRADE_MODE_UNMONITORED_MANUAL;
                break;

            case RollingUpgradeMode::Monitored:
            {
                policyDescription->RollingUpgradeMode = FABRIC_ROLLING_UPGRADE_MODE_MONITORED;

                auto policyDescriptionEx = heap.AddItem<FABRIC_ROLLING_UPGRADE_POLICY_DESCRIPTION_EX1>();

                auto monitoringPolicy = heap.AddItem<FABRIC_ROLLING_UPGRADE_MONITORING_POLICY>();
                policyDescriptionEx->MonitoringPolicy = monitoringPolicy.GetRawPointer();
                monitoringPolicy_.ToPublicApi(heap, *monitoringPolicy);

                if (healthPolicy_)
                {
                    auto healthPolicy = heap.AddItem<FABRIC_APPLICATION_HEALTH_POLICY>();
                    policyDescriptionEx->HealthPolicy = healthPolicy.GetRawPointer();
                    healthPolicy_->ToPublicApi(heap, *healthPolicy);
                }
                else
                {
                    policyDescriptionEx->HealthPolicy = nullptr;
                }

                policyDescription->Reserved = policyDescriptionEx.GetRawPointer();

                break;
            }

            default:
                policyDescription->RollingUpgradeMode = FABRIC_ROLLING_UPGRADE_MODE_INVALID;
        }
        policyDescription->ForceRestart = forceRestart_? TRUE : FALSE;
        policyDescription->UpgradeReplicaSetCheckTimeoutInSeconds = replicaSetCheckTimeoutInSeconds_;
        publicResult.UpgradePolicyDescription = policyDescription.GetRawPointer();
    }
    else
    {
        publicResult.UpgradePolicyDescription = nullptr;
    }

    publicResult.TargetApplicationTypeVersion = heap.AddString(targetAppTypeVersion_);
    publicResult.UpgradeState = publicUpgradeState_;
    publicResult.NextUpgradeDomain = nextUpgradeDomain_.empty() ? nullptr : heap.AddString(nextUpgradeDomain_);

    ReferenceArray<FABRIC_UPGRADE_DOMAIN_STATUS_DESCRIPTION> domains;
    UpgradeHelper::ToPublicUpgradeDomains(
        heap,
        inProgressUpgradeDomain_,
        pendingUpgradeDomains_,
        completedUpgradeDomains_,
        domains);
    auto domainListPtr = heap.AddItem<FABRIC_UPGRADE_DOMAIN_STATUS_DESCRIPTION_LIST>();
    domainListPtr->Count = static_cast<ULONG>(domains.GetCount());
    domainListPtr->Items = domains.GetRawArray();
    publicResult.UpgradeDomains = domainListPtr.GetRawPointer();

    publicResult.UpgradeDurationInSeconds = static_cast<DWORD>(upgradeDuration_.TotalSeconds());
    publicResult.CurrentUpgradeDomainDurationInSeconds = static_cast<DWORD>(currentUpgradeDomainDuration_.TotalSeconds());
    
    auto publicHealthEvaluationsPtr = heap.AddItem<FABRIC_HEALTH_EVALUATION_LIST>();
    auto error = PublicApiHelper::ToPublicApiList<HealthEvaluation, FABRIC_HEALTH_EVALUATION, FABRIC_HEALTH_EVALUATION_LIST>(
        heap, 
        unhealthyEvaluations_, 
        *publicHealthEvaluationsPtr); 
    if (error.IsSuccess())
    {
        publicResult.ApplicationUnhealthyEvaluations = publicHealthEvaluationsPtr.GetRawPointer();
    }
    else
    {
        Trace.WriteError("ComposeDeploymentUpgradeStatusDescription", "Unhealthy evaluations to public API failed: error={0}", error);

        publicResult.ApplicationUnhealthyEvaluations = nullptr;
    }

    auto upgradeDomainProgressPtr = heap.AddItem<FABRIC_UPGRADE_DOMAIN_PROGRESS>();
    currentUpgradeDomainProgress_.ToPublicApi(heap, *upgradeDomainProgressPtr);
    publicResult.CurrentUpgradeDomainProgress = upgradeDomainProgressPtr.GetRawPointer();
    publicResult.UpgradeStatusDetails = heap.AddString(statusDetails_);

    publicResult.StartTimestampUtc = commonUpgradeContextData_.StartTime.AsFileTime;
    publicResult.FailureTimestampUtc = commonUpgradeContextData_.FailureTime.AsFileTime;
    publicResult.FailureReason = Management::ClusterManager::UpgradeFailureReason::ToPublicApi(commonUpgradeContextData_.FailureReason);
    if (commonUpgradeContextData_.FailureTime != DateTime::Zero)
    {
        auto upgradeProgressAtFailurePtr = heap.AddItem<FABRIC_UPGRADE_DOMAIN_PROGRESS>();
        commonUpgradeContextData_.UpgradeProgressAtFailure.ToPublicApi(heap, *upgradeProgressAtFailurePtr);
        publicResult.UpgradeDomainProgressAtFailure = upgradeProgressAtFailurePtr.GetRawPointer();
    }

    publicResult.ApplicationUpgradeStatusDetails = commonUpgradeContextData_.UpgradeStatusDetails.empty() ? nullptr : heap.AddString(commonUpgradeContextData_.UpgradeStatusDetails);
}
Beispiel #13
0
   template <class P> static
   void Apply( GenericImage<P>& image, const Resample& resample )
   {
      int width = image.Width();
      int w0 = width;
      int height = image.Height();
      int h0 = height;

      resample.GetNewSizes( width, height );

      if ( width == w0 && height == h0 )
         return;

      if ( width <= 0 || height <= 0 )
      {
         image.FreeData();
         return;
      }

      image.EnsureUnique();

      typename P::sample* f = nullptr;
      typename P::sample** f0 = nullptr;

      int n = image.NumberOfChannels();
      typename GenericImage<P>::color_space cs0 = image.ColorSpace();

      double rx = double( w0 )/width;
      double ry = double( h0 )/height;

      StatusMonitor status = image.Status();

      int numberOfThreads = resample.IsParallelProcessingEnabled() ?
               Min( resample.MaxProcessors(), pcl::Thread::NumberOfThreads( height, 1 ) ) : 1;
      int rowsPerThread = height/numberOfThreads;

      try
      {
         size_type N = size_type( width )*size_type( height );
         if ( status.IsInitializationEnabled() )
            status.Initialize( String().Format( "Resampling to %dx%d px, ", width, height )
                                                + resample.Interpolation().Description(), size_type( n )*N );
         f0 = image.ReleaseData();

         for ( int c = 0; c < n; ++c )
         {
            ThreadData<P> data( rx, ry, width, status, N );
            data.f = f = image.Allocator().AllocatePixels( width, height );

            ReferenceArray<Thread<P> > threads;
            for ( int i = 0, j = 1; i < numberOfThreads; ++i, ++j )
               threads.Add( new Thread<P>( data, resample.Interpolation().NewInterpolator<P>( f0[c], w0, h0 ),
                                           i*rowsPerThread,
                                           (j < numberOfThreads) ? j*rowsPerThread : height ) );

            AbstractImage::RunThreads( threads, data );

            threads.Destroy();

            image.Allocator().Deallocate( f0[c] );
            f0[c] = f;
            f = nullptr;

            status = data.status;
         }

         image.ImportData( f0, width, height, n, cs0 ).Status() = status;
      }
      catch ( ... )
      {
         if ( f != nullptr )
            image.Allocator().Deallocate( f );
         if ( f0 != nullptr )
         {
            for ( int c = 0; c < n; ++c )
               if ( f0[c] != nullptr )
                  image.Allocator().Deallocate( f0[c] );
            image.Allocator().Deallocate( f0 );
         }
         image.FreeData();
         throw;
      }
   }