Line |
Branch |
Exec |
Source |
1 |
|
|
/************************************************************************ |
2 |
|
|
* |
3 |
|
|
* Copyright (C) 2018-2023 IRCAD France |
4 |
|
|
* Copyright (C) 2018-2021 IHU Strasbourg |
5 |
|
|
* |
6 |
|
|
* This file is part of Sight. |
7 |
|
|
* |
8 |
|
|
* Sight is free software: you can redistribute it and/or modify it under |
9 |
|
|
* the terms of the GNU Lesser General Public License as published by |
10 |
|
|
* the Free Software Foundation, either version 3 of the License, or |
11 |
|
|
* (at your option) any later version. |
12 |
|
|
* |
13 |
|
|
* Sight is distributed in the hope that it will be useful, |
14 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 |
|
|
* GNU Lesser General Public License for more details. |
17 |
|
|
* |
18 |
|
|
* You should have received a copy of the GNU Lesser General Public |
19 |
|
|
* License along with Sight. If not, see <https://www.gnu.org/licenses/>. |
20 |
|
|
* |
21 |
|
|
***********************************************************************/ |
22 |
|
|
|
23 |
|
|
#pragma once |
24 |
|
|
|
25 |
|
|
#include "filter/image/filters.hpp" |
26 |
|
|
|
27 |
|
|
#include <io/itk/itk.hpp> |
28 |
|
|
|
29 |
|
|
#include <itkBinaryBallStructuringElement.h> |
30 |
|
|
#include <itkBinaryFillholeImageFilter.h> |
31 |
|
|
#include <itkBinaryThresholdImageFilter.h> |
32 |
|
|
#include <itkConnectedComponentImageFilter.h> |
33 |
|
|
#include <itkExtractImageFilter.h> |
34 |
|
|
#include <itkFloodFilledImageFunctionConditionalConstIterator.h> |
35 |
|
|
#include <itkGrayscaleMorphologicalClosingImageFilter.h> |
36 |
|
|
#include <itkMedianImageFilter.h> |
37 |
|
|
#include <itkRelabelComponentImageFilter.h> |
38 |
|
|
|
39 |
|
|
namespace sight::filter::image |
40 |
|
|
{ |
41 |
|
|
|
42 |
|
|
//------------------------------------------------------------------------------ |
43 |
|
|
|
44 |
|
|
template<typename IMAGEINPTR, typename IMAGELABELPTR> |
45 |
|
✗ |
void bug_work_around_labeling( |
46 |
|
|
IMAGEINPTR _image_in, |
47 |
|
|
IMAGELABELPTR _labeled, |
48 |
|
|
typename IMAGEINPTR::ObjectType::PixelType _background |
49 |
|
|
) |
50 |
|
|
{ |
51 |
|
✗ |
itk::ImageRegionIterator<typename IMAGEINPTR::ObjectType> i_it(_image_in, _image_in->GetLargestPossibleRegion()); |
52 |
|
✗ |
itk::ImageRegionIterator<typename IMAGELABELPTR::ObjectType> l_it(_labeled, _labeled->GetLargestPossibleRegion()); |
53 |
|
|
|
54 |
|
✗ |
while(!i_it.IsAtEnd()) |
55 |
|
|
{ |
56 |
|
✗ |
if(i_it.Get() == _background) |
57 |
|
|
{ |
58 |
|
✗ |
l_it.Set(itk::NumericTraits<typename IMAGELABELPTR::ObjectType::PixelType>::Zero); |
59 |
|
|
} |
60 |
|
|
|
61 |
|
✗ |
++i_it; |
62 |
|
✗ |
++l_it; |
63 |
|
|
} |
64 |
|
|
} |
65 |
|
|
|
66 |
|
|
//------------------------------------------------------------------------------ |
67 |
|
|
|
68 |
|
|
template<typename IMAGE_TYPE, unsigned int DIM> |
69 |
|
|
typename itk::Image<std::uint8_t, DIM>::Pointer threshold( |
70 |
|
|
typename itk::Image<IMAGE_TYPE, DIM>::Pointer _image, |
71 |
|
|
IMAGE_TYPE _lower_threshold, |
72 |
|
|
IMAGE_TYPE _upper_threshold |
73 |
|
|
) |
74 |
|
|
{ |
75 |
|
|
// ITK threshold |
76 |
|
|
typedef itk::BinaryThresholdImageFilter<itk::Image<IMAGE_TYPE, DIM>, |
77 |
|
|
itk::Image<std::uint8_t, DIM> > BinaryThresholdImageFilter; |
78 |
|
|
typename BinaryThresholdImageFilter::Pointer threshold_filter = BinaryThresholdImageFilter::New(); |
79 |
|
|
threshold_filter->SetInput(_image); |
80 |
|
|
threshold_filter->SetInsideValue(std::numeric_limits<std::uint8_t>::max()); |
81 |
|
|
threshold_filter->SetOutsideValue(std::numeric_limits<std::uint8_t>::min()); |
82 |
|
|
threshold_filter->SetLowerThreshold(_lower_threshold); |
83 |
|
|
threshold_filter->SetUpperThreshold(_upper_threshold); |
84 |
|
|
threshold_filter->Update(); |
85 |
|
|
|
86 |
|
|
return threshold_filter->GetOutput(); |
87 |
|
|
} |
88 |
|
|
|
89 |
|
|
//------------------------------------------------------------------------------ |
90 |
|
|
|
91 |
|
|
template<typename IMAGE_TYPE, unsigned int DIM> |
92 |
|
|
typename itk::Image<IMAGE_TYPE, DIM>::Pointer median( |
93 |
|
|
typename itk::Image<IMAGE_TYPE, DIM>::Pointer _image, |
94 |
|
|
std::size_t _x, |
95 |
|
|
std::size_t _y, |
96 |
|
|
std::size_t _z |
97 |
|
|
) |
98 |
|
|
{ |
99 |
|
|
// ITK median filter |
100 |
|
|
typedef itk::Image<IMAGE_TYPE, DIM> image_t; |
101 |
|
|
typedef itk::MedianImageFilter<image_t, image_t> MedianImageFilter; |
102 |
|
|
typename MedianImageFilter::Pointer median_filter = MedianImageFilter::New(); |
103 |
|
|
|
104 |
|
|
typename MedianImageFilter::radius_t radius; |
105 |
|
|
radius[0] = _x; |
106 |
|
|
radius[1] = _y; |
107 |
|
|
radius[2] = _z; |
108 |
|
|
|
109 |
|
|
median_filter->SetInput(_image); |
110 |
|
|
median_filter->SetRadius(radius); |
111 |
|
|
median_filter->Update(); |
112 |
|
|
|
113 |
|
|
return median_filter->GetOutput(); |
114 |
|
|
} |
115 |
|
|
|
116 |
|
|
//------------------------------------------------------------------------------ |
117 |
|
|
|
118 |
|
|
template<typename IMAGE_TYPE, unsigned int DIM> |
119 |
|
✗ |
typename itk::Image<std::uint8_t, DIM>::Pointer labeling( |
120 |
|
|
typename itk::Image<IMAGE_TYPE, DIM>::Pointer _image, |
121 |
|
|
unsigned int _num_labels |
122 |
|
|
) |
123 |
|
|
{ |
124 |
|
|
// ITK median filter |
125 |
|
|
typedef itk::Image<IMAGE_TYPE, DIM> image_t; |
126 |
|
|
typedef itk::Image<std::uint8_t, 3> label_image_t; |
127 |
|
|
|
128 |
|
|
// ITK labeling |
129 |
|
|
// Connected component filter |
130 |
|
|
typedef itk::ConnectedComponentImageFilter<image_t, label_image_t> ConnectedComponentFilter; |
131 |
|
✗ |
typename ConnectedComponentFilter::Pointer filter_cc = ConnectedComponentFilter::New(); |
132 |
|
✗ |
filter_cc->SetInput(_image); |
133 |
|
✗ |
filter_cc->SetBackgroundValue(0); // ignored by ITK !!! fixed by (*) |
134 |
|
✗ |
filter_cc->SetFullyConnected(true); |
135 |
|
✗ |
filter_cc->Update(); |
136 |
|
|
|
137 |
|
✗ |
typename label_image_t::Pointer labeled_img = filter_cc->GetOutput(); |
138 |
|
✗ |
bug_work_around_labeling(_image, labeled_img, 0); // (*) |
139 |
|
|
|
140 |
|
|
// Relabels connected component filter |
141 |
|
|
typedef itk::RelabelComponentImageFilter<label_image_t, label_image_t> RelabelFilter; |
142 |
|
✗ |
typename RelabelFilter::Pointer relabel_filter = RelabelFilter::New(); |
143 |
|
✗ |
relabel_filter->SetInPlace(true); // can be set inplace because it is an internal filter |
144 |
|
✗ |
relabel_filter->SetInput(labeled_img); |
145 |
|
✗ |
relabel_filter->Update(); |
146 |
|
|
|
147 |
|
|
// Output |
148 |
|
✗ |
typename label_image_t::Pointer img_out = relabel_filter->GetOutput(); |
149 |
|
✗ |
typename itk::ImageRegionIterator<label_image_t> itk_it_out(img_out, img_out->GetBufferedRegion()); |
150 |
|
|
|
151 |
|
✗ |
for(itk_it_out.GoToBegin() ; !itk_it_out.IsAtEnd() ; ++itk_it_out) |
152 |
|
|
{ |
153 |
|
✗ |
bool is_pixel = itk_it_out.Get() != itk::NumericTraits<IMAGE_TYPE>::Zero; |
154 |
|
|
|
155 |
|
✗ |
is_pixel = is_pixel && itk_it_out.Get() <= static_cast<IMAGE_TYPE>(_num_labels); |
156 |
|
|
|
157 |
|
|
if(!is_pixel) |
158 |
|
|
{ |
159 |
|
✗ |
itk_it_out.Set(decltype(itk_it_out) ::PixelType(itk::NumericTraits<IMAGE_TYPE>::Zero)); |
160 |
|
|
} |
161 |
|
|
} |
162 |
|
|
|
163 |
|
✗ |
return img_out; |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
//------------------------------------------------------------------------------ |
167 |
|
|
|
168 |
|
|
template<typename IMAGE_TYPE, unsigned int DIM> |
169 |
|
|
typename itk::Image<IMAGE_TYPE, DIM>::Pointer closing( |
170 |
|
|
typename itk::Image<IMAGE_TYPE, DIM>::Pointer _image, |
171 |
|
|
std::size_t _x, |
172 |
|
|
std::size_t _y, |
173 |
|
|
std::size_t _z |
174 |
|
|
) |
175 |
|
|
{ |
176 |
|
|
// ITK median filter |
177 |
|
|
typedef itk::Image<IMAGE_TYPE, DIM> image_t; |
178 |
|
|
typedef itk::BinaryBallStructuringElement<IMAGE_TYPE, 3> structuring_element_t; |
179 |
|
|
typedef itk::GrayscaleMorphologicalClosingImageFilter<image_t, image_t, |
180 |
|
|
structuring_element_t> ITKFilterType; |
181 |
|
|
typename image_t::Pointer itk_output_image; |
182 |
|
|
typename ITKFilterType::Pointer filter = ITKFilterType::New(); |
183 |
|
|
|
184 |
|
|
typename ITKFilterType::Pointer::ObjectType::KernelType structuring_element; |
185 |
|
|
typename ITKFilterType::Pointer::ObjectType::KernelType::size_t size; |
186 |
|
|
size[0] = _x; |
187 |
|
|
size[1] = _y; |
188 |
|
|
size[2] = _z; |
189 |
|
|
|
190 |
|
|
structuring_element.SetRadius(size); |
191 |
|
|
structuring_element.CreateStructuringElement(); |
192 |
|
|
filter->SetKernel(structuring_element); |
193 |
|
|
filter->SetInput(_image); |
194 |
|
|
filter->Update(); |
195 |
|
|
itk_output_image = filter->GetOutput(); |
196 |
|
|
|
197 |
|
|
return itk_output_image; |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
//------------------------------------------------------------------------------ |
201 |
|
|
|
202 |
|
|
template<typename IMAGE_TYPE, unsigned int DIM> |
203 |
|
|
typename itk::Image<IMAGE_TYPE, DIM>::Pointer fill_hole_2d( |
204 |
|
|
typename itk::Image<IMAGE_TYPE, DIM>::Pointer _image, |
205 |
|
|
unsigned int _direction, |
206 |
|
|
IMAGE_TYPE _foreground |
207 |
|
|
) |
208 |
|
|
{ |
209 |
|
|
typedef itk::Image<IMAGE_TYPE, DIM> Image3D; |
210 |
|
|
typedef itk::Image<IMAGE_TYPE, 2> Image2D; |
211 |
|
|
std::uint64_t nb_planes = _image->GetBufferedRegion().GetSize(_direction); |
212 |
|
|
|
213 |
|
|
for(std::uint64_t plane = 0 ; plane < nb_planes ; ++plane) |
214 |
|
|
{ |
215 |
|
|
itk::ImageRegion<3> region_to_extract = _image->GetBufferedRegion(); |
216 |
|
|
|
217 |
|
|
typedef typename itk::ExtractImageFilter<Image3D, Image2D> ExtractFilter; |
218 |
|
|
typename ExtractFilter::Pointer extractor = ExtractFilter::New(); |
219 |
|
|
|
220 |
|
|
// extracts plane along other "direction" |
221 |
|
|
region_to_extract.SetSize(_direction, 0); |
222 |
|
|
region_to_extract.SetIndex(_direction, static_cast<std::int64_t>(plane)); |
223 |
|
|
extractor->InPlaceOff(); |
224 |
|
|
extractor->set_input(_image); |
225 |
|
|
extractor->SetExtractionRegion(region_to_extract); |
226 |
|
|
extractor->SetDirectionCollapseToIdentity(); |
227 |
|
|
extractor->Update(); |
228 |
|
|
|
229 |
|
|
typename Image2D::Pointer image_2d = extractor->GetOutput(); |
230 |
|
|
|
231 |
|
|
typedef typename itk::BinaryFillholeImageFilter<Image2D> FillHoleFilter; |
232 |
|
|
typename FillHoleFilter::Pointer fill_hole = FillHoleFilter::New(); |
233 |
|
|
|
234 |
|
|
fill_hole->set_input(image_2d); |
235 |
|
|
fill_hole->SetForegroundValue(_foreground); |
236 |
|
|
fill_hole->SetFullyConnected(true); |
237 |
|
|
fill_hole->Update(); |
238 |
|
|
|
239 |
|
|
itk::ImageRegionConstIterator<Image2D> img_2d_it(fill_hole->GetOutput(), |
240 |
|
|
fill_hole->GetOutput()->GetBufferedRegion()); |
241 |
|
|
|
242 |
|
|
// creates a non "empty" region |
243 |
|
|
region_to_extract.SetSize(_direction, 1); |
244 |
|
|
itk::ImageRegionIterator<Image3D> img_3d_it(_image, region_to_extract); |
245 |
|
|
img_3d_it.GoToBegin(); |
246 |
|
|
img_2d_it.GoToBegin(); |
247 |
|
|
while(img_3d_it.IsAtEnd() == false) |
248 |
|
|
{ |
249 |
|
|
img_3d_it.Set(img_2d_it.Get()); |
250 |
|
|
++img_3d_it; |
251 |
|
|
++img_2d_it; |
252 |
|
|
} |
253 |
|
|
} |
254 |
|
|
|
255 |
|
|
return _image; |
256 |
|
|
} |
257 |
|
|
|
258 |
|
|
} // namespace sight::filter::image. |
259 |
|
|
|