Shinya Kitaoka e5734a
#include <memory></memory>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "tropcm.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzCore includes
Toshihiro Shimizu 890ddd
#include "traster.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// STD includes
Toshihiro Shimizu 890ddd
#include <limits></limits>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
//#define UNIT_TEST                                             // Enables unit
Shinya Kitaoka 38fd86
// testing at program startup
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//************************************************************************
Toshihiro Shimizu 890ddd
//    Rationale
Toshihiro Shimizu 890ddd
//************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*!
Toshihiro Shimizu 890ddd
  \file                   tdistancetransform.cpp
Toshihiro Shimizu 890ddd
  \brief    This file implements an O(rows * cols) 2-dimensional distance
Toshihiro Shimizu 890ddd
            transform algorithm with customizable action on squared pixel
Toshihiro Shimizu 890ddd
            distance from the closest pixel.
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//************************************************************************
Toshihiro Shimizu 890ddd
//    Local namespace  stuff
Toshihiro Shimizu 890ddd
//************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*!
Toshihiro Shimizu 890ddd
  \brief    Given 2 parabolas with (minimal) height at centers \p a and \p b
Toshihiro Shimizu 890ddd
            and centers separated by distance \p d, returns the min between
Shinya Kitaoka 120a6e
            \p d and the value \p x satisfying <tt>a + x^2 == b + (x -</tt>
Shinya Kitaoka 120a6e
  d)^2.
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
unsigned int takeoverDist(unsigned int a, unsigned int b, unsigned int d) {
Shinya Kitaoka 120a6e
  // The actual formula is: x = (h^2 + b - a) / 2h. It simplifies as follows
Shinya Kitaoka 120a6e
  // using integers only.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // NOTE: It can be proven that with integer division, x/ab == (x/a)/b.
Shinya Kitaoka 120a6e
  return (b < a) ? d : std::max((d + (b - a) / d + 1) / 2,
Shinya Kitaoka 120a6e
                                d);  // Note the +1 to get the ceil
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename isinsidefunc="" pix,="" typename=""></typename>
Shinya Kitaoka 120a6e
void initializeDT(const TRasterPT<pix> &ras,</pix>
Shinya Kitaoka 120a6e
                  const TRasterPT<unsigned int=""> &dtRas, IsInsideFunc isInside) {</unsigned>
Shinya Kitaoka 120a6e
  assert(ras->getLx() == dtRas->getLx() && ras->getLy() == dtRas->getLy());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  static const unsigned int uiMax =  // Due to the above takeoverDist, for
Shinya Kitaoka 120a6e
      (std::numeric_limits<unsigned int="">::max)() - 2;  // d == 1</unsigned>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int lx = ras->getLx(), ly = ras->getLy();
Shinya Kitaoka 120a6e
  for (int y = 0; y != ly; ++y) {
Shinya Kitaoka 120a6e
    Pix *pix = ras->pixels(y), *rowEnd = pix + lx;
Shinya Kitaoka 120a6e
    unsigned int *dt = dtRas->pixels(y);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (; pix != rowEnd; ++pix, ++dt) {
Shinya Kitaoka 120a6e
      assert(*dt == 0u);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (!isInside(*pix)) *dt = uiMax;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename outfunc="" pix,="" typename=""></typename>
Shinya Kitaoka 120a6e
void expand(int lineLength, int linesCount, Pix *buf, int incrPix, int incrLine,
Shinya Kitaoka 120a6e
            unsigned int *dtBuf, int dtIncrPix, int dtIncrLine,
Shinya Kitaoka 120a6e
            OutFunc outFunc) {
Shinya Kitaoka 120a6e
  struct locals {
Shinya Kitaoka 120a6e
    static void copyLine(unsigned int *dst, unsigned int *src,
Shinya Kitaoka 120a6e
                         unsigned int *srcEnd, int srcStride) {
Shinya Kitaoka 120a6e
      for (; src != srcEnd; src += srcStride, ++dst) *dst = *src;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    static void buildRange(unsigned int *dtRef, unsigned int *dtLineEnd,
Shinya Kitaoka 120a6e
                           unsigned int *&dtEnd, unsigned int *&dtNewRef) {
Shinya Kitaoka 120a6e
      unsigned int d    = 1,
Shinya Kitaoka 120a6e
                   dNew = 0,  // dNew at 0 to provide a consistent dtNewRef
Shinya Kitaoka 120a6e
          dMax = (std::numeric_limits<unsigned int="">::max)();  // at the end -</unsigned>
Shinya Kitaoka 120a6e
                                                              // should not
Shinya Kitaoka 120a6e
                                                              // matter though
Shinya Kitaoka 120a6e
      unsigned int *dt = dtRef + 1;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      for (; d <= dMax && dt != dtLineEnd;
Shinya Kitaoka 120a6e
           ++d, ++dt)  // Pick larger intervals if possible
Shinya Kitaoka 120a6e
      {
Shinya Kitaoka 120a6e
        unsigned int newDMax = ::takeoverDist(*dtRef, *dt, d);  //
Shinya Kitaoka 120a6e
        if (newDMax <= dMax) {
Shinya Kitaoka 120a6e
          dNew = d;
Shinya Kitaoka 120a6e
          dMax = newDMax;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      dtEnd =
Shinya Kitaoka 120a6e
          dtRef + std::min(d, dMax);  // Could end the line before (dMax < d)
Shinya Kitaoka 120a6e
      dtNewRef = dtRef + dNew;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  };  // locals
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Allocate a buffer equivalent to a dt line. It will store the original
Shinya Kitaoka 120a6e
  // dt values. Final dt values will be written directly on the dt raster.
Shinya Kitaoka 120a6e
  // This is necessary since read and write intervals overlap.
Shinya Kitaoka 120a6e
  std::unique_ptr<unsigned[]> dtOriginalLine(new unsigned[lineLength]);</unsigned[]>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  unsigned int *odtLineStart = dtOriginalLine.get(),
Shinya Kitaoka 120a6e
               *odtLineEnd   = odtLineStart + lineLength;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Process each line
Shinya Kitaoka 120a6e
  for (int l = 0; l != linesCount; ++l) {
Shinya Kitaoka 120a6e
    unsigned int *dtLineStart =
Shinya Kitaoka 120a6e
                     dtBuf +
Shinya Kitaoka 120a6e
                     dtIncrLine *
Shinya Kitaoka 120a6e
                         l,  // Using dtBuf to track colors from now on,
Shinya Kitaoka 120a6e
        *dtLineEnd =
Shinya Kitaoka 120a6e
            dtLineStart +
Shinya Kitaoka 120a6e
            dtIncrPix * lineLength,  // it already embeds colorFunc's output due
Shinya Kitaoka 120a6e
            *dt         = dtLineStart,  // to the way it was initialized.
Shinya Kitaoka 120a6e
                *odtRef = odtLineStart;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    Pix *lineStart = buf + incrLine * l, *pix = lineStart;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Make a copy of the original dt values
Shinya Kitaoka 120a6e
    locals::copyLine(dtOriginalLine.get(), dtLineStart, dtLineEnd, dtIncrPix);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Expand a colored pixel along the line
Shinya Kitaoka 120a6e
    while (dt != dtLineEnd) {
Shinya Kitaoka 120a6e
      // The line is subdivided in consecutive ranges associated to the same
Shinya Kitaoka 120a6e
      // half-parabola - process one
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Build a half-parabola range
Shinya Kitaoka 120a6e
      unsigned int *dtEnd, *odtNewRef;
Shinya Kitaoka 120a6e
      locals::buildRange(odtRef, odtLineEnd, dtEnd, odtNewRef);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      assert(odtLineStart <= odtNewRef && odtNewRef <= odtLineEnd);
Shinya Kitaoka 120a6e
      assert(odtLineStart <= dtEnd && dtEnd <= odtLineEnd);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      dtEnd =
Shinya Kitaoka 120a6e
          dtLineStart +
Shinya Kitaoka 120a6e
          dtIncrPix *
Shinya Kitaoka 120a6e
              (dtEnd - odtLineStart);  // Convert dtEnd to the dt raster buffer
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Process the range
Shinya Kitaoka 120a6e
      Pix *ref = lineStart + incrPix * (odtRef - odtLineStart);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      unsigned int d = (pix - ref) / incrPix;
Shinya Kitaoka 120a6e
      for (; dt != dtEnd; ++d, dt += dtIncrPix, pix += incrPix)
Shinya Kitaoka 120a6e
        outFunc(*pix, *ref, *dt = *odtRef + sq(d));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      odtRef = odtNewRef;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*!
Toshihiro Shimizu 890ddd
  \brief    Performs an O(rows * cols) distance transform on the specified
Toshihiro Shimizu 890ddd
            raster image.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  \details  The algorithm relies on the separability of the 2D DT into 2
Toshihiro Shimizu 890ddd
            passes (by rows and columns) of 1-dimensional DTs.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
            The 1D DT sums a pre-existing (from the previous DT step if any)
Toshihiro Shimizu 890ddd
            DT result with the one currently calculated.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  \warning  Templace parameter OutFunc is supposed to satisfy \a transitivity
Toshihiro Shimizu 890ddd
            upon comparison of its output - so, if \p b is the output of \p a,
Toshihiro Shimizu 890ddd
            and \p c is the output of \p b, then \p c is the same as the output
Toshihiro Shimizu 890ddd
            of \p a.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  \todo     Accept a different output raster - but preserve the case where
Toshihiro Shimizu 890ddd
            (srcRas == dstRas).
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename isinsidefunc,="" outfunc="" pix,="" typename=""></typename>
Shinya Kitaoka 120a6e
void distanceTransform(const TRasterPT<pix> &ras, IsInsideFunc isInside,</pix>
Shinya Kitaoka 120a6e
                       OutFunc outFunc) {
Shinya Kitaoka 120a6e
  int lx = ras->getLx(), ly = ras->getLy();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Allocate a suitable temporary raster holding the (squared) distance
Shinya Kitaoka 120a6e
  // transform
Shinya Kitaoka 120a6e
  // built from the specified color function
Shinya Kitaoka 120a6e
  TRasterPT<unsigned int=""> dtRas(</unsigned>
Shinya Kitaoka 120a6e
      lx, ly);  // Summed squared distances will be limited to
Shinya Kitaoka 120a6e
                // 2 billions. This is generally suitable.
Shinya Kitaoka 120a6e
  ::initializeDT(ras, dtRas,
Shinya Kitaoka 120a6e
                 isInside);  // The raster is binarized directly into the
Shinya Kitaoka 120a6e
                             // auxiliary dtRas. Pixels in the set to expand
Shinya Kitaoka 120a6e
  // will have value 0, the others a suitable high value.
Shinya Kitaoka 120a6e
  expand(lx, ly, ras->pixels(0), 1, ras->getWrap(), dtRas->pixels(0), 1,
Shinya Kitaoka 120a6e
         dtRas->getWrap(), outFunc);
Shinya Kitaoka 120a6e
  expand(lx, ly, ras->pixels(0) + lx - 1, -1, ras->getWrap(),
Shinya Kitaoka 120a6e
         dtRas->pixels(0) + lx - 1, -1, dtRas->getWrap(), outFunc);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  expand(ly, lx, ras->pixels(0), ras->getWrap(), 1, dtRas->pixels(0),
Shinya Kitaoka 120a6e
         dtRas->getWrap(), 1, outFunc);
Shinya Kitaoka 120a6e
  expand(ly, lx, ras->pixels(ly - 1), -ras->getWrap(), 1, dtRas->pixels(ly - 1),
Shinya Kitaoka 120a6e
         -dtRas->getWrap(), 1, outFunc);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//************************************************************************
Toshihiro Shimizu 890ddd
//    Local Functors
Toshihiro Shimizu 890ddd
//************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*
Toshihiro Shimizu 890ddd
  Using functors here just to be absolutely sure that calls are not
Toshihiro Shimizu 890ddd
  callbacks.
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct SomePaint {
Shinya Kitaoka 120a6e
  inline bool operator()(const TPixelCM32 &pix) const {
Shinya Kitaoka 120a6e
    return (pix.getTone() != 0) || (pix.getPaint() != 0);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct CopyPaint {
Shinya Kitaoka 120a6e
  inline void operator()(TPixelCM32 &out, const TPixelCM32 &in,
Shinya Kitaoka 120a6e
                         unsigned int) const {
Shinya Kitaoka 120a6e
    out.setPaint(in.getPaint());
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//************************************************************************
Toshihiro Shimizu 890ddd
//    API functions
Toshihiro Shimizu 890ddd
//************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void TRop::expandPaint(const TRasterCM32P &rasCM) {
Shinya Kitaoka 120a6e
  distanceTransform(rasCM, SomePaint(), CopyPaint());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//************************************************************************
Toshihiro Shimizu 890ddd
//    Unit testing
Toshihiro Shimizu 890ddd
//************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#if defined UNIT_TEST && !defined NDEBUG
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void assertEqualBufs(const TRasterT<unsigned int=""> &a,</unsigned>
Shinya Kitaoka 120a6e
                     const TRasterT<unsigned int=""> &b) {</unsigned>
Shinya Kitaoka 120a6e
  for (int y = 0; y != a.getLy(); ++y) {
Shinya Kitaoka 120a6e
    for (int x = 0; x != a.getLx(); ++x)
Shinya Kitaoka 120a6e
      assert(a.pixels(y)[x] == b.pixels(y)[x]);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct Selector {
Shinya Kitaoka 120a6e
  inline bool operator()(unsigned int val) const { return val; }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct OutputDT {
Shinya Kitaoka 120a6e
  inline void operator()(unsigned int &out, const unsigned int &in,
Shinya Kitaoka 120a6e
                         unsigned int d2) const {
Shinya Kitaoka 120a6e
    out = d2;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct DTTest {
Shinya Kitaoka 120a6e
  DTTest() {
Shinya Kitaoka 120a6e
    unsigned int imgBuf[] = {
Shinya Kitaoka 120a6e
        0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1,
Shinya Kitaoka 120a6e
        1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Shinya Kitaoka 120a6e
    };
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    unsigned int dtBuf[] = {
Shinya Kitaoka 120a6e
        4, 1, 0, 1, 4, 5, 2, 1, 0, 1, 1, 2, 1, 0, 0,
Shinya Kitaoka 120a6e
        0, 0, 1, 1, 0, 1, 1, 1, 2, 2, 1, 2, 4, 4, 5,
Shinya Kitaoka 120a6e
    };
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TRasterPT<unsigned int=""> imgRas(6, 5, 6, imgBuf, false),</unsigned>
Shinya Kitaoka 120a6e
        dtRas(6, 5, 6, dtBuf, false);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    distanceTransform(imgRas, Selector(), OutputDT());
Shinya Kitaoka 120a6e
    assertEqualBufs(*imgRas, *dtRas);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
} dtTest;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
#endif  // UNIT_TEST && !NDEBUG