21 #ifndef mia_internal_watershed_hh 22 #define mia_internal_watershed_hh 35 class TWatershed :
public watershed_traits<dim>::Handler::Product {
38 typedef typename PNeighbourhood::element_type::value_type
MPosition;
39 typedef typename watershed_traits<dim>::Handler
Handler;
40 typedef typename Handler::Product
CFilter;
41 typedef typename CFilter::Pointer
PFilter;
42 typedef typename CFilter::Image
CImage;
43 typedef typename CImage::Pointer
PImage;
44 typedef typename CImage::dimsize_type
Position;
47 TWatershed(PNeighbourhood neighborhood,
bool with_borders,
float treash,
bool eval_grad);
49 template <
template <
typename>
class Image,
typename T>
52 struct PixelWithLocation {
59 template <
template <
typename>
class Image,
typename T>
60 bool grow(
const PixelWithLocation& p, Image<unsigned int>& labels,
const Image<T>& data)
const;
62 friend bool operator < (
const PixelWithLocation& lhs,
const PixelWithLocation& rhs) {
63 mia::less_then<Position> l;
64 return lhs.value > rhs.value||
65 ( lhs.value == rhs.value && l(rhs.pos, lhs.pos));
68 std::vector<MPosition> m_neighborhood;
85 virtual typename watershed_traits<dim>::Handler::Product *do_create()
const;
86 virtual const std::string do_get_descr()
const;
87 typename watershed_traits<dim>::PNeighbourhood m_neighborhood;
96 m_with_borders(with_borders),
100 m_neighborhood.reserve(neighborhood->size() - 1);
101 for (
auto i = neighborhood->begin(); i != neighborhood->end();++i)
102 if ( *i != MPosition::_0 )
103 m_neighborhood.push_back(*i);
106 m_togradnorm = Handler::instance().produce(
"gradnorm");
109 static const unsigned int boundary_label = std::numeric_limits<unsigned int>::max();
112 template <
template <
typename>
class Image,
typename T>
113 bool TWatershed<dim>::grow(
const PixelWithLocation& p, Image<unsigned int>& labels,
const Image<T>& data)
const 115 const auto size = data.get_size();
116 std::vector<Position> backtrack;
117 std::priority_queue<Position, std::vector<Position>, mia::less_then<Position> > locations;
118 bool has_backtracked =
false;
120 backtrack.push_back(p.pos);
122 std::vector<Position> new_positions;
123 new_positions.reserve(m_neighborhood.size());
125 float value = p.value;
126 unsigned int label = labels(p.pos);
128 for (
auto i = m_neighborhood.begin(); i != m_neighborhood.end(); ++i) {
130 if (new_pos < size && !labels(new_pos) && data(new_pos) <= value) {
131 locations.push(new_pos);
132 backtrack.push_back(new_pos);
136 while (!locations.empty()) {
138 auto loc = locations.top();
141 new_positions.clear();
143 unsigned int first_label = 0;
144 bool loc_is_boundary =
false;
146 for (
auto i = m_neighborhood.begin(); i != m_neighborhood.end() && !loc_is_boundary; ++i) {
149 if (! (new_pos < size) )
152 cverb << data(new_pos);
154 if (data(new_pos) > value)
157 unsigned int new_pos_label = labels(new_pos);
158 if (!new_pos_label) {
159 new_positions.push_back(new_pos);
169 first_label = new_pos_label;
170 }
else if (first_label != new_pos_label) {
172 loc_is_boundary =
true;
176 if (!loc_is_boundary) {
177 labels(loc) = first_label;
178 backtrack.push_back(loc);
179 if (first_label != label) {
185 if (!has_backtracked) {
186 for_each(backtrack.begin(), backtrack.end(),
187 [&first_label, &labels](
const Position& p){labels(p) = first_label;});
189 has_backtracked =
true;
197 backtrack.push_back(loc);
201 for_each(new_positions.begin(), new_positions.end(),
202 [&locations](
const Position& p){locations.push(p);});
206 while (!locations.empty() && locations.top() == loc)
210 return has_backtracked;
214 template <
template <
typename>
class Image,
typename T>
217 auto sizeND = data.get_size();
218 Image<unsigned int> labels(data.get_size());
221 auto gradient_range = std::minmax_element(data.begin(), data.end());
222 float thresh = m_thresh * (*gradient_range.second - *gradient_range.first) + *gradient_range.first;
224 std::priority_queue<PixelWithLocation> pixels;
226 auto i = data.begin_range(Position::_0, data.get_size());
227 auto e = data.end_range(Position::_0, data.get_size());
228 auto l = labels.begin();
232 p.value = *i > thresh ? *i : thresh;
233 if (p.value <= thresh) {
236 if (!grow(p, labels, data))
245 while (!pixels.empty()) {
246 auto pixel = pixels.top();
249 if (labels(pixel.pos)) {
253 unsigned int first_label = 0;
254 bool is_boundary =
false;
257 for (
auto i = m_neighborhood.begin(); i != m_neighborhood.end() && !is_boundary; ++i) {
259 if (new_pos < sizeND) {
260 auto l = labels(new_pos);
265 if (first_label != l)
266 is_boundary = m_with_borders;
272 labels(pixel.pos) = first_label;
275 cvdebug() <<
" set " << pixel.pos <<
" with " << data(pixel.pos) <<
" to "<< labels(pixel.pos) <<
"\n";
281 labels(pixel.pos) = next_label;
282 if (!grow(pixel, labels, data))
288 cvmsg() <<
"Got " << next_label <<
" distinct bassins\n";
289 if (next_label < 255) {
290 Image<unsigned char> *result =
new Image<unsigned char>(data.get_size(), data);
291 std::transform(labels.begin(), labels.end(), result->begin(),
292 [](
unsigned int p)->
unsigned char {
return (p !=
boundary_label) ?
static_cast<unsigned char>(p) : 255; });
294 }
else if (next_label < std::numeric_limits<unsigned short>::max()) {
295 Image<unsigned short> *result =
new Image<unsigned short>(data.get_size(), data);
296 std::transform(labels.begin(), labels.end(), result->begin(),
297 [](
unsigned int p)->
unsigned short {
return (p !=
boundary_label) ?
static_cast<unsigned short>(p) :
298 std::numeric_limits<unsigned short>::max(); });
301 Image<unsigned int> * result =
new Image<unsigned int>(data.get_size(), data);
302 copy(labels.begin(), labels.end(), result->begin());
312 return m_togradnorm ?
mia::filter(*
this, *m_togradnorm->filter(image)):
318 watershed_traits<dim>::
Handler::Interface(
"ws"),
319 m_with_borders(false),
323 this->add_parameter(
"n",
make_param(m_neighborhood,
"sphere:r=1",
false,
"Neighborhood for watershead region growing"));
324 this->add_parameter(
"mark",
new mia::CBoolParameter(m_with_borders,
false,
"Mark the segmented watersheds with a special gray scale value"));
325 this->add_parameter(
"thresh",
make_coi_param(m_thresh, 0, 1.0,
false,
"Relative gradient norm threshold. The actual value " 326 "threshold value is thresh * (max_grad - min_grad) + min_grad. Bassins " 327 "separated by gradients with a lower norm will be joined"));
328 this->add_parameter(
"evalgrad",
new mia::CBoolParameter(m_eval_grad,
false,
"Set to 1 if the input image does " 329 "not represent a gradient norm image"));
333 typename watershed_traits<dim>::Handler::Product *
336 return new TWatershed<dim>(m_neighborhood, m_with_borders, m_thresh, m_eval_grad);
342 return "basic watershead segmentation.";
watershed_traits< dim >::Handler Handler
TWatershed< dim >::result_type operator()(const Image< T > &data) const
CImage::dimsize_type Position
static const unsigned int boundary_label
CParameter * make_param(T &value, bool required, const char *descr)
plugin for the templated version of the standard watershed algorithm
CTParameter< bool > CBoolParameter
boolean parameter
#define cverb
define a shortcut to the raw output stream
#define NS_MIA_BEGIN
conveniance define to start the mia namespace
static F::result_type filter(const F &f, const B &b)
vstream & cvmsg()
send messages to this stream adapter
PNeighbourhood::element_type::value_type MPosition
TWatershed(PNeighbourhood neighborhood, bool with_borders, float treash, bool eval_grad)
friend bool operator<(const PixelWithLocation &lhs, const PixelWithLocation &rhs)
CParameter * make_coi_param(T &value, S1 lower_bound, S2 upper_bound, bool required, const char *descr)
watershed_traits< dim >::PNeighbourhood PNeighbourhood
templated version of the standard watershed algorithm
#define NS_MIA_END
conveniance define to end the mia namespace