SMIL  1.1
DMorphoEuclidean.hpp
1 /* __HEAD__
2  * Copyright (c) 2011-2016, Matthieu FAESSEL and ARMINES
3  * Copyright (c) 2017-2023, Centre de Morphologie Mathematique
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * * Neither the name of Matthieu FAESSEL, or ARMINES nor the
15  * names of its contributors may be used to endorse or promote products
16  * derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28  * THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * Description :
31  * This file evaluates the Euclidean Distance on a binary image
32  *
33  * History :
34  * - 04/03/2022 - by Jose-Marcio
35  * Euclidean Distance - based on Beatriz Marcotegui implementation
36  *
37  * __HEAD__ - Stop here !
38  */
39 
40 #ifndef _D_MORPHOEUCLIDEAN_HPP
41 #define _D_MORPHOEUCLIDEAN_HPP
42 
43 namespace smil
44 {
46  {
47  public:
49  {
50  }
51 
52  template <class T1, class T2>
53  RES_T _exec(const Image<T1> &imIn, Image<T2> &imOut,
54  const StrElt &se = DEFAULT_SE)
55  {
56  return _distance(imIn, imOut, se);
57  }
58 
59  private:
60  double sqr(size_t x)
61  {
62  return x * x;
63  }
64 
65  double sqModule(IntPoint &p1, IntPoint &p2)
66  {
67  return sqr(p1.x - p2.x) + sqr(p1.y - p2.y) + sqr(p1.z - p2.z);
68  }
69 
70  template <class T1, class T2>
71  RES_T _distance(const Image<T1> &imIn, Image<T2> &imOut,
72  const StrElt &se = DEFAULT_SE)
73  {
74  ASSERT_ALLOCATED(&imIn, &imOut);
75  ASSERT_SAME_SIZE(&imIn, &imOut);
76 
77  ImageFreezer freeze(imOut);
78 
79  typedef Image<T1> imageInType;
80  typedef typename imageInType::lineType lineInType;
81  typedef Image<T2> imageOutType;
82  typedef typename imageOutType::lineType lineOutType;
83 
84  lineInType pixelsIn = imIn.getPixels();
85  lineOutType pixelsOut = imOut.getPixels();
86 
88 
89  off_t pixelCount = imIn.getPixelCount();
90  vector<off_t> pixelsOffset(pixelCount, 0);
91 
92  StrElt seLoc = se.noCenter();
93 
94  off_t maxOffset = pixelCount;
95 
96  // INITIALIZE
97  // Pixels != 0 but with a 0 neighbor (contour pixels) are:
98  // - pushed in the FAH
99  // - out[p] = 1
100  // - pixelOffset[p]=bckg neighbor
101 
102  // pixelOffset: contains the closest bck pixel for each object pixel (for
103  // pixels with computed distance value)
104  // maxOffset+1 for pixels not processed yet (or background pixels)
105  hq.initialize(imIn);
106  for (auto i = 0; i < pixelCount; i++) {
107  pixelsOffset[i] = maxOffset + 1;
108  if (pixelsIn[i] == T1(0))
109  continue;
110 
111  IntPoint pt = imIn.getPointFromOffset(i);
112 
113  bool oddLine = seLoc.odd && (pt.y % 2 != 0);
114 
115  for (auto it = seLoc.points.begin(); it != seLoc.points.end(); it++) {
116  IntPoint ptSE = *it;
117  IntPoint ptNb = pt + ptSE;
118  off_t ofNb;
119 
120  if (oddLine && (ptNb.y + 1) % 2 != 0)
121  ptNb.x += 1;
122 
123  if (imIn.isPointInImage(ptNb))
124  ofNb = imIn.getOffsetFromPoint(ptNb);
125  else
126  continue;
127 
128  if (pixelsIn[ofNb] == 0) {
129  hq.push(T2(1), i);
130  pixelsOffset[i] = ofNb;
131  pixelsOut[i] = 1;
132  break;
133  }
134  }
135  } // for each pixel
136 
137  //#####################################
138 
139  // Process hierarchical queue. Get a pixel(p0), find ngb without
140  // computed distance(p1), p2 is a ngb of p1. If p2 knows its
141  // distance, find p3 (the closest bckg pixel to p2) and compute
142  // distance (p1,p3). Keep the smallest distance to p1 and assign
143  // it. The origin of p1 is set, pixelsOffset[p1] is set to oo
144  // (the p3 leading to the smallest distance).
145 
146  T2 T2_maxVal = ImDtTypes<T2>::max();
147 
148  while (!hq.isEmpty()) {
149  off_t pCurr = hq.pop();
150  if (pixelsOffset[pCurr] > maxOffset) {
151  std::cout << "BAD PLACE, BAD TIME." << int(pCurr)
152  << "goes out without pixelFrom\n";
153  continue;
154  }
155 
156  IntPoint ptCurr = imIn.getPointFromOffset(pCurr);
157 
158  bool oddLine = seLoc.odd && (ptCurr.y % 2 != 0);
159  for (auto it1 = seLoc.points.begin(); it1 != seLoc.points.end();
160  it1++) {
161  IntPoint ptSe1 = *it1;
162  IntPoint pt1 = ptCurr + ptSe1;
163 
164  if (oddLine && ((pt1.y + 1) % 2) != 0)
165  pt1.x += 1;
166 
167  if (!imIn.isPointInImage(pt1))
168  continue;
169 
170  off_t p1 = imIn.getOffsetFromPoint(pt1);
171 
172  // non NULL input pixel, but without output value
173  // (PixelsOffset == maxOffset+1)
174  if ((pixelsIn[p1] == 0) || (pixelsOffset[p1] <= maxOffset))
175  continue;
176 
177  int current_dist = ImDtTypes<int>::max(); // INFINITE!!!
178 
179  off_t oo;
180  for (auto it2 = seLoc.points.begin(); it2 != seLoc.points.end();
181  it2++) {
182  IntPoint ptSe2 = *it2;
183  IntPoint pt2 = pt1 + ptSe2;
184 
185  bool oddLine2 = seLoc.odd && (pt1.y % 2 != 0);
186  if (oddLine2 && (((pt2.y + 1) % 2) != 0))
187  pt2.x += 1;
188 
189  if (!imIn.isPointInImage(pt2))
190  continue;
191 
192  off_t p2 = imIn.getOffsetFromPoint(pt2);
193 
194  if (pixelsOffset[p2] <= maxOffset) {
195  /* voisin de distance calculee */
196  off_t p3 = pixelsOffset[p2];
197  IntPoint pt3 = imIn.getPointFromOffset(p3);
198 
199  int wd = sqModule(pt1, pt3);
200  if (wd < current_dist) {
201  current_dist = wd;
202  oo = p2;
203  }
204  } // if ngb knows its distance
205  } // for ngb
206  T2 pr1 = T2(std::sqrt(current_dist));
207 
208  if (pr1 > T2_maxVal)
209  pr1 = T2_maxVal;
210 
211  hq.push(pr1, p1);
212 
213  pixelsOut[p1] = pr1;
214  pixelsOffset[p1] = pixelsOffset[oo];
215  } // for each ngb of p
216  } // while ! EMPTY
217 
218  return RES_OK;
219  } // END euclidian_distance
220  };
221 
222  template <class T1, class T2>
223  RES_T distanceEuclidean(const Image<T1> &imIn, Image<T2> &imOut,
224  const StrElt &se)
225  {
226  EuclideanFunctor func;
227 
228  return func._exec(imIn, imOut, se);
229  }
230 
231 } // namespace smil
232 
233 #endif // _D_MORPHOEUCLIDEAN_HPP
size_t getPixelCount() const
Get the number of pixels.
Definition: DBaseImage.h:160
size_t getOffsetFromPoint(IntPoint &p) const
Get an offset for given x,y(,z) coordinates.
Definition: DBaseImage.h:276
bool isPointInImage(const IntPoint &p) const
isPointInImage() - checks if a Point is in inside the image bounds.
Definition: DBaseImage.h:224
Definition: DMorphoEuclidean.hpp:46
Definition: DMorphoHierarQ.hpp:143
Definition: DBaseImage.h:386
lineType getPixels() const
Get the pixels as a 1D array.
Definition: DImage.hpp:105
Base structuring element.
Definition: DStructuringElement.h:70
vector< IntPoint > points
List of neighbor points.
Definition: DStructuringElement.h:203
RES_T sqrt(const Image< T1 > &imIn, Image< T2 > &imOut)
sqrt() - square root of an image
Definition: DImageArith.hpp:926
RES_T distanceEuclidean(const Image< T1 > &imIn, Image< T2 > &imOut, const StrElt &se=DEFAULT_SE)
distanceEuclidean() - Euclidean distance function.
Definition: DMorphoEuclidean.hpp:223
Definition: DTypes.hpp:88