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
Toshihiro Shimizu 890ddd
//#define UNIT_TEST                                             // Enables unit 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
Toshihiro Shimizu 890ddd
namespace
Toshihiro Shimizu 890ddd
{
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
Toshihiro Shimizu 890ddd
            \p d and the value \p x satisfying <tt>a + x^2 == b + (x - d)^2</tt>.
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
unsigned int takeoverDist(unsigned int a, unsigned int b, unsigned int d)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	// The actual formula is: x = (h^2 + b - a) / 2h. It simplifies as follows
Toshihiro Shimizu 890ddd
	// using integers only.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// NOTE: It can be proven that with integer division, x/ab == (x/a)/b.
Shinya Kitaoka 12c444
	return (b < a) ? d : std::max((d + (b - a) / d + 1) / 2, 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>
Toshihiro Shimizu 890ddd
void initializeDT(const TRasterPT<pix> &ras, const TRasterPT<unsigned int=""> &dtRas,</unsigned></pix>
Toshihiro Shimizu 890ddd
				  IsInsideFunc isInside)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	assert(ras->getLx() == dtRas->getLx() && ras->getLy() == dtRas->getLy());
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	static const unsigned int uiMax =					// Due to the above takeoverDist, for
Toshihiro Shimizu 890ddd
		(std::numeric_limits<unsigned int="">::max)() - 2; // d == 1</unsigned>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	int lx = ras->getLx(), ly = ras->getLy();
Toshihiro Shimizu 890ddd
	for (int y = 0; y != ly; ++y) {
Toshihiro Shimizu 890ddd
		Pix *pix = ras->pixels(y), *rowEnd = pix + lx;
Toshihiro Shimizu 890ddd
		unsigned int *dt = dtRas->pixels(y);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		for (; pix != rowEnd; ++pix, ++dt) {
Toshihiro Shimizu 890ddd
			assert(*dt == 0u);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			if (!isInside(*pix))
Toshihiro Shimizu 890ddd
				*dt = uiMax;
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename outfunc="" pix,="" typename=""></typename>
Toshihiro Shimizu 890ddd
void expand(int lineLength, int linesCount,
Toshihiro Shimizu 890ddd
			Pix *buf, int incrPix,
Toshihiro Shimizu 890ddd
			int incrLine,
Toshihiro Shimizu 890ddd
			unsigned int *dtBuf, int dtIncrPix,
Toshihiro Shimizu 890ddd
			int dtIncrLine,
Toshihiro Shimizu 890ddd
			OutFunc outFunc)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	struct locals {
Toshihiro Shimizu 890ddd
		static void copyLine(unsigned int *dst, unsigned int *src, unsigned int *srcEnd,
Toshihiro Shimizu 890ddd
							 int srcStride)
Toshihiro Shimizu 890ddd
		{
Toshihiro Shimizu 890ddd
			for (; src != srcEnd; src += srcStride, ++dst)
Toshihiro Shimizu 890ddd
				*dst = *src;
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		static void buildRange(unsigned int *dtRef, unsigned int *dtLineEnd,
Toshihiro Shimizu 890ddd
							   unsigned int *&dtEnd, unsigned int *&dtNewRef)
Toshihiro Shimizu 890ddd
		{
Toshihiro Shimizu 890ddd
			unsigned int d = 1, dNew = 0,						   // dNew at 0 to provide a consistent dtNewRef
Toshihiro Shimizu 890ddd
				dMax = (std::numeric_limits<unsigned int="">::max)(); // at the end - should not matter though</unsigned>
Toshihiro Shimizu 890ddd
			unsigned int *dt = dtRef + 1;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			for (; d <= dMax && dt != dtLineEnd; ++d, ++dt) // Pick larger intervals if possible
Toshihiro Shimizu 890ddd
			{
Toshihiro Shimizu 890ddd
				unsigned int newDMax = ::takeoverDist(*dtRef, *dt, d); //
Toshihiro Shimizu 890ddd
				if (newDMax <= dMax) {
Toshihiro Shimizu 890ddd
					dNew = d;
Toshihiro Shimizu 890ddd
					dMax = newDMax;
Toshihiro Shimizu 890ddd
				}
Toshihiro Shimizu 890ddd
			}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 12c444
			dtEnd = dtRef + std::min(d, dMax); // Could end the line before (dMax < d)
Toshihiro Shimizu 890ddd
			dtNewRef = dtRef + dNew;
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}; // locals
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Allocate a buffer equivalent to a dt line. It will store the original
Toshihiro Shimizu 890ddd
	// dt values. Final dt values will be written directly on the dt raster.
Toshihiro Shimizu 890ddd
	// This is necessary since read and write intervals overlap.
Shinya Kitaoka e5734a
	std::unique_ptr<unsigned[]> dtOriginalLine(new unsigned[lineLength]);</unsigned[]>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	unsigned int *odtLineStart = dtOriginalLine.get(),
Toshihiro Shimizu 890ddd
				 *odtLineEnd = odtLineStart + lineLength;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Process each line
Toshihiro Shimizu 890ddd
	for (int l = 0; l != linesCount; ++l) {
Toshihiro Shimizu 890ddd
		unsigned int *dtLineStart = dtBuf + dtIncrLine * l,	// Using dtBuf to track colors from now on,
Toshihiro Shimizu 890ddd
			*dtLineEnd = dtLineStart + dtIncrPix * lineLength, // it already embeds colorFunc's output due
Toshihiro Shimizu 890ddd
				*dt = dtLineStart,							   // to the way it was initialized.
Toshihiro Shimizu 890ddd
					*odtRef = odtLineStart;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		Pix *lineStart = buf + incrLine * l,
Toshihiro Shimizu 890ddd
			*pix = lineStart;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// Make a copy of the original dt values
Toshihiro Shimizu 890ddd
		locals::copyLine(dtOriginalLine.get(), dtLineStart, dtLineEnd, dtIncrPix);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		// Expand a colored pixel along the line
Toshihiro Shimizu 890ddd
		while (dt != dtLineEnd) {
Toshihiro Shimizu 890ddd
			// The line is subdivided in consecutive ranges associated to the same
Toshihiro Shimizu 890ddd
			// half-parabola - process one
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			// Build a half-parabola range
Toshihiro Shimizu 890ddd
			unsigned int *dtEnd, *odtNewRef;
Toshihiro Shimizu 890ddd
			locals::buildRange(odtRef, odtLineEnd, dtEnd, odtNewRef);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			assert(odtLineStart <= odtNewRef && odtNewRef <= odtLineEnd);
Toshihiro Shimizu 890ddd
			assert(odtLineStart <= dtEnd && dtEnd <= odtLineEnd);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			dtEnd = dtLineStart + dtIncrPix * (dtEnd - odtLineStart); // Convert dtEnd to the dt raster buffer
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			// Process the range
Toshihiro Shimizu 890ddd
			Pix *ref = lineStart + incrPix * (odtRef - odtLineStart);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			unsigned int d = (pix - ref) / incrPix;
Toshihiro Shimizu 890ddd
			for (; dt != dtEnd; ++d, dt += dtIncrPix, pix += incrPix)
Toshihiro Shimizu 890ddd
				outFunc(*pix, *ref, *dt = *odtRef + sq(d));
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
			odtRef = odtNewRef;
Toshihiro Shimizu 890ddd
		}
Toshihiro Shimizu 890ddd
	}
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>
Toshihiro Shimizu 890ddd
void distanceTransform(const TRasterPT<pix> &ras, IsInsideFunc isInside, OutFunc outFunc)</pix>
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	int lx = ras->getLx(), ly = ras->getLy();
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	// Allocate a suitable temporary raster holding the (squared) distance transform
Toshihiro Shimizu 890ddd
	// built from the specified color function
Toshihiro Shimizu 890ddd
	TRasterPT<unsigned int=""> dtRas(lx, ly); // Summed squared distances will be limited to</unsigned>
Toshihiro Shimizu 890ddd
										   // 2 billions. This is generally suitable.
Toshihiro Shimizu 890ddd
	::initializeDT(ras, dtRas, isInside);  // The raster is binarized directly into the
Toshihiro Shimizu 890ddd
										   // auxiliary dtRas. Pixels in the set to expand
Toshihiro Shimizu 890ddd
										   // will have value 0, the others a suitable high value.
Toshihiro Shimizu 890ddd
	expand(lx, ly, ras->pixels(0), 1, ras->getWrap(),
Toshihiro Shimizu 890ddd
		   dtRas->pixels(0), 1, dtRas->getWrap(), outFunc);
Toshihiro Shimizu 890ddd
	expand(lx, ly, ras->pixels(0) + lx - 1, -1, ras->getWrap(),
Toshihiro Shimizu 890ddd
		   dtRas->pixels(0) + lx - 1, -1, dtRas->getWrap(), outFunc);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
	expand(ly, lx, ras->pixels(0), ras->getWrap(), 1,
Toshihiro Shimizu 890ddd
		   dtRas->pixels(0), dtRas->getWrap(), 1, outFunc);
Toshihiro Shimizu 890ddd
	expand(ly, lx, ras->pixels(ly - 1), -ras->getWrap(), 1,
Toshihiro Shimizu 890ddd
		   dtRas->pixels(ly - 1), -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
Toshihiro Shimizu 890ddd
namespace
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct SomePaint {
Toshihiro Shimizu 890ddd
	inline bool operator()(const TPixelCM32 &pix) const
Toshihiro Shimizu 890ddd
	{
Toshihiro Shimizu 890ddd
		return (pix.getTone() != 0) || (pix.getPaint() != 0);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct CopyPaint {
Toshihiro Shimizu 890ddd
	inline void operator()(TPixelCM32 &out, const TPixelCM32 &in, unsigned int) const
Toshihiro Shimizu 890ddd
	{
Toshihiro Shimizu 890ddd
		out.setPaint(in.getPaint());
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//************************************************************************
Toshihiro Shimizu 890ddd
//    API functions
Toshihiro Shimizu 890ddd
//************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void TRop::expandPaint(const TRasterCM32P &rasCM)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	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
Toshihiro Shimizu 890ddd
namespace
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void assertEqualBufs(const TRasterT<unsigned int=""> &a, const TRasterT<unsigned int=""> &b)</unsigned></unsigned>
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
	for (int y = 0; y != a.getLy(); ++y) {
Toshihiro Shimizu 890ddd
		for (int x = 0; x != a.getLx(); ++x)
Toshihiro Shimizu 890ddd
			assert(a.pixels(y)[x] == b.pixels(y)[x]);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct Selector {
Toshihiro Shimizu 890ddd
	inline bool operator()(unsigned int val) const
Toshihiro Shimizu 890ddd
	{
Toshihiro Shimizu 890ddd
		return val;
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct OutputDT {
Toshihiro Shimizu 890ddd
	inline void operator()(unsigned int &out, const unsigned int &in, unsigned int d2) const
Toshihiro Shimizu 890ddd
	{
Toshihiro Shimizu 890ddd
		out = d2;
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct DTTest {
Toshihiro Shimizu 890ddd
	DTTest()
Toshihiro Shimizu 890ddd
	{
Toshihiro Shimizu 890ddd
		unsigned int imgBuf[] = {
Toshihiro Shimizu 890ddd
			0, 0, 1, 0, 0, 0,
Toshihiro Shimizu 890ddd
			0, 0, 1, 0, 0, 0,
Toshihiro Shimizu 890ddd
			0, 1, 1, 1, 1, 0,
Toshihiro Shimizu 890ddd
			0, 1, 0, 0, 0, 0,
Toshihiro Shimizu 890ddd
			0, 0, 0, 0, 0, 0,
Toshihiro Shimizu 890ddd
		};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		unsigned int dtBuf[] = {
Toshihiro Shimizu 890ddd
			4, 1, 0, 1, 4, 5,
Toshihiro Shimizu 890ddd
			2, 1, 0, 1, 1, 2,
Toshihiro Shimizu 890ddd
			1, 0, 0, 0, 0, 1,
Toshihiro Shimizu 890ddd
			1, 0, 1, 1, 1, 2,
Toshihiro Shimizu 890ddd
			2, 1, 2, 4, 4, 5,
Toshihiro Shimizu 890ddd
		};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		TRasterPT<unsigned int=""> imgRas(6, 5, 6, imgBuf, false),</unsigned>
Toshihiro Shimizu 890ddd
			dtRas(6, 5, 6, dtBuf, false);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
		distanceTransform(imgRas, Selector(), OutputDT());
Toshihiro Shimizu 890ddd
		assertEqualBufs(*imgRas, *dtRas);
Toshihiro Shimizu 890ddd
	}
Toshihiro Shimizu 890ddd
} dtTest;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
} // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#endif // UNIT_TEST && !NDEBUG