Loading...
Searching...
No Matches
DistanceTransformOp.h
1/*
2
3MIT License
4
5Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26/*
27Part of Rack development has been done in the BALTRAD projects part-financed
28by the European Union (European Regional Development Fund and European
29Neighbourhood Partnership Instrument, Baltic Sea Region Programme 2007-2013)
30*/
31#ifndef DISTANCETRANSFORMOP_H_
32#define DISTANCETRANSFORMOP_H_
33
34
35
36#include <math.h>
37
38#include "drain/image/CoordinateHandler.h"
39#include "drain/image/DistanceModelLinear.h"
40#include "drain/image/DistanceModelExponential.h"
41
42#include "ImageOp.h"
43
44
45namespace drain
46{
47
48namespace image
49{
50
51
52
56template <class T>
58{
59
60public:
61
62
63 typedef float dist_t;
64
65 virtual ~DistanceTransformOp(){};
66
67 inline
68 void setRadius(dist_t width, dist_t height=DistanceModel::nan_f, dist_t width2=DistanceModel::nan_f, dist_t height2=DistanceModel::nan_f){
69 distanceModel.setRadius(width, height, width2, height2);
70 };
71
72
73
74 virtual
75 void traverseChannels(const ImageTray<const Channel> & src, ImageTray<Channel> & dst) const {
76 this->traverseChannelsSeparately(src, dst);
77 };
78
79 virtual
80 void traverseChannel(const Channel & src, Channel &dst) const ;
81
83 virtual
84 inline
85 void traverseChannel(const Channel &src, const Channel &srcAlpha, Channel &dst, Channel &dstAlpha) const {
86 drain::Logger mout(getImgLog(), __FILE__, __FUNCTION__);
87 mout.note("discarding alpha channels, redirecting to traverseChannel(src, dst)");
88 traverseChannel(src, dst);
89 };
90
92 /*
93 */
94 virtual inline
95 void getDstConf(const ImageConf &src, ImageConf & dst) const {
96
97 dst.setGeometry(src.getGeometry());
98
99 if (src.isPhysical()){
100 dst.setPhysicalRange(src.getPhysicalRange(), true);
101 }
102 // dst.setPhysicalRange(0.0, 1.0);
103
105
106
107 }
108
109 /*
110 virtual // TODO: non-virtual, ie, final!
111 void makeCompatible(const ImageConf & src, Image & dst) const {
112 drain::Logger mout(getImgLog(), __FILE__, __FUNCTION__);
113 mout.warn("derived " );
114 ImageOp::makeCompatible(src, dst);
115 }
116 */
117
118 /*
119 virtual
120 inline
121 void make Compatible(const ImageFrame &src, Image &dst) const {
122
123 drain::Logger mout(getImgLog(), __FILE__, __FUNCTION__);
124 mout.debug3("src: " , src );
125
126 //if (!dst.typeIsSet())
127 //dst.setType(src.getType());
128 if (dst.getType() != src.getType()){
129 mout.note(" changing dst image type: " , dst.getType().name() , '>' , src.getType().name() );
130 }
131
132 dst.copyShallow(src);
133 // dst.initialize(src.getType(), src.getGeometry());
134 // dst.adoptScaling(src);
135
136 mout .debug3() << "dst: " << dst << mout.endl;
137
138 };
139 */
140
141 // Debugging
142
143 virtual
144 const DistanceModel & getDistanceModel() const {
145 return distanceModel;
146 };
147
148 virtual
149 DistanceModel & getDistanceModel() {
150 return distanceModel;
151 };
152
153
154protected:
155
156 mutable T distanceModel;
157
158 //int topology;
159
160 DistanceTransformOp(const std::string &name, const std::string &description, float width, float height,
161 //DistanceTransformOp(const std::string &name, float width, float height,
162 DistanceModel::topol_t topology=DistanceModel::KNIGHT) : // PIX_ADJACENCY_
163 ImageOp(name, description) { // TODO: from model
164 // drain::Logger mout(getImgLog(), __FILE__, __FUNCTION__);
165 // mout.attention("model:", distanceModel);
166 parameters.append(distanceModel.getParameters());
167 distanceModel.setTopology(topology);
168 // mout.attention("width:", width, ", height:", height);
169 //distanceModel.setRadius(width, height, width, height);
170 distanceModel.setRadiusVerbatim(width, height, width, height); // NEW 2024: does not "expand" defaults.
171 // mout.attention("model:", distanceModel);
172 };
173
174 DistanceTransformOp(const DistanceTransformOp & op) : ImageOp(op) {
175 parameters.append(this->distanceModel.getParameters());
176 //setParameters(op.getParameters());
177 this->distanceModel.setParameters(op.distanceModel.getParameters());
178 this->distanceModel.update(); // Important: handles nan_f's. Is it desired?
179 };
180
181 // Extend default range (of src) when coord policy is WRAP
182 Range<int> getHorzRange(const CoordinateHandler2D & coordinateHandler) const ;
183
184 // Extend default range (of src) when coord policy is WRAP
185 Range<int> getVertRange(const CoordinateHandler2D & coordinateHandler) const;
186
187
189
192 virtual
193 void initializeParameters(const ImageFrame &src, const ImageFrame &dst) const {
194
195 drain::Logger mout(getImgLog(), __FILE__, __FUNCTION__);
196
197 // WAS: src (all) // .getEncoding()
198 const double physMax = dst.getConf().requestPhysicalMax(100.0);
199 const double codeMax = dst.getScaling().inv(physMax);
200
201 mout.debug("dst of type=", Type::getTypeChar(dst.getType()), ", scaling: ", dst.getScaling());
202
203 if (dst.getScaling().isPhysical()){
204 mout.info("ok, physical scaling [", dst.getConf().getPhysicalRange(), "] : ");
205 }
206 else if (Type::call<typeIsSmallInt>(dst.getType())){
207 mout.note("no physical scaling, but small int, guessing: ");
208 }
209 else {
210 mout.warn("no physical scaling, no small int: default ");
211 }
212 mout.note("physMax=", physMax, " => ", codeMax);
213
214 distanceModel.setMax(codeMax);
215 // distModel.setMax(dst.getMax<double>()); // why not dst
216 distanceModel.update(); // radii
217 }
218
219
220
221 void traverseDownRight(const Channel & src, Channel & dst) const ;
222
223 void traverseUpLeft(const Channel & src, Channel & dst) const ;
224
225
226
227};
228
229template <class T>
231
232 Range<int> xRange = coordinateHandler.getXRange();
233 const Bidirectional<float> & radiusHorz = getDistanceModel().getRadiusHorz();
234
235 if (coordinateHandler.policy.xUnderFlowPolicy == EdgePolicy::WRAP)
236 xRange.min -= radiusHorz.backward;
237
238 if (coordinateHandler.policy.xOverFlowPolicy == EdgePolicy::WRAP)
239 xRange.max += radiusHorz.forward;
240
241 return xRange;
242}
243
244template <class T>
246
247 Logger mout(getImgLog(), __FILE__, __FUNCTION__);
248
249 Range<int> yRange = coordinateHandler.getYRange();
250
251 //mout.warn("yRange: " , yRange );
252
253 const Bidirectional<float> & radiusVert = getDistanceModel().getRadiusVert();
254
255 // mout.warn("radiusVert: " , radiusVert );
256
257
258 if (coordinateHandler.policy.yUnderFlowPolicy == EdgePolicy::WRAP)
259 yRange.min -= radiusVert.backward;
260
261 if (coordinateHandler.policy.yOverFlowPolicy == EdgePolicy::WRAP)
262 yRange.max += radiusVert.forward;
263
264 // mout.warn("yRange: " , yRange );
265
266 return yRange;
267
268}
269
270template <class T>
272{
273
274 Logger mout(getImgLog(), __FILE__, __FUNCTION__);
275
276 //mout.warn("start" );
277 mout.debug("model max: ", getDistanceModel().getMax());
278
279 //File::write(dst,"DistanceTransformOp-dst0.png");
280 traverseDownRight(src,dst);
281 //File::write(dst,"DistanceTransformOp-dst1.png");
282 traverseUpLeft(dst,dst);
283 //File::write(dst,"DistanceTransformOp-dst2.png");
284
285}
286
287
288template <class T>
290{
291
292 Logger mout(getImgLog(), __FILE__, __FUNCTION__);
293 //mout.debug("start" );
294
295 //const DistanceModel & distModel = getDistanceModel();
296 mout.debug2("distModel:", this->distanceModel);
297
298 DistanceNeighbourhood chain;
299 this->distanceModel.createChain(chain, true);
300
301 mout.debug2("coordPolicy: ", src.getCoordinatePolicy());
302
303 CoordinateHandler2D coordinateHandler(src); // TODO: change param to ImageConf
304 mout.debug2("coordHandler:", coordinateHandler);
305
306
307 const ValueScaling src2dst(dst.getConf().getTypeMax<double>()/src.getConf().getTypeMax<double>(), 0);
308 //const ValueScaling src2dst(src.getScaling(), dst.getScaling());
309
310 /*
311 for (double d: {0.0, 0.5, 1.0, 255.0, 65535.0}){
312 mout.special(d, " =>\t", src2dst.fwd(d), " inv:", src2dst.inv(d));
313 }
314 */
315
316 // proximity (inverted distance)
317 dist_t d;
318 dist_t dTest;
319
320 // Point in the source image
321 Point2D<int> pTest;
322
323 // Point in the target image
324 Point2D<int> p;
325 Point2D<int> pSafe;
326
327 Range<int> xRange = getHorzRange(coordinateHandler);
328 Range<int> yRange = getVertRange(coordinateHandler);
329 mout.debug2(xRange, " - ", yRange);
330
331 // Experimental (element horz/vert topology not yet implemented)
332 // Possibly wrong... not interchangible due to to scanning element geometry?
333 /*
334 bool HORZ = true;
335 const Range<int> & inner = HORZ ? xRange : yRange;
336 const Range<int> & outer = HORZ ? yRange : xRange;
337 int & innerValue = HORZ ? p.x : p.y;
338 int & outerValue = HORZ ? p.y : p.x;
339 mout.debug2("outer range:" , outer );
340 mout.debug2("inner range:" , inner );
341 */
342 //Range<int>
343
344 // Todo: extended area, needs coordinateHandler.handle(p);?
345 for (p.y=yRange.min; p.y<=yRange.max; ++p.y){
346 for (p.x=xRange.min; p.x<=xRange.max; ++p.x){
347
348 pSafe.setLocation(p);
349 if (coordinateHandler.handle(pSafe) == CoordinateHandler2D::IRREVERSIBLE)
350 continue;
351
352 // Take (converted) source value as default
353 d = src2dst.fwd(src.get<dist_t>(pSafe));
354
355 // Compare to previous value
356 dTest = dst.get<dist_t>(pSafe);
357 if (dTest > d){
358 d = dTest;
359 }
360
361 for (const DistanceElement & elem: chain){
362 pTest.setLocation(pSafe.x + elem.diff.x, pSafe.y + elem.diff.y);
363 coordinateHandler.handle(pTest);
364 dTest = this->distanceModel.decrease(dst.get<dist_t>(pTest), elem.coeff);
365 if (dTest > d){
366 d = dTest;
367 }
368 }
369
370 if (d > 0){
371 dst.put(pSafe,d);
372 }
373
374 };
375 };
376
377}
378
379
380
381template <class T>
383
384 Logger mout(getImgLog(), __FILE__, __FUNCTION__);
385 mout.debug("start");
386
387 //const DistanceModel & distModel = getDistanceModel();
388 mout.debug2("distModel:", this->distanceModel);
389
390 DistanceNeighbourhood chain;
391 this->distanceModel.createChain(chain, false);
392
393 CoordinateHandler2D coordinateHandler(src);
394 mout.debug2("coordHandler:", coordinateHandler);
395
396 //const ValueScaling src2dst(src.scaling.scale, src.scaling.offset, dst.scaling.scale, dst.scaling.offset);
397 // const ValueScaling src2dst(src.getScaling(), dst.getScaling());
398 const ValueScaling src2dst(dst.getConf().getTypeMax<double>()/src.getConf().getTypeMax<double>(), 0);
399
400 // proximity (inverted distance)
401 dist_t d;
402 dist_t dTest;
403
405 Point2D<int> pTest(0,0);
406
407 // Point in the target image
408 Point2D<int> p;
409 Point2D<int> pSafe;
410
411 //const Range<int> & xRange = coordinateHandler.getXRange();
412 //const Range<int> & yRange = coordinateHandler.getYRange();
413 Range<int> xRange = getHorzRange(coordinateHandler); //coordinateHandler.getXRange();
414 Range<int> yRange = getVertRange(coordinateHandler); // coordinateHandler.getYRange();
415 mout.special(xRange, ',', yRange);
416
417 for (p.y=yRange.max; p.y>=yRange.min; --p.y){
418 for (p.x=xRange.max; p.x>=xRange.min; --p.x){
419
420 pSafe.setLocation(p);
421 if (coordinateHandler.handle(pSafe) == CoordinateHandler2D::IRREVERSIBLE)
422 continue;
423
424 // Take (converted) source value as default
425 d = src2dst.fwd(src.get<dist_t>(pSafe));
426
427 // Compare to previous value
428 dTest = dst.get<dist_t>(pSafe);
429 if (dTest > d){
430 d = dTest;
431 }
432
433 for (const DistanceElement & elem: chain){
434 pTest.setLocation(pSafe.x + elem.diff.x, pSafe.y + elem.diff.y);
435 coordinateHandler.handle(pTest);
436 dTest = this->distanceModel.decrease(dst.get<dist_t>(pTest), elem.coeff);
437 if (dTest > d){
438 d = dTest;
439 }
440 }
441
442 if (d>0)
443 dst.put(pSafe,d);
444
445 }
446 }
447
448
449}
450
451
452
453
454
455
457
481class DistanceTransformLinearOp : public DistanceTransformOp<DistanceModelLinear>
482{
483public:
484
485 inline
486 DistanceTransformLinearOp(float horz = 10.0, float vert = DistanceModel::nan_f, DistanceModel::topol_t topology = DistanceModel::KNIGHT): // PIX_ADJACENCY_
487 DistanceTransformOp<DistanceModelLinear>(__FUNCTION__, "Linearly decreasing intensities - applies decrements.", horz, vert, topology) {
488 };
489
490
491};
492
512class DistanceTransformExponentialOp : public DistanceTransformOp<DistanceModelExponential>
513{
514public:
515
517
520 inline
521 DistanceTransformExponentialOp(dist_t horz = 10.0, dist_t vert = DistanceModel::nan_f, DistanceModel::topol_t topology = DistanceModel::KNIGHT): // PIX_ADJACENCY_
522 DistanceTransformOp<DistanceModelExponential>(__FUNCTION__, "Exponentially decreasing intensities. Set half-decay radii.", horz, vert, topology) {
523 };
524
525};
526
527} // image::
528} // drain::
529
530#endif /*DISTANCETRANSFORMOP_H_*/
531
Definition DistanceModel.h:46
LogSourc e is the means for a function or any program segment to "connect" to a Log.
Definition Log.h:312
Logger & warn(const TT &... args)
Possible error, but execution can continue.
Definition Log.h:430
Logger & debug(const TT &... args)
Debug information.
Definition Log.h:666
Logger & note(const TT &... args)
For top-level information.
Definition Log.h:489
Logger & special(const TT &... args)
Other useful information.
Definition Log.h:531
Logger & debug2(const TT &... args)
Debug information.
Definition Log.h:676
Definition Range.h:52
void append(ReferenceMap &rMap, bool replace=true)
Adopts the references of r. If replace==false, only new entries are appended.
Definition ReferenceMap.h:320
Linear scaling and physical range for image intensities.
Definition ValueScaling.h:64
const Range< double > & getPhysicalRange() const
Returns a typical or supported range for physical values.
Definition ValueScaling.h:221
bool isPhysical() const
Returns true, physical intensity range has been set.
Definition ValueScaling.h:281
double fwd(double x) const
Forward scaling: given encoded value x, returns corresponding value (possibly physically meaningful).
Definition ValueScaling.h:295
double inv(double y) const
Inverse scaling: given physically meaningful value y, returns the corresponding code value.
Definition ValueScaling.h:301
Image with static geometry.
Definition ImageChannel.h:60
Definition CoordinateHandler.h:77
static const coord_overflow_t IRREVERSIBLE
Equal move in inverse direction would not result original position.
Definition CoordinateHandler.h:90
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:205
Definition DistanceModel.h:76
Definition DistanceModelExponential.h:55
Base class for linear and exponential distances in rectangular pixel images.
Definition DistanceModel.h:123
Definition DistanceTransformOp.h:513
DistanceTransformExponentialOp(dist_t horz=10.0, dist_t vert=DistanceModel::nan_f, DistanceModel::topol_t topology=DistanceModel::KNIGHT)
Constructor with default dimensions.
Definition DistanceTransformOp.h:521
Computes inverse linear distance to bright points.
Definition DistanceTransformOp.h:482
Definition DistanceTransformOp.h:58
void traverseUpLeft(const Channel &src, Channel &dst) const
Definition DistanceTransformOp.h:382
virtual void getDstConf(const ImageConf &src, ImageConf &dst) const
Ensures dst the same geometry and type with src.
Definition DistanceTransformOp.h:95
virtual void traverseChannel(const Channel &src, Channel &dst) const
Apply to single channel.
Definition DistanceTransformOp.h:271
virtual void initializeParameters(const ImageFrame &src, const ImageFrame &dst) const
Sets internal parameters.
Definition DistanceTransformOp.h:193
virtual void traverseChannel(const Channel &src, const Channel &srcAlpha, Channel &dst, Channel &dstAlpha) const
Apply to single channel with alpha.
Definition DistanceTransformOp.h:85
double requestPhysicalMax(double defaultMax=static_cast< double >(std::numeric_limits< short int >::max())) const
Returns the actual or guessed maximum physical value,.
Definition ImageConf.h:293
T getTypeMax() const
Returns the maximum value supported by the current storage type.
Definition ImageConf.h:281
void setPhysicalRange(const Range< double > &range, bool rescale=false)
Sets channel specific scaling instead of shared (image-level) scaling.
Definition ImageConf.h:229
Struct for image (excluding data)
Definition ImageConf.h:333
CoordinatePolicy coordinatePolicy
Rules to handle under- and overflows of horizontal and vertical coordinates.
Definition ImageConf.h:383
Image with static geometry.
Definition ImageFrame.h:67
void put(size_t i, T x)
Sets the intensity in location i to x. See \address.
Definition ImageFrame.h:192
T get(size_t i) const
Gets the intensity at location i. See address().
Definition ImageFrame.h:254
const CoordinatePolicy & getCoordinatePolicy() const
Coord policy.
Definition ImageLike.h:167
const std::type_info & getType() const
Get the storage type.
Definition ImageLike.h:100
virtual int srcAlpha() const
Tell if alpha channel(s) is required in input.
Definition ImageMod.h:66
Base class for image processing functions.
Definition ImageOp.h:49
ImageOp(const std::string &name=__FUNCTION__, const std::string &description="")
Definition ImageOp.h:162
void traverseChannelsSeparately(const ImageTray< const Channel > &src, ImageTray< Channel > &dst) const
Process each (src,dst) channel pair independently. Raise error if their counts differ.
Definition ImageOp.cpp:340
Container applicable for Channels and Images, with alpha support.
Definition ImageTray.h:267
Definition DataSelector.cpp:1277
Definition Point.h:48