DistanceTransformFillOp.h
1 /*
2 
3 MIT License
4 
5 Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13 
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 
25 */
26 /*
27 Part of Rack development has been done in the BALTRAD projects part-financed
28 by the European Union (European Regional Development Fund and European
29 Neighbourhood Partnership Instrument, Baltic Sea Region Programme 2007-2013)
30 */
31 #ifndef DISTANCETRANSFORMFILLOP_H_
32 #define DISTANCETRANSFORMFILLOP_H_
33 
34 #include <drain/image/ImageFile.h> // debugging
35 #include <drain/util/Fuzzy.h>
36 #include "FunctorOp.h"
37 #include "DistanceTransformOp.h"
38 
39 
40 namespace drain
41 {
42 
43 namespace image
44 {
45 
47 
52 template <class T>
54 {
55 public:
56 
57  virtual inline
58  int srcAlpha() const {
59  return 2; // REQUIRED
60  };
61 
62  // proximity (inverted distance)
63  typedef float dist_t;
64 
65  virtual
67 
68  virtual
69  void getDstConf(const ImageConf & srcConf, ImageConf & dstConf) const {
70 
71  dstConf.setGeometry(srcConf.getGeometry());
72  if (!dstConf.hasAlphaChannel())
73  dstConf.setAlphaChannelCount(1);
74 
75  }
76 
77 
79  virtual // TODO: non-virtual, ie, final!
80  void makeCompatible(const ImageConf & src, Image & dst) const {
81 
82  drain::Logger mout(getImgLog(), __FILE__, __FUNCTION__);
83  mout.debug3("src: ", src);
84 
85  ImageConf dstConf(dst.getConf());
86  dstConf.setCoordinatePolicy(src.getCoordinatePolicy());
87  getDstConf(src, dstConf);
88 
89  if (!dstConf.typeIsSet())
90  dstConf.setType(src.getType());
91 
92  if (!dstConf.hasAlphaChannel())
93  dstConf.setAlphaChannelCount(1);
94 
95 
96  dst.setConf(dstConf);
97 
98  mout.debug("clearing alpha (use traverseChannel(s) to avoid)" );
99 
100  // Important
101  dst.getAlphaChannel().fill(0);
102 
103  mout.debug3("dst: ", dst);
104 
105  };
106 
107 
109 
110  virtual
111  void traverseChannel(const Channel &src, const Channel &srcAlpha, Channel &dst, Channel &dstAlpha) const {
112  ImageTray<const Channel> srcTray; //(src, srcAlpha);
113  srcTray.setChannels(src);
114  srcTray.setAlphaChannels(srcAlpha);
115  ImageTray<Channel> dstTray; //(dst, dstAlpha);
116  dstTray.setChannels(dst);
117  dstTray.setAlphaChannels(dstAlpha);
118  traverseChannels(srcTray, dstTray);
119  };
120 
121 
122 
123 protected:
124 
125  DistanceTransformFillOp(const std::string &name, const std::string &description, dist_t horz = 14.0, dist_t vert = DistanceModel::nan_f,
126  DistanceModel::topol_t topology=DistanceModel::KNIGHT) : // PIX_ADJACENCY_
127  DistanceTransformOp<T>(name, description, horz, vert, topology), alphaThreshold(0.0, 0.0) {
128  this->parameters.link("alphaThreshold", alphaThreshold.tuple(), "0..1").fillArray = true;
129  };
130 
131  //DistanceTransformFillOp(const DistanceTransformFillOp & op) : DistanceTransformOp<T>(op) {
132  //};
133 
134 
136 
138 
139  //double alphaThreshold;
140  drain::Range<double> alphaThreshold;
141 
142 };
143 
148 template <class T>
150  // void DistanceTransformFillOp<T>::process(const ImageFrame &src, const ImageFrame &srcAlpha, ImageFrame & dst, ImageFrame & dstAlpha) const {
151 
152  drain::Logger mout(getImgLog(), __FILE__, __FUNCTION__);
153 
154  mout.debug("start: ", *this );
155 
156  if (!src.hasAlpha()){
157  mout.warn("src: ", src );
158  mout.error("required alpha channel missing in src" );
159  return;
160  }
161  else
162  mout.debug2("src: ", src );
163 
164  if (!dst.hasAlpha()){
165  mout.warn("dst: ", dst );
166  mout.error("required alpha channel missing in dst" );
167  return;
168  }
169  else
170  mout.debug2("dst: ", dst );
171 
172 
173  // mout.debug3("init params: " );
174  mout.debug3("init params, using alpha: " );
175  this->initializeParameters(src.getAlpha(), dst.getAlpha());
176  // dst.getAlpha().fill(0); NEW: MOved to ...
177 
178  //drain::image::File::write(src.getAlpha(), "dts-a.png");
179  //drain::image::File::write(dst.getAlpha(),"dt0-a.png");
180 
181 
182  mout.debug("calling traverseDownRight" );
183  // mout.debug2(src );
184  // mout.debug2(dst );
185  traverseDownRight(src, dst);
186  /*
187  if (getImgLog().getVerbosity() > 10){
188  drain::image::File::write(dst,"dtd-i.png");
189  drain::image::File::write(dstAlpha,"dtd-a.png");
190  }
191  */
192 
193  mout.debug("calling traverseUpLeft" );
194  // mout.debug2(dst );
195  traverseUpLeft(dst, dst);
196  /*
197  if (getImgLog().getVerbosity() > 10){
198  drain::image::File::write(dst,"dtu-i.png");
199  drain::image::File::write(dstAlpha,"dtu-a.png");
200  }
201  */
202  if (alphaThreshold.max > 0.0){ // TODO: check if (0,1) = neutral
203 
204  Channel & dstAlpha = dst.alpha.get();
205 
206  UnaryFunctorOp<FuzzyStep<double>,true> threshold;
207  threshold.functor.set(alphaThreshold);
208  mout.debug("Thresholding: ", threshold.functor );
209  // mout.debug(threshold );
210  threshold.traverseChannel(dstAlpha, dstAlpha);
211  //FilePng::write(dstAlpha, "a2.png");
212  }
213 
214 
215 }
216 
218 
222 template <class T>
224 
225  Logger mout(getImgLog(), __FILE__, __FUNCTION__);
226 
227  mout.debug("distModel: ", this->distanceModel );
228 
229  DistanceNeighbourhood chain;
230  this->distanceModel.createChain(chain, true);
231 
232  mout.debug2("neighbourHood ", drain::sprinter(chain) );
233 
234  const Channel & srcAlpha = srcTray.getAlpha();
235  Channel & dstAlpha = dstTray.getAlpha();
236 
237  CoordinateHandler2D coordinateHandler(srcTray.get(0));
238  mout.debug("coordHandler ", coordinateHandler );
239 
240  mout.debug3("src alpha:", srcAlpha );
241  mout.debug3("dst alpha:", dstAlpha );
242 
244  Point2D<int> p;
245  Point2D<int> pTest;
246  Point2D<int> pWin;
247 
248  // Winning distance
249  dist_t dWin;
250  // Distance candidate
251  dist_t dTest;
252 
253  const size_t K = std::min(srcTray.size(), dstTray.size());
254 
255  const Range<int> & xRange = coordinateHandler.getXRange();
256  const Range<int> & yRange = coordinateHandler.getYRange();
257 
258  size_t address = 0;
259  size_t addressWin = 0;
260 
261  mout.debug("main loop, K=", K );
262  for (p.y=0; p.y<=yRange.max; ++p.y){
263  for (p.x=0; p.x<=xRange.max; ++p.x){
264 
265  address = dstAlpha.address(p.x, p.y);
266 
267  // Take source value as default
268  dWin = srcAlpha.get<dist_t>(address);
269  pWin.setLocation(p);
270 
271  for (const DistanceElement & elem: chain){
272  pTest.setLocation(p.x + elem.diff.x, p.y + elem.diff.y);
273  coordinateHandler.handle(pTest);
274  dTest = this->distanceModel.decrease(dstAlpha.get<dist_t>(pTest), elem.coeff);
275  if (dTest > dWin){
276  dWin = dTest;
277  pWin = pTest; // consider direct addressWin = dstAlpha.address(pWin.x, pWin.y); so, pTest
278  }
279  }
280 
281 
282  // Finally, update target image (if data found)
283  if (dWin > 0.0){
284 
285  dstAlpha.put(address, dWin);
286 
287  addressWin = dstAlpha.address(pWin.x, pWin.y);
288 
289  if (addressWin != address){
290  for (size_t k=0; k<K; ++k) // copy from DST
291  dstTray.get(k).put(address, dstTray.get(k).get<dist_t>(addressWin));
292  }
293  else {
294  for (size_t k=0; k<K; ++k) // copy from SRC
295  dstTray.get(k).put(address, srcTray.get(k).get<dist_t>(address));
296  }
297 
298  }
299 
300  /*
301  if ((p.x == p.y) && ((p.x&15)==0)){
302  std::cerr << p << "\t a=" << address << ", aW=" << addressWin << "\t" << dWin << '>' << dTest << '\t';
303  std::cerr << p << "\t s=" << srcAlpha.get<dist_t>(address) << '=' << srcAlpha.get<dist_t>(p) << '\t';
304  std::cerr << p << "\t d=" << dstAlpha.get<dist_t>(address) << '=' << dstAlpha.get<dist_t>(p) << '\n';
305  }
306  */
307 
308  }
309  }
310 
311 }
312 
313 
314 
316 
320 template <class T>
322 
323  Logger mout(getImgLog(), __FILE__, __FUNCTION__);
324  mout.debug("start" );
325 
326  mout.debug2("this->distanceModel", this->distanceModel );
327 
328  DistanceNeighbourhood chain;
329  this->distanceModel.createChain(chain, false);
330 
331  CoordinateHandler2D coordinateHandler(srcTray.get(0));
332 
333  const Channel & srcAlpha = srcTray.getAlpha();
334  Channel & dstAlpha = dstTray.getAlpha();
335 
337  Point2D<int> p;
338  Point2D<int> pTest;
339  Point2D<int> pWin;
340 
341  // proximity (inverted distance)
342  dist_t dWin;
343  dist_t dTest;
344 
345  // Current position
346  size_t address;
347  // Winning position
348  size_t addressWin;
349 
350  const size_t K = std::min(srcTray.size(), dstTray.size());
351 
352  const Range<int> & xRange = coordinateHandler.getXRange();
353  const Range<int> & yRange = coordinateHandler.getYRange();
354 
355  mout.debug("main loop, K=", K);
356 
357  for (p.y=yRange.max; p.y>=0; --p.y){
358  for (p.x=xRange.max; p.x>=0; --p.x){
359 
360  address = dstAlpha.address(p.x, p.y);
361 
362  dWin = srcAlpha.get<dist_t>(address);
363  pWin.setLocation(p);
364 
365  for (const DistanceElement & elem: chain){
366  pTest.setLocation(p.x + elem.diff.x, p.y + elem.diff.y);
367  coordinateHandler.handle(pTest);
368  dTest = this->distanceModel.decrease(dstAlpha.get<dist_t>(pTest), elem.coeff);
369  if (dTest > dWin){
370  dWin = dTest;
371  pWin = pTest;
372  }
373  }
374 
375 
376  // Finally, update target image (if stronger data found)
377  if (dWin > 0.0){
378 
379  dstAlpha.put(address, dWin);
380 
381  addressWin = dstAlpha.address(pWin.x, pWin.y);
382 
383  if (addressWin != address){
384  for (size_t k=0; k<K; ++k) // copy from DST
385  dstTray.get(k).put(address, dstTray.get(k).get<dist_t>(addressWin));
386  }
387  else {
388  for (size_t k=0; k<K; ++k) // copy from SRC
389  dstTray.get(k).put(address, srcTray.get(k).get<dist_t>(address));
390  }
391 
392  }
393 
394  }
395  }
396 }
397 
398 
399 
400 
401 
402 
403 
404 
406 
447 class DistanceTransformFillLinearOp : public DistanceTransformFillOp<DistanceModelLinear>
448 {
449 
450 public:
451 
452  inline
453  DistanceTransformFillLinearOp(dist_t horz = 12.3, dist_t vert = DistanceModel::nan_f,
454  DistanceModel::topol_t topology=DistanceModel::KNIGHT) :
455  DistanceTransformFillOp<DistanceModelLinear> (__FUNCTION__, "Spreads intensities linearly up to distance defined by alpha channel.",
456  horz, vert, topology) {
457  };
458 
459 };
460 
461 
501 class DistanceTransformFillExponentialOp : public DistanceTransformFillOp<DistanceModelExponential>
502 {
503 
504 public:
505 
506  inline
507  DistanceTransformFillExponentialOp(dist_t horz = 12.0, dist_t vert = DistanceModel::nan_f,
508  //DistanceModel::topol_t topology=DistanceModel::PIX_ADJACENCY_KNIGHT) :
509  DistanceModel::topol_t topology=DistanceModel::KNIGHT) :
510  DistanceTransformFillOp<DistanceModelExponential> (__FUNCTION__, "Spreads intensities exponentially up to distance defined by alpha intensities.",
511  horz, vert, topology) {
512  };
513 
514 };
515 
516 
517 }
518 }
519 
520 
521 #endif /* DISTANCETRANSFORMFILL_H_ */
522 
LogSourc e is the means for a function or any program segment to "connect" to a Log.
Definition: Log.h:308
Logger & error(const TT &... args)
Echoes.
Definition: Log.h:412
Logger & warn(const TT &... args)
Possible error, but execution can continue.
Definition: Log.h:426
Logger & debug(const TT &... args)
Public, yet typically used "internally", when TIMING=true.
Definition: Log.h:676
Logger & debug2(const TT &... args)
Debug information.
Definition: Log.h:686
Image with static geometry.
Definition: ImageChannel.h:60
Definition: CoordinateHandler.h:62
virtual coord_overflow_t handle(int &x, int &y) const
Ensures the validity of the coordinates. If inside limits, arguments (x,y) remain intact and 0 is ret...
Definition: CoordinateHandler.h:190
Definition: DistanceModel.h:76
Definition: DistanceTransformFillOp.h:502
Spreeads values using inverse linear distance transform.
Definition: DistanceTransformFillOp.h:448
Template for DistanceTransformFillLinearOp and DistanceTransformFillExponentialOp.
Definition: DistanceTransformFillOp.h:54
void traverseUpLeft(ImageTray< Channel > &src, ImageTray< Channel > &dst) const
Step 1/2: backward traversal.
Definition: DistanceTransformFillOp.h:321
void traverseChannels(const ImageTray< const Channel > &src, ImageTray< Channel > &dst) const
Definition: DistanceTransformFillOp.h:149
virtual int srcAlpha() const
Tell if alpha channel(s) is required in input.
Definition: DistanceTransformFillOp.h:58
virtual void getDstConf(const ImageConf &srcConf, ImageConf &dstConf) const
Ensures dst the same geometry and type with src.
Definition: DistanceTransformFillOp.h:69
virtual void makeCompatible(const ImageConf &src, Image &dst) const
Like ImageOp::makeCompatible(), but clears the alpha channel.
Definition: DistanceTransformFillOp.h:80
void traverseDownRight(const ImageTray< const Channel > &src, ImageTray< Channel > &dst) const
Step 1/2: forward traversal.
Definition: DistanceTransformFillOp.h:223
virtual void traverseChannel(const Channel &src, const Channel &srcAlpha, Channel &dst, Channel &dstAlpha) const
Apply to single channel with alpha.
Definition: DistanceTransformFillOp.h:111
Definition: DistanceTransformOp.h:58
bool typeIsSet() const
Get the storage type.
Definition: ImageConf.h:116
const std::type_info & getType() const
Linear scaling.
Definition: ImageConf.h:110
void setType(const std::type_info &t)
Set storage type.
Definition: ImageConf.h:121
Struct for image (excluding data)
Definition: ImageConf.h:333
void setCoordinatePolicy(const T &policy)
Does not set any CoordinateHandler object.
Definition: ImageConf.h:368
void put(size_t i, T x)
Sets the intensity in location i to x. See \address.
Definition: ImageFrame.h:192
size_t address(size_t i) const
Computes the index location from image coordinates. Does not involve bit resolution.
Definition: ImageFrame.h:148
T get(size_t i) const
Gets the intensity at location i. See address().
Definition: ImageFrame.h:254
void fill(T x)
Sets the intensities to given value. Does not change image geometry.
Definition: ImageFrame.h:330
Container applicable for Channels and Images, with alpha support.
Definition: ImageTray.h:267
void setChannels(T2 &img)
Splits.
Definition: ImageTray.h:402
void setAlphaChannels(T2 &img)
Splits.
Definition: ImageTray.h:434
const image_t & getAlpha(size_t i=0) const
Returns the i'th alpha image.
Definition: ImageTray.h:329
Class for multi-channel digital images. Supports dynamic typing with base types (char,...
Definition: Image.h:184
const image_t & get(size_t i=0) const
Returns the i'th image.
Definition: ImageTray.h:81
Class for using simple function objects (like std::functor) for sequential pixel iteration.
Definition: FunctorOp.h:135
void traverseChannel(const Channel &src, Channel &dst) const
Process the image.
Definition: FunctorOp.h:225
Definition: DataSelector.cpp:1277