-
Notifications
You must be signed in to change notification settings - Fork 22
/
itkAttenuationImageFilter.h
333 lines (282 loc) · 11.6 KB
/
itkAttenuationImageFilter.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/*=========================================================================
*
* 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 itkAttenuationImageFilter_h
#define itkAttenuationImageFilter_h
#include <vector>
#include "itkImage.h"
#include "itkImageToImageFilter.h"
#include "itkImageRegionSplitterDirection.h"
#include "itkMacro.h"
#include "itkNumericTraits.h"
namespace itk
{
/** \class AttenuationImageFilter
* \brief Computes the estimated attentuation in dB/(MHz*cm)
*
* Attenuation is a measure of how an RF signal fades in strength
* as it passes through a physical region. In ultrasound analysis
* signal attenuation tends to be roughly similar over areas
* of similar material composition, such as different types of
* tissue within an image.
*
* AttenuationImageFilter receives an input vector image representing
* RF spectra. One image direction represents the direction of an
* RF waveform emitted from an ultrasound probe. Remaining image
* directions may represent directions in physical space, such as
* where the probe contains multiple elements, or directions in
* time as multiple samples are captured in a sweep with the probe.
* Each pixel in the input image is a vector representing frequency
* components at bins based on the sampling frequency.
* The filter also receives a mandatory mask input indicating the
* region over which attenuations should be estimated.
*
* AttenuationImageFilter generates a scalar output image with
* pixel intensities representing attenuation estimates.
* Estimates are made in continuous segments of the mask region
* along the RF sampling direction.
*
* There are three modes of computation, see documentation for
* ComputationMode for detailed description.
* Pixels outside of the mask after padding erosion will have a value of zero.
* Pixels inside the mask for which the attenuation has not been computed
* will also have a value of zero.
*
* \sa MaskedImageToHistogramFilter
* \sa Spectra1DImageFilter
* \sa VariableLengthVector
* \sa VectorImage
*
* \ingroup ITKImageStatistics
* \ingroup Ultrasound
*/
template <typename TInputImage,
typename TOutputImage = itk::Image<float, TInputImage::ImageDimension>,
typename TMaskImage = itk::Image<unsigned char, TInputImage::ImageDimension>>
class ITK_TEMPLATE_EXPORT AttenuationImageFilter : public ImageToImageFilter<TInputImage, TOutputImage>
{
public:
ITK_DISALLOW_COPY_AND_MOVE(AttenuationImageFilter);
/** Standard class type aliases. */
using Self = AttenuationImageFilter;
using Superclass = ImageToImageFilter<TInputImage, TOutputImage>;
using Pointer = SmartPointer<Self>;
using ConstPointer = SmartPointer<const Self>;
/** Method for creation through the object factory. */
itkNewMacro(Self);
/** Run-time type information (and related methods). */
itkTypeMacro(AttenuationImageFilter, ImageToImageFilter);
/** Image type alias support */
static constexpr unsigned int ImageDimension = TInputImage::ImageDimension;
using InputImageType = TInputImage;
using InputImagePointer = typename TInputImage::Pointer;
using OutputImageType = TOutputImage;
using OutputImagePointer = typename TOutputImage::Pointer;
using MaskImageType = TMaskImage;
using MaskImagePointer = typename TMaskImage::Pointer;
using MaskPixelType = typename TMaskImage::PixelType;
using InputRegionType = typename TInputImage::RegionType;
using InputSizeType = typename TInputImage::SizeType;
using InputIndexType = typename TInputImage::IndexType;
using InputPixelType = typename TInputImage::PixelType;
using OutputRegionType = typename TOutputImage::RegionType;
using OutputPixelType = typename TOutputImage::PixelType;
/** Input mask image represents input region for analysis */
itkSetInputMacro(InputMaskImage, TMaskImage);
itkGetInputMacro(InputMaskImage, TMaskImage);
/** Output mask image represents output region for analysis
* after padding is applied */
virtual const MaskImageType *
GetOutputMaskImage() const
{
return dynamic_cast<const MaskImageType *>(this->ProcessObject::GetOutput(1));
}
virtual MaskImageType *
GetOutputMaskImage()
{
return dynamic_cast<MaskImageType *>(this->ProcessObject::GetOutput(1));
}
MaskImageType *
GetOutput(unsigned int idx)
{
if (idx == 0)
{
// Pythonic interface invokes this in a for loop because filter has multiple outputs
return reinterpret_cast<MaskImageType *>(this->GetOutput());
}
else if (idx != 1)
{
itkExceptionMacro("This filter has only two outputs");
}
return this->GetOutputMaskImage();
}
TOutputImage *
GetOutput()
{
return this->Superclass::GetOutput();
}
/** Mode of computation (0=all pairs, 1=first and last, 2=fixed distance).
*
* 0. (Default) Between all pixel pairs in a segment, producing a weighted
* estimate for each pixel. More distant pixel pairs have higher weights.
* This decreases influence of numerical instability.
* 1. Between first and last pixel in a segment.
* 2. Between first pixel in a segment, and
* a pixel at a fixed distance away from it.
* This distance is controlled by FixedEstimationDepth parameter.
*
* Modes 1 and 2 produce an estimate for the midpoint pixel only.
*/
itkSetMacro(ComputationMode, unsigned int);
itkGetConstMacro(ComputationMode, unsigned int);
/** Label value indicating which mask pixel values should be included in analysis.
* A value of zero indicates that any nonzero pixel should be included.
*/
itkSetMacro(LabelValue, MaskPixelType);
itkGetConstMacro(LabelValue, MaskPixelType);
/** Fix the pixel distance between voxels for estimating
* attenuation in a scan line.
* If set to zero then the last continuous pixel
* in the inclusion will always be chosen as the
* second pixel for attenuation calculation. */
itkSetMacro(FixedEstimationDepth, unsigned int);
itkGetConstMacro(FixedEstimationDepth, unsigned int);
/** Set/get fixed estimation depth in physical space.
* Assumes input image spacing is in millimenters. */
void
SetFixedEstimationDepthMM(const float distanceMM);
float
GetFixedEstimationDepthMM() const;
/** RF scanline direction */
itkSetMacro(Direction, unsigned int);
itkGetConstMacro(Direction, unsigned int);
/** RF sampling frequency */
itkSetMacro(SamplingFrequencyMHz, float);
itkGetConstMacro(SamplingFrequencyMHz, float);
/** Low end of RF frequency band. Must be a positive value. */
itkSetMacro(FrequencyBandStartMHz, float);
itkGetConstMacro(FrequencyBandStartMHz, float);
/* High end of RF frequency band. Must be a positive value.*/
itkSetMacro(FrequencyBandEndMHz, float);
itkGetConstMacro(FrequencyBandEndMHz, float);
/** Optionally discard negative attenuation estimates
* so that they are not considered in statistic computations.
* Negative attenuation implies that a signal strengthened
* while passing through tissue and may result from
* sampling error or external interference. */
itkSetMacro(ConsiderNegativeAttenuations, bool);
itkGetConstMacro(ConsiderNegativeAttenuations, bool);
/** Skip attenuation estimation for a fixed number
* of pixels at the start of an inclusion region.
* Applied before spatial padding.
* Can help with uncertainty at borders of mask.
*/
itkSetMacro(PadUpperBounds, unsigned int);
itkGetConstMacro(PadUpperBounds, unsigned int);
/** Skip attenuation estimation at the end of an inclusion region. */
itkSetMacro(PadLowerBounds, unsigned int);
itkGetConstMacro(PadLowerBounds, unsigned int);
/** Set padding at start of inclusion region based on
* physical distance. Assumes input image is in millimeters. */
void
SetPadLowerBoundsMM(const float distanceMM);
float
GetPadLowerBoundsMM() const;
/** Set padding at end of inclusion region based on
* physical distance. Assumes input image is in millimeters. */
void
SetPadUpperBoundsMM(const float distanceMM);
float
GetPadUpperBoundsMM() const;
// Alias for setting direction of RF waveform in data collection
void
SetScanDirection(unsigned int direction)
{
this->SetDirection(direction);
};
unsigned int
GetScanDirection()
{
return this->GetDirection();
};
void
PrintSelf(std::ostream & os, Indent indent) const override;
/** Standard itk::ProcessObject subclass method. */
using DataObjectPointer = DataObject::Pointer;
using DataObjectPointerArraySizeType = ProcessObject::DataObjectPointerArraySizeType;
using Superclass::MakeOutput;
DataObjectPointer
MakeOutput(DataObjectPointerArraySizeType idx) override;
protected:
AttenuationImageFilter();
~AttenuationImageFilter() override = default;
void
VerifyPreconditions() const override;
/** Allocate buffers and other initializations before threaded execution. */
void
BeforeThreadedGenerateData() override;
void
ThreadedGenerateData(const OutputRegionType & regionForThread, ThreadIdType) override;
const ImageRegionSplitterBase *
GetImageRegionSplitter() const override;
/** Compute attenuation between two pixels in the RF spectra vector image.
* Assumes that image spacing is in MM. */
OutputPixelType
ComputeAttenuation(const InputIndexType & end, const InputIndexType & start) const;
/** Transform spatial distance along an RF scan line to continuous pixel distance
* in the input image.
* Assumes input distance and image spacing are in millimeters.
* Rounded to nearest integer. */
float
TransformPhysicalToPixelScanLineDistance(float distanceMM) const;
/** Transform pixel distance along an RF scan line to spatial distance
* in the input image.
* Assumes input image spacing is in millimeters. Output is in millimeters. */
float
TransformPixelToPhysicalScanLineDistance(unsigned int distance) const;
/** Check whether given pixel index is included in the mask */
bool
ThreadedIsIncluded(InputIndexType index) const;
private:
unsigned int m_ComputationMode = 0;
MaskPixelType m_LabelValue = 0;
unsigned int m_Direction = 0;
unsigned int m_FixedEstimationDepth = 0;
unsigned int m_PadUpperBounds = 0;
unsigned int m_PadLowerBounds = 0;
std::vector<float> m_DistanceWeights;
float m_ScanStepMM = 1.0f;
float m_SamplingFrequencyMHz = 0.0f;
float m_FrequencyBandStartMHz = 0.0f;
float m_FrequencyBandEndMHz = 0.0f;
float m_FrequencyDelta = 0.0f;
// Frequency band to consider for attenuation
unsigned int m_StartComponent = 0;
unsigned int m_EndComponent = 0;
unsigned int m_ConsideredComponents = 1;
bool m_ConsiderNegativeAttenuations = false;
/** Region splitter to ensure scanline is intact in threaded regions */
ImageRegionSplitterDirection::Pointer m_RegionSplitter = ImageRegionSplitterDirection::New();
/** Cache mask image reference before threaded execution to reduce calls to GetMaskImage() */
mutable const MaskImageType * m_ThreadedInputMaskImage;
unsigned int m_LastScanlineIndex = 0;
};
} // end namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
# include "itkAttenuationImageFilter.hxx"
#endif
#endif