/*========================================================================= * * Copyright NumFOCUS * * 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 * * https://www.apache.org/licenses/LICENSE-2.0.txt * * 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. * *=========================================================================*/ #ifndef itkObjectToObjectMetric_h #define itkObjectToObjectMetric_h #include "itkObjectToObjectMetricBase.h" #include "itkDisplacementFieldTransform.h" #include "itkImage.h" #include "itkObject.h" #include "itkPointSet.h" #include "itkTransform.h" namespace itk { /** * \class ObjectToObjectMetric * \brief Computes similarity between regions of two objects. * * This class is templated over the dimensionality of the two input objects. * This is the abstract templated base class for a hierarchy of similarity metrics * that may, in derived classes, operate on meshes, images, etc. * This class computes a value that measures the similarity between the two * objects. * * Derived classes must provide implementations for: * GetValue * GetDerivative * GetValueAndDerivative * SupportsArbitraryVirtualDomainSamples * * Similarity is evaluated using fixed and moving transforms. * Both transforms are initialized to an IdentityTransform, and can be * set by the user using SetFixedTransform() and SetMovingTransform(). * * Virtual Domain * * This class uses a virtual reference space. This space defines the resolution * at which the evaluation is performed, as well as the physical coordinate * system. This is useful for unbiased registration. The virtual domain is stored * in the m_VirtualDomain member, but this is subject to change so the convenience * methods GetVirtualSpacing(), GetVirtualDirection() and GetVirtualOrigin() should * be used whenever possible to retrieve virtual domain information. The region over which * metric evaluation is performed is taken from the virtual image buffered region. * * The user can define a virtual domain by calling either * \c SetVirtualDomain or \c SetVirtualDomainFromImage. See these * methods for details. Derived classes may automatically assign a virtual domain * if the user has not assigned one by initialization time. * * If the virtual domain is left undefined by the user and by derived classes, * then unit or zero values are returned for GetVirtualSpacing(), * GetVirtualDirection() and GetVirtualOrigin(), as appropriate. The virtual region is left * undefined and an attempt to retrieve it via GetVirtualRegion() will generate an exception. * The m_VirtualImage member will be nullptr. * * During evaluation, derived classes should verify that points are within the virtual domain * and thus valid, as appropriate for the needs of the metric. When points are deemed invalid * the number of valid points returned by GetNumberOfValidPoints() should reflect this. * * \note Transform Optimization * This hierarchy currently assumes only the moving transform is 'active', * i.e. only the moving transform is being optimized when used in an optimizer. * Methods relevant to transform optimization such as GetNumberOfParameters(), * UpdateTransformParameters() are passed on to the active transform. * The eventual goal however is to allow for either moving, fixed or both * transforms to be active within a single metric. * * \ingroup ITKOptimizersv4 */ template , typename TParametersValueType = double> class ITK_TEMPLATE_EXPORT ObjectToObjectMetric : public ObjectToObjectMetricBaseTemplate { public: ITK_DISALLOW_COPY_AND_MOVE(ObjectToObjectMetric); /** Standard class type aliases. */ using Self = ObjectToObjectMetric; using Superclass = ObjectToObjectMetricBaseTemplate; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** \see LightObject::GetNameOfClass() */ itkOverrideGetNameOfClassMacro(ObjectToObjectMetric); /** Type used for representing object components */ using CoordinateRepresentationType = TParametersValueType; /** Type for internal computations */ using InternalComputationValueType = TParametersValueType; /** Type of the measure. */ using typename Superclass::MeasureType; /** Type of object. */ using ObjectType = typename Superclass::Object; /** Type of the derivative. */ using typename Superclass::DerivativeType; using typename Superclass::DerivativeValueType; /** Type of the parameters. */ using typename Superclass::ParametersType; using typename Superclass::NumberOfParametersType; using typename Superclass::GradientSourceEnum; /** Dimension type */ using DimensionType = SizeValueType; /** Object dimension accessors */ static constexpr DimensionType FixedDimension = TFixedDimension; static constexpr DimensionType MovingDimension = TMovingDimension; static constexpr DimensionType VirtualDimension = TVirtualImage::ImageDimension; /** Types for the virtual domain */ using VirtualImageType = TVirtualImage; using VirtualImagePointer = typename VirtualImageType::Pointer; using VirtualImageConstPointer = typename VirtualImageType::ConstPointer; using VirtualPixelType = typename VirtualImageType::PixelType; using VirtualRegionType = typename VirtualImageType::RegionType; using VirtualSizeType = typename VirtualRegionType::SizeType; using VirtualSpacingType = typename VirtualImageType::SpacingType; using VirtualOriginType = typename VirtualImageType::PointType; using VirtualPointType = typename VirtualImageType::PointType; using VirtualDirectionType = typename VirtualImageType::DirectionType; using VirtualRadiusType = typename VirtualImageType::SizeType; using VirtualIndexType = typename VirtualImageType::IndexType; /** Point set in the virtual domain */ using VirtualPointSetType = PointSet; using VirtualPointSetPointer = typename VirtualPointSetType::Pointer; /** Type of the Transform Base classes */ using MovingTransformType = Transform; using FixedTransformType = Transform; using FixedTransformPointer = typename FixedTransformType::Pointer; using FixedInputPointType = typename FixedTransformType::InputPointType; using FixedOutputPointType = typename FixedTransformType::OutputPointType; using FixedTransformParametersType = typename FixedTransformType::ParametersType; using MovingTransformPointer = typename MovingTransformType::Pointer; using MovingInputPointType = typename MovingTransformType::InputPointType; using MovingOutputPointType = typename MovingTransformType::OutputPointType; using MovingTransformParametersType = typename MovingTransformType::ParametersType; /** Jacobian type. This is the same for all transforms */ using JacobianType = typename FixedTransformType::JacobianType; using FixedTransformJacobianType = typename FixedTransformType::JacobianType; using MovingTransformJacobianType = typename MovingTransformType::JacobianType; /** DisplacementFieldTransform types for working with local-support transforms */ using MovingDisplacementFieldTransformType = DisplacementFieldTransform; void Initialize() override; NumberOfParametersType GetNumberOfParameters() const override; NumberOfParametersType GetNumberOfLocalParameters() const override; void SetParameters(ParametersType & params) override; const ParametersType & GetParameters() const override; bool HasLocalSupport() const override; void UpdateTransformParameters(const DerivativeType & derivative, TParametersValueType factor) override; /** Connect the fixed transform. */ itkSetObjectMacro(FixedTransform, FixedTransformType); /** Get a pointer to the fixed transform. */ itkGetModifiableObjectMacro(FixedTransform, FixedTransformType); /** Connect the moving transform. */ itkSetObjectMacro(MovingTransform, MovingTransformType); /** Get a pointer to the moving transform. */ itkGetModifiableObjectMacro(MovingTransform, MovingTransformType); /** Connect the moving transform using a backwards-compatible name. * This assigns the input transform to the moving transform. */ void SetTransform(MovingTransformType * transform); /** Get the moving transform using a backwards-compatible name */ const MovingTransformType * GetTransform(); /** Get the number of valid points after a call to evaluate the * metric. */ itkGetConstMacro(NumberOfValidPoints, SizeValueType); /** Define the virtual reference space. This space defines the resolution * at which the registration is performed as well as the physical coordinate * system. Useful for unbiased registration. * This method will allocate \c m_VirtualImage with the passed * information, with the pixel buffer left unallocated. * Metric evaluation will be performed within the constraints of the virtual * domain depending on implementation in derived classes. * A default domain is created during initialization in derived * classes according to their need. * \param spacing spacing * \param origin origin * \param direction direction * \param region region is used to set all image regions. * * \sa SetVirtualDomainFromImage */ void SetVirtualDomain(const VirtualSpacingType & spacing, const VirtualOriginType & origin, const VirtualDirectionType & direction, const VirtualRegionType & region); /** Use a virtual domain image to define the virtual reference space. * \sa SetVirtualDomain */ void SetVirtualDomainFromImage(const VirtualImageType * virtualImage); /** Returns a flag. True if arbitrary virtual domain points will * always correspond to data points. False if not. For example, * point-set metrics return false because only some virtual domain * points will correspond to points within the point sets. */ virtual bool SupportsArbitraryVirtualDomainSamples() const = 0; /** Return a timestamp relating to the virtual domain. * This returns the greater of the metric timestamp and the * virtual domain image timestamp. This allows us to * capture if the virtual domain image is changed by the user * after being assigned to the metric. */ virtual const TimeStamp & GetVirtualDomainTimeStamp() const; /** Accessors for the virtual domain spacing. * Returns unit spacing if a virtual domain is undefined. */ VirtualSpacingType GetVirtualSpacing() const; /** Accessor for virtual domain origin. * Returns zero origin if a virtual domain is undefined. */ VirtualOriginType GetVirtualOrigin() const; /** Accessor for virtual domain direction. * Returns unit direction if a virtual domain is undefined. */ VirtualDirectionType GetVirtualDirection() const; /** Return the virtual domain region, which is retrieved from * the m_VirtualImage buffered region. */ const VirtualRegionType & GetVirtualRegion() const; itkGetModifiableObjectMacro(VirtualImage, VirtualImageType); /** Computes an offset for accessing parameter data from a virtual domain * index. Relevant for metrics with local-support transforms, to access * parameter or derivative memory that is stored linearly in a 1D array. * The result is the offset (1D array index) to the first of N parameters * corresponding to the given virtual index, where N is the number of * local parameters. * \param index the virtual index to convert * \param numberOfLocalParameters corresponding to the transform **/ OffsetValueType ComputeParameterOffsetFromVirtualIndex(const VirtualIndexType & index, const NumberOfParametersType & numberOfLocalParameters) const; /** Computes an offset for accessing parameter data from a virtual domain * point. Relevant for metrics with local-support transforms, to access * parameter or derivative memory that is stored linearly in a 1D array. * The result is the offset (1D array index) to the first of N parameters * corresponding to the given virtual index, where N is the number of * local parameters. * \param point the virtual point to convert * \param numberOfLocalParameters corresponding to the transform **/ OffsetValueType ComputeParameterOffsetFromVirtualPoint(const VirtualPointType & point, const NumberOfParametersType & numberOfLocalParameters) const; /** Determine if a point is within the virtual domain. * \note Returns true if the virtual domain has not been defined. This * allows, for example, use in point set metrics where the virtual domain * is implicitly defined by the point sets and transforms. */ bool IsInsideVirtualDomain(const VirtualPointType & point) const; bool IsInsideVirtualDomain(const VirtualIndexType & index) const; using MetricCategoryType = typename Superclass::MetricCategoryEnum; /** Get metric category */ MetricCategoryType GetMetricCategory() const override { return MetricCategoryType::OBJECT_METRIC; } protected: ObjectToObjectMetric(); ~ObjectToObjectMetric() override = default; void PrintSelf(std::ostream & os, Indent indent) const override; /** Verify that virtual domain and displacement field are the same size * and in the same physical space. */ virtual void VerifyDisplacementFieldSizeAndPhysicalSpace(); bool TransformPhysicalPointToVirtualIndex(const VirtualPointType &, VirtualIndexType &) const; void TransformVirtualIndexToPhysicalPoint(const VirtualIndexType &, VirtualPointType &) const; /** If the moving transform is a DisplacementFieldTransform, return it. * If the moving transform is a CompositeTransform, the routine will check if the * first (last to be added) transform is a DisplacementFieldTransform, and if so return it. * Otherwise, return nullptr. */ const MovingDisplacementFieldTransformType * GetMovingDisplacementFieldTransform() const; /** Check that the number of valid points is above a default * minimum (zero). If not, then return false, and assign to 'value' a value * indicating insufficient valid points were found during evaluation, and set * the derivative to zero. A warning is also output. * This functionality is provided as a separate method so derived classes * can use it without hardcoding the details. */ bool VerifyNumberOfValidPoints(MeasureType & value, DerivativeType & derivative) const; /** Transforms */ FixedTransformPointer m_FixedTransform{}; MovingTransformPointer m_MovingTransform{}; VirtualImagePointer m_VirtualImage{}; /** Flag that is set when user provides a virtual domain, either via * SetVirtualDomain() or SetVirtualDomainFromImage(). */ bool m_UserHasSetVirtualDomain{}; /** Store the number of points used during most recent value and derivative * calculation. * \sa VerifyNumberOfValidPoints() */ mutable SizeValueType m_NumberOfValidPoints{ 0 }; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION # include "itkObjectToObjectMetric.hxx" #endif #endif