ITK 6.0.0
Insight Toolkit
 
Loading...
Searching...
No Matches
itkVectorGradientMagnitudeImageFilter.h
Go to the documentation of this file.
1/*=========================================================================
2 *
3 * Copyright NumFOCUS
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0.txt
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *=========================================================================*/
18#ifndef itkVectorGradientMagnitudeImageFilter_h
19#define itkVectorGradientMagnitudeImageFilter_h
20
23#include "itkImage.h"
24#include "itkVector.h"
25#include "vnl/vnl_matrix.h"
26#include "vnl/vnl_vector_fixed.h"
27#include "vnl/algo/vnl_symmetric_eigensystem.h"
28#include "itkMath.h"
29
30namespace itk
31{
104
105 * \par Constraints
106 * The filter requires an image with at least two dimensions and a vector
107 * length of at least 2. The theory supports extension to scalar images, but
108 * the implementation of the itk vector classes do not
109 *
110 * The template parameter TRealType must be floating point (float or double) or
111 * a user-defined "real" numerical type with arithmetic operations defined
112 * sufficient to compute derivatives.
113 *
114 * \par Performance
115 * This filter will automatically multithread if run with
116 * SetUsePrincipleComponents=Off or on 3D data in UsePrincipleComponents=On
117 * mode. Unfortunately the ND eigen solver used is not thread safe (a special
118 * 3D solver is), so it cannot multithread for data other than 3D in
120 *
121 * For algorithmic details see \cite sapiro1996.
122 *
123 * \ingroup GradientFilters
124 *
125 * \sa Image
126 * \sa Neighborhood
129 * \ingroup ITKImageGradient
130 */
131template <typename TInputImage,
132 typename TRealType = float,
133 typename TOutputImage = Image<TRealType, TInputImage::ImageDimension>>
134class ITK_TEMPLATE_EXPORT VectorGradientMagnitudeImageFilter : public ImageToImageFilter<TInputImage, TOutputImage>
135{
136public:
137 ITK_DISALLOW_COPY_AND_MOVE(VectorGradientMagnitudeImageFilter);
138
144
146 itkNewMacro(Self);
147
149 itkOverrideGetNameOfClassMacro(VectorGradientMagnitudeImageFilter);
150
153 using OutputPixelType = typename TOutputImage::PixelType;
154 using InputPixelType = typename TInputImage::PixelType;
155
157 using InputImageType = TInputImage;
158 using OutputImageType = TOutputImage;
159 using InputImagePointer = typename InputImageType::Pointer;
160 using OutputImagePointer = typename OutputImageType::Pointer;
161
164 static constexpr unsigned int InputImageDimension = TInputImage::ImageDimension;
165 static constexpr unsigned int ImageDimension = TOutputImage::ImageDimension;
166
168 static constexpr unsigned int VectorDimension = InputPixelType::Dimension;
169
171 using RealType = TRealType;
173 using RealVectorImageType = Image<RealVectorType, TInputImage::ImageDimension>;
174
177 using ConstNeighborhoodIteratorType = ConstNeighborhoodIterator<RealVectorImageType>;
178 using RadiusType = typename ConstNeighborhoodIteratorType::RadiusType;
179
181 using typename Superclass::OutputImageRegionType;
182
191 void
192 GenerateInputRequestedRegion() override;
193
199 void
200 SetUseImageSpacing(bool);
201 itkGetConstMacro(UseImageSpacing, bool);
202 itkBooleanMacro(UseImageSpacing);
204
205#if !defined(ITK_FUTURE_LEGACY_REMOVE)
211 void
212 SetUseImageSpacingOn()
213 {
214 this->SetUseImageSpacing(true);
215 }
216
221 void
222 SetUseImageSpacingOff()
223 {
224 this->SetUseImageSpacing(false);
225 }
226#endif
227
228 using ComponentWeightsType = FixedArray<TRealType, VectorDimension>;
229 using DerivativeWeightsType = FixedArray<TRealType, ImageDimension>;
230#if !defined(ITK_LEGACY_REMOVE)
231 using WeightsType [[deprecated("Use DerivativeWeightsType or ComponentWeightsType instead.")]] = ComponentWeightsType;
232#endif
233
236 itkSetMacro(DerivativeWeights, DerivativeWeightsType);
237 itkGetConstReferenceMacro(DerivativeWeights, DerivativeWeightsType);
239
242 itkSetMacro(ComponentWeights, ComponentWeightsType);
243 itkGetConstReferenceMacro(ComponentWeights, ComponentWeightsType);
245
251 itkSetMacro(UsePrincipleComponents, bool);
252 itkGetConstMacro(UsePrincipleComponents, bool);
253 itkBooleanMacro(UsePrincipleComponents);
255
256#if !defined(ITK_FUTURE_LEGACY_REMOVE)
258 void
259 SetUsePrincipleComponentsOn()
260 {
261 this->SetUsePrincipleComponents(true);
262 }
263
265 void
266 SetUsePrincipleComponentsOff()
267 {
268 this->SetUsePrincipleComponents(false);
269 }
270#endif
271
274 static int
275 CubicSolver(const double *, double *);
276
279 itkConceptMacro(RealTypeHasNumericTraitsCheck, (Concept::HasNumericTraits<RealType>));
280
281protected:
283 ~VectorGradientMagnitudeImageFilter() override = default;
284
288 void
289 BeforeThreadedGenerateData() override;
290
302 void
303 DynamicThreadedGenerateData(const OutputImageRegionType & outputRegionForThread) override;
304
305
306 void
307 PrintSelf(std::ostream & os, Indent indent) const override;
308
309 using ImageBaseType = typename InputImageType::Superclass;
310
312 itkGetConstObjectMacro(RealValuedInputImage, RealVectorImageType);
313
314 TRealType
315 NonPCEvaluateAtNeighborhood(const ConstNeighborhoodIteratorType & it) const
316 {
317 unsigned int i;
318 unsigned int j;
319 TRealType dx;
320 TRealType sum;
321 auto accum = TRealType{};
322 for (i = 0; i < ImageDimension; ++i)
323 {
324 sum = TRealType{};
325 for (j = 0; j < VectorDimension; ++j)
326 {
327 dx = m_DerivativeWeights[i] * m_SqrtComponentWeights[j] * 0.5 * (it.GetNext(i)[j] - it.GetPrevious(i)[j]);
328 sum += dx * dx;
329 }
330 accum += sum;
331 }
332 return std::sqrt(accum);
333 }
334
335 TRealType
336 EvaluateAtNeighborhood3D(const ConstNeighborhoodIteratorType & it) const
337 {
338 // WARNING: ONLY CALL THIS METHOD WHEN PROCESSING A 3D IMAGE
339 unsigned int i;
340 unsigned int j;
341 double Lambda[3];
342 double CharEqn[3];
343 double ans;
344
345 vnl_matrix<TRealType> g(ImageDimension, ImageDimension);
346 vnl_vector_fixed<TRealType, VectorDimension> d_phi_du[TInputImage::ImageDimension];
347
348 // Calculate the directional derivatives for each vector component using
349 // central differences.
350 for (i = 0; i < ImageDimension; ++i)
351 {
352 for (j = 0; j < VectorDimension; ++j)
353 {
354 d_phi_du[i][j] =
355 m_DerivativeWeights[i] * m_SqrtComponentWeights[j] * 0.5 * (it.GetNext(i)[j] - it.GetPrevious(i)[j]);
356 }
357 }
358
359 // Calculate the symmetric metric tensor g
360 for (i = 0; i < ImageDimension; ++i)
361 {
362 for (j = i; j < ImageDimension; ++j)
363 {
364 g[j][i] = g[i][j] = dot_product(d_phi_du[i], d_phi_du[j]);
365 }
366 }
367
368 // Find the coefficients of the characteristic equation det(g - lambda I)=0
369 // CharEqn[3] = 1.0;
370
371 CharEqn[2] = -(g[0][0] + g[1][1] + g[2][2]);
372
373 CharEqn[1] = (g[0][0] * g[1][1] + g[0][0] * g[2][2] + g[1][1] * g[2][2]) -
374 (g[0][1] * g[1][0] + g[0][2] * g[2][0] + g[1][2] * g[2][1]);
375
376 CharEqn[0] = g[0][0] * (g[1][2] * g[2][1] - g[1][1] * g[2][2]) + g[1][0] * (g[2][2] * g[0][1] - g[0][2] * g[2][1]) +
377 g[2][0] * (g[1][1] * g[0][2] - g[0][1] * g[1][2]);
378
379 // Find the eigenvalues of g
380 const int numberOfDistinctRoots = this->CubicSolver(CharEqn, Lambda);
381
382 // Define gradient magnitude as the difference between two largest
383 // eigenvalues. Other definitions may be appropriate here as well.
384 if (numberOfDistinctRoots == 3) // By far the most common case
385 {
386 if (Lambda[0] > Lambda[1])
387 {
388 if (Lambda[1] > Lambda[2]) // Most common, guaranteed?
389 {
390 ans = Lambda[0] - Lambda[1];
391 }
392 else
393 {
394 if (Lambda[0] > Lambda[2])
395 {
396 ans = Lambda[0] - Lambda[2];
397 }
398 else
399 {
400 ans = Lambda[2] - Lambda[0];
401 }
402 }
403 }
404 else
405 {
406 if (Lambda[0] > Lambda[2])
407 {
408 ans = Lambda[1] - Lambda[0];
409 }
410 else
411 {
412 if (Lambda[1] > Lambda[2])
413 {
414 ans = Lambda[1] - Lambda[2];
415 }
416 else
417 {
418 ans = Lambda[2] - Lambda[1];
419 }
420 }
421 }
422 }
423 else if (numberOfDistinctRoots == 2)
424 {
425 if (Lambda[0] > Lambda[1])
426 {
427 ans = Lambda[0] - Lambda[1];
428 }
429 else
430 {
431 ans = Lambda[1] - Lambda[0];
432 }
433 }
434 else if (numberOfDistinctRoots == 1)
435 {
436 ans = 0.0;
437 }
438 else
439 {
440 itkExceptionMacro("Undefined condition. Cubic root solver returned " << numberOfDistinctRoots
441 << " distinct roots.");
442 }
443
444 return ans;
445 }
446
447 // Function is defined here because the templating confuses gcc 2.96 when
448 // defined
449 // in .hxx file. jc 1/29/03
450 TRealType
451 EvaluateAtNeighborhood(const ConstNeighborhoodIteratorType & it) const
452 {
453 unsigned int i;
454 unsigned int j;
455
456 vnl_matrix<TRealType> g(ImageDimension, ImageDimension);
457 vnl_vector_fixed<TRealType, VectorDimension> d_phi_du[TInputImage::ImageDimension];
458
459 // Calculate the directional derivatives for each vector component using
460 // central differences.
461 for (i = 0; i < ImageDimension; ++i)
462 {
463 for (j = 0; j < VectorDimension; ++j)
464 {
465 d_phi_du[i][j] =
466 m_DerivativeWeights[i] * m_SqrtComponentWeights[j] * 0.5 * (it.GetNext(i)[j] - it.GetPrevious(i)[j]);
467 }
468 }
469
470 // Calculate the symmetric metric tensor g
471 for (i = 0; i < ImageDimension; ++i)
472 {
473 for (j = i; j < ImageDimension; ++j)
474 {
475 g[j][i] = g[i][j] = dot_product(d_phi_du[i], d_phi_du[j]);
476 }
477 }
478
479 // Find the eigenvalues of g
480 const vnl_symmetric_eigensystem<TRealType> E(g);
481
482 // Return the difference in length between the first two principle axes.
483 // Note that other edge strength metrics may be appropriate here instead..
484 return (E.get_eigenvalue(ImageDimension - 1) - E.get_eigenvalue(ImageDimension - 2));
485 }
486
488 DerivativeWeightsType m_DerivativeWeights = DerivativeWeightsType::Filled(1);
489
493 ComponentWeightsType m_ComponentWeights = ComponentWeightsType::Filled(1);
494 ComponentWeightsType m_SqrtComponentWeights = ComponentWeightsType::Filled(1);
496
497private:
498 bool m_UseImageSpacing{ true };
499 bool m_UsePrincipleComponents{};
500
501 ThreadIdType m_RequestedNumberOfWorkUnits{};
502
503 typename RealVectorImageType::ConstPointer m_RealValuedInputImage{};
504};
505} // end namespace itk
506
507#ifndef ITK_MANUAL_INSTANTIATION
508# include "itkVectorGradientMagnitudeImageFilter.hxx"
509#endif
510
511#endif
Computes a scalar, gradient magnitude image from a multiple channel (pixels are vectors) input.
Const version of NeighborhoodIterator, defining iteration of a local N-dimensional neighborhood of pi...
Simulate a standard C array with copy semantics.
Base class for filters that take an image as input and produce an image as output.
Templated n-dimensional image class.
Definition itkImage.h:89
Control indentation during Print() invocation.
Definition itkIndent.h:50
Defines iteration of a local N-dimensional neighborhood of pixels across an itk::Image.
Virtual class that defines a common interface to all neighborhood operator subtypes.
A light-weight container object for storing an N-dimensional neighborhood of values.
Implements transparent reference counting.
A templated class holding a n-Dimensional vector.
Definition itkVector.h:63
SmartPointer< const Self > ConstPointer
BinaryGeneratorImageFilter< TInputImage1, TInputImage2, TOutputImage > Superclass
SmartPointer< Self > Pointer
AddImageFilter Self
#define itkConceptMacro(name, concept)
The "itk" namespace contains all Insight Segmentation and Registration Toolkit (ITK) classes....
unsigned int ThreadIdType
*par Constraints *The filter image with at least two dimensions and a vector *length of at least The theory supports extension to scalar but *the implementation of the itk vector classes do not **The template parameter TRealType must be floating point(float or double) or *a user-defined "real" numerical type with arithmetic operations defined *sufficient to compute derivatives. **\par Performance *This filter will automatically multithread if run with *SetUsePrincipleComponents
*par Constraints *The filter image with at least two dimensions and a vector *length of at least The theory supports extension to scalar but *the implementation of the itk vector classes do not **The template parameter TRealType must be floating so it cannot multithread for data other than in * UsePrincipleComponents
*par Constraints *The filter image with at least two dimensions and a vector *length of at least The theory supports extension to scalar images