|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// tcg includes
|
|
Toshihiro Shimizu |
890ddd |
#include "tcg/tcg_misc.h"
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
#include "trop.h"
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*! \file terodilate.cpp
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
This file contains an implementation of a greyscale (ie per-channel) erode/dilate
|
|
Toshihiro Shimizu |
890ddd |
morphological operator, following the van Herk/Gil-Werman O(row*cols) algorithm.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
An extension with circular structuring element is attempted - unfortunately I could
|
|
Toshihiro Shimizu |
890ddd |
not retrieve a copy of Miyataka's paper about that, which seemingly claimed
|
|
Toshihiro Shimizu |
890ddd |
O(rows * cols) too. The implemented algorithm is a sub-optimal O(rows*cols*radius).
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//********************************************************
|
|
Toshihiro Shimizu |
890ddd |
// Auxiliary functions
|
|
Toshihiro Shimizu |
890ddd |
//********************************************************
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
namespace
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename pix=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void copyMatte(const TRasterPT<pix> &src, const TRasterPT<typename pix::channel=""> &matte)</typename></pix>
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
typedef typename Pix::Channel Chan;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
int y, lx = src->getLx(), ly = src->getLy();
|
|
Toshihiro Shimizu |
890ddd |
for (y = 0; y != ly; ++y) {
|
|
Toshihiro Shimizu |
890ddd |
Pix *s, *sBegin = src->pixels(y), *sEnd = sBegin + lx;
|
|
Toshihiro Shimizu |
890ddd |
Chan *m, *mBegin = matte->pixels(y);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (s = sBegin, m = mBegin; s != sEnd; ++s, ++m)
|
|
Toshihiro Shimizu |
890ddd |
*m = s->m;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//--------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename pix=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void copyChannels_erode(const TRasterPT<pix> &src, const TRasterPT<typename pix::channel=""> &matte,</typename></pix>
|
|
Toshihiro Shimizu |
890ddd |
const TRasterPT<pix> &dst)</pix>
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
typedef typename Pix::Channel Chan;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Just assemble src and matte, remembering to depremultiply src pixels before
|
|
Toshihiro Shimizu |
890ddd |
// applying the new matte
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
double fac;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
int y, lx = src->getLx(), ly = src->getLy();
|
|
Toshihiro Shimizu |
890ddd |
for (y = 0; y != ly; ++y) {
|
|
Toshihiro Shimizu |
890ddd |
const Pix *s, *sBegin = src->pixels(y), *sEnd = sBegin + lx;
|
|
Toshihiro Shimizu |
890ddd |
Pix *d, *dBegin = dst->pixels(y);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
Chan *m, *mBegin = matte->pixels(y);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (s = sBegin, d = dBegin, m = mBegin; s != sEnd; ++s, ++d, ++m) {
|
|
Toshihiro Shimizu |
890ddd |
fac = double(*m) / double(s->m);
|
|
Toshihiro Shimizu |
890ddd |
d->r = fac * s->r, d->g = fac * s->g, d->b = fac * s->b, d->m = *m;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//--------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename pix=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void copyChannels_dilate(const TRasterPT<pix> &src, const TRasterPT<typename pix::channel=""> &matte,</typename></pix>
|
|
Toshihiro Shimizu |
890ddd |
const TRasterPT<pix> &dst)</pix>
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
typedef typename Pix::Channel Chan;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Trickier - since src is presumably premultiplied, increasing its pixels' alpha by direct
|
|
Toshihiro Shimizu |
890ddd |
// substitution would expose the excessive RGB discretization of pixels with a low matte value.
|
|
Toshihiro Shimizu |
890ddd |
// So, let's just put the pixels on a black background. It should do fine.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
double max = Pix::maxChannelValue;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
int y, lx = src->getLx(), ly = src->getLy();
|
|
Toshihiro Shimizu |
890ddd |
for (y = 0; y != ly; ++y) {
|
|
Toshihiro Shimizu |
890ddd |
const Pix *s, *sBegin = src->pixels(y), *sEnd = sBegin + lx;
|
|
Toshihiro Shimizu |
890ddd |
Pix *d, *dBegin = dst->pixels(y);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
const Chan *m, *mBegin = matte->pixels(y);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (s = sBegin, d = dBegin, m = mBegin; s != sEnd; ++s, ++d, ++m) {
|
|
Toshihiro Shimizu |
890ddd |
*d = *s;
|
|
Toshihiro Shimizu |
890ddd |
d->m = s->m + (1.0 - s->m / max) * *m;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
} // namespace
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//********************************************************
|
|
Toshihiro Shimizu |
890ddd |
// EroDilate algorithms
|
|
Toshihiro Shimizu |
890ddd |
//********************************************************
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
namespace
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename chan=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
struct MaxFunc {
|
|
Toshihiro Shimizu |
890ddd |
inline Chan operator()(const Chan &a, const Chan &b) { return tmax(a, b); }
|
|
Toshihiro Shimizu |
890ddd |
};
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename chan=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
struct MinFunc {
|
|
Toshihiro Shimizu |
890ddd |
inline Chan operator()(const Chan &a, const Chan &b) { return tmin(a, b); }
|
|
Toshihiro Shimizu |
890ddd |
};
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//--------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// NOTE: src and dst must be NOT OVERLAPPING (eg src != dst)
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename chan,="" func="" typename=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void erodilate_row(int len, const Chan *src, int sIncr, Chan *dst, int dIncr,
|
|
Toshihiro Shimizu |
890ddd |
int rad, double radR, Func func)
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
assert(rad >= 0);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Segment the row of specified length into wCount windows of max wSize elements
|
|
Toshihiro Shimizu |
890ddd |
int w, wSize = 2 * rad + 1, wCount = len / wSize + 1;
|
|
Toshihiro Shimizu |
890ddd |
int swIncr = wSize * sIncr, srIncr = rad * sIncr;
|
|
Toshihiro Shimizu |
890ddd |
int dwIncr = wSize * dIncr, drIncr = rad * dIncr;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
const Chan *s, *sEnd = src + len * sIncr;
|
|
Toshihiro Shimizu |
890ddd |
Chan *d, *dEnd = dst + len * dIncr;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
double one_radR = (1.0 - radR);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (w = 0; w != wCount; ++w) {
|
|
Toshihiro Shimizu |
890ddd |
Chan *dwBegin = dst + w * dwIncr, *dwEnd = tmin(dwBegin + dwIncr, dEnd);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Compute prefixes
|
|
Toshihiro Shimizu |
890ddd |
const Chan *swBegin = src + tmax(w * swIncr - srIncr - sIncr, 0),
|
|
Toshihiro Shimizu |
890ddd |
*swEnd = src + tmin(w * swIncr + srIncr + sIncr, len * sIncr);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
s = swEnd - sIncr, d = dst + ((s - src) / sIncr) * dIncr + drIncr; // d already decremented by dIncr
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
Chan val = *s, oldVal;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (s -= sIncr; (d >= dEnd) && (s >= swBegin); s -= sIncr, d -= dIncr) // s decremented here
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
assert(s >= src);
|
|
Toshihiro Shimizu |
890ddd |
assert(s < sEnd);
|
|
Toshihiro Shimizu |
890ddd |
assert((s - src) % sIncr == 0);
|
|
Toshihiro Shimizu |
890ddd |
assert(d >= dst);
|
|
Toshihiro Shimizu |
890ddd |
assert((d - dst) % dIncr == 0);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
val = func(oldVal = val, *s);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (; s >= swBegin; s -= sIncr, d -= dIncr) {
|
|
Toshihiro Shimizu |
890ddd |
assert(s >= src);
|
|
Toshihiro Shimizu |
890ddd |
assert(s < sEnd);
|
|
Toshihiro Shimizu |
890ddd |
assert((s - src) % sIncr == 0);
|
|
Toshihiro Shimizu |
890ddd |
assert(d >= dst);
|
|
Toshihiro Shimizu |
890ddd |
assert(d < dEnd);
|
|
Toshihiro Shimizu |
890ddd |
assert((d - dst) % dIncr == 0);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
val = func(oldVal = val, *s);
|
|
Toshihiro Shimizu |
890ddd |
*d = (oldVal == val) ? val : one_radR * oldVal + radR * val;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (d = tmin(d, dEnd - dIncr); d >= dwBegin; d -= dIncr) {
|
|
Toshihiro Shimizu |
890ddd |
assert(d >= dst);
|
|
Toshihiro Shimizu |
890ddd |
assert(d < dEnd);
|
|
Toshihiro Shimizu |
890ddd |
assert((d - dst) % dIncr == 0);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
val = func(oldVal = val, 0);
|
|
Toshihiro Shimizu |
890ddd |
*d = (oldVal == val) ? val : one_radR * oldVal + radR * val;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Compute suffixes
|
|
Toshihiro Shimizu |
890ddd |
swBegin = src + w * swIncr + srIncr, swEnd = tmin(swBegin + swIncr + sIncr, sEnd);
|
|
Toshihiro Shimizu |
890ddd |
if (swBegin >= swEnd)
|
|
Toshihiro Shimizu |
890ddd |
continue;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
s = swBegin, d = dwBegin;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
val = *s;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (s += sIncr; (s < swEnd); s += sIncr, d += dIncr) {
|
|
Toshihiro Shimizu |
890ddd |
assert(s >= src);
|
|
Toshihiro Shimizu |
890ddd |
assert(s < sEnd);
|
|
Toshihiro Shimizu |
890ddd |
assert((s - src) % sIncr == 0);
|
|
Toshihiro Shimizu |
890ddd |
assert(d >= dst);
|
|
Toshihiro Shimizu |
890ddd |
assert(d < dEnd);
|
|
Toshihiro Shimizu |
890ddd |
assert((d - dst) % dIncr == 0);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
val = func(oldVal = val, *s);
|
|
Toshihiro Shimizu |
890ddd |
*d = func(*d, (oldVal == val) ? val : one_radR * oldVal + radR * val);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (; d < dwEnd; d += dIncr) {
|
|
Toshihiro Shimizu |
890ddd |
assert(d >= dst);
|
|
Toshihiro Shimizu |
890ddd |
assert(d < dEnd);
|
|
Toshihiro Shimizu |
890ddd |
assert((d - dst) % dIncr == 0);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
val = func(oldVal = val, 0);
|
|
Toshihiro Shimizu |
890ddd |
*d = func(*d, (oldVal == val) ? val : one_radR * oldVal + radR * val);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//--------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename chan="" pix,="" typename=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void erodilate_chan(const TRasterPT<pix> &src, const TRasterPT<chan> &dst, double radius, bool dilate)</chan></pix>
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
assert(radius > 0.0);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
int radI = tfloor(radius);
|
|
Toshihiro Shimizu |
890ddd |
double radR = radius - radI;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Using a temporary raster to keep intermediate results. This allows us to
|
|
Toshihiro Shimizu |
890ddd |
// perform a cache-friendly iteration in the separable/square kernel case
|
|
Toshihiro Shimizu |
890ddd |
int x, y, lx = src->getLx(), ly = src->getLy();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Peform rows erodilation
|
|
Toshihiro Shimizu |
890ddd |
TRasterPT<chan> temp(ly, lx); // Notice transposition plz</chan>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
if (dilate)
|
|
Toshihiro Shimizu |
890ddd |
for (y = 0; y != ly; ++y)
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_row(lx, &src->pixels(y)->m, 4, temp->pixels(0) + y, ly, radI, radR, MaxFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
else
|
|
Toshihiro Shimizu |
890ddd |
for (y = 0; y != ly; ++y)
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_row(lx, &src->pixels(y)->m, 4, temp->pixels(0) + y, ly, radI, radR, MinFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Perform columns erodilation
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
if (dilate)
|
|
Toshihiro Shimizu |
890ddd |
for (x = 0; x != lx; ++x)
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_row(ly, temp->pixels(x), 1, dst->pixels(0) + x, dst->getWrap(), radI, radR, MaxFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
else
|
|
Toshihiro Shimizu |
890ddd |
for (x = 0; x != lx; ++x)
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_row(ly, temp->pixels(x), 1, dst->pixels(0) + x, dst->getWrap(), radI, radR, MinFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//--------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename pix=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void rect_erodilate(const TRasterPT<pix> &src, const TRasterPT<pix> &dst, double radius)</pix></pix>
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
typedef typename Pix::Channel Chan;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
if (radius == 0.0) {
|
|
Toshihiro Shimizu |
890ddd |
// No-op case
|
|
Toshihiro Shimizu |
890ddd |
TRop::copy(dst, src);
|
|
Toshihiro Shimizu |
890ddd |
return;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
bool dilate = (radius >= 0.0);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Perform columns erodilation
|
|
Toshihiro Shimizu |
890ddd |
TRasterPT<chan> temp(src->getLx(), src->getLy());</chan>
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_chan(src, temp, fabs(radius), dilate);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Remember that we have just calculated the matte values. We still have to apply them to the old RGB
|
|
Toshihiro Shimizu |
890ddd |
// values, which requires depremultiplying from source matte and premultiplying with the new one.
|
|
Toshihiro Shimizu |
890ddd |
if (dilate)
|
|
Toshihiro Shimizu |
890ddd |
::copyChannels_dilate(src, temp, dst);
|
|
Toshihiro Shimizu |
890ddd |
else
|
|
Toshihiro Shimizu |
890ddd |
::copyChannels_erode(src, temp, dst);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
} // namespace
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//********************************************************
|
|
Toshihiro Shimizu |
890ddd |
// EroDilate round algorithm
|
|
Toshihiro Shimizu |
890ddd |
//********************************************************
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
namespace
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename chan,="" func="" typename=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void erodilate_quarters(int lx, int ly,
|
|
Toshihiro Shimizu |
890ddd |
Chan *src, int sIncrX, int sIncrY,
|
|
Toshihiro Shimizu |
890ddd |
Chan *dst, int dIncrX, int dIncrY,
|
|
Toshihiro Shimizu |
890ddd |
double radius, double shift, Func func)
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
double sqRadius = sq(radius);
|
|
Toshihiro Shimizu |
890ddd |
double squareHeight = radius / tcg::consts::sqrt2;
|
|
Toshihiro Shimizu |
890ddd |
int squareHeightI = tfloor(squareHeight);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// For every arc point
|
|
Toshihiro Shimizu |
890ddd |
int arcY;
|
|
Toshihiro Shimizu |
890ddd |
for (arcY = -squareHeightI; arcY <= squareHeightI; ++arcY) {
|
|
Toshihiro Shimizu |
890ddd |
// Calculate x and weights
|
|
Toshihiro Shimizu |
890ddd |
double sqArcY = sq(arcY);
|
|
Toshihiro Shimizu |
890ddd |
assert(sqRadius >= sqArcY);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
double x = shift + sqrt(sqRadius - sqArcY) - squareHeight;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
int arcX = tfloor(x);
|
|
Toshihiro Shimizu |
890ddd |
double w = x - arcX, one_w = 1.0 - w;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Build dst area influenced by the arc point. Func with 0 outside that.
|
|
Toshihiro Shimizu |
890ddd |
TRect bounds(0, 0, lx, ly);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
TRect dRect(bounds * (bounds + TPoint(-arcX, -arcY)));
|
|
Toshihiro Shimizu |
890ddd |
TRect sRect(bounds * (bounds + TPoint(arcX, arcY)));
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
int sy, dy;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Func with 0 before dRect.y0
|
|
Toshihiro Shimizu |
890ddd |
for (dy = 0; dy < dRect.y0; ++dy) {
|
|
Toshihiro Shimizu |
890ddd |
Chan *d, *dBegin = dst + dy * dIncrY, *dEnd = dBegin + lx * dIncrX;
|
|
Toshihiro Shimizu |
890ddd |
for (d = dBegin; d != dEnd; d += dIncrX) {
|
|
Toshihiro Shimizu |
890ddd |
//assert(d >= dst); assert(d < dEnd); assert((d-dst) % dIncrX == 0);
|
|
Toshihiro Shimizu |
890ddd |
*d = func(*d, 0);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Func with 0 after dRect.y1
|
|
Toshihiro Shimizu |
890ddd |
for (dy = dRect.y1; dy < ly; ++dy) {
|
|
Toshihiro Shimizu |
890ddd |
Chan *d, *dBegin = dst + dy * dIncrY, *dEnd = dBegin + lx * dIncrX;
|
|
Toshihiro Shimizu |
890ddd |
for (d = dBegin; d != dEnd; d += dIncrX) {
|
|
Toshihiro Shimizu |
890ddd |
//assert(d >= dst); assert(d < dEnd); assert((d-dst) % dIncrX == 0);
|
|
Toshihiro Shimizu |
890ddd |
*d = func(*d, 0);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// For every dst pixel in the area, Func with the corresponding pixel in src
|
|
Toshihiro Shimizu |
890ddd |
for (dy = dRect.y0, sy = sRect.y0; dy != dRect.y1; ++dy, ++sy) {
|
|
Toshihiro Shimizu |
890ddd |
Chan *d, *dLine = dst + dy * dIncrY, *dBegin = dLine + dRect.x0 * dIncrX;
|
|
Toshihiro Shimizu |
890ddd |
Chan *s, *sLine = src + sy * sIncrY, *sBegin = sLine + sRect.x0 * sIncrX, *sEnd = sLine + sRect.x1 * sIncrX;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
Chan *sLast = sEnd - sIncrX; // sLast would lerp with sEnd
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
for (d = dBegin, s = sBegin; s != sLast; d += dIncrX, s += sIncrX) // hence we stop before it
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
//assert(s >= src); assert(s < sEnd); assert((s-src) % sIncrX == 0);
|
|
Toshihiro Shimizu |
890ddd |
//assert(d >= dst); assert(d < dEnd); assert((d-dst) % dIncrX == 0);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
*d = func(*d, *s * one_w + *(s + sIncrX) * w);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//assert(s >= src); assert(s < sEnd); assert((s-src) % sIncrX == 0);
|
|
Toshihiro Shimizu |
890ddd |
//assert(d >= dst); assert(d < dEnd); assert((d-dst) % dIncrX == 0);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
*d = func(*d, *s * one_w); // lerp sLast with 0
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//--------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename pix=""></typename>
|
|
Toshihiro Shimizu |
890ddd |
void circular_erodilate(const TRasterPT<pix> &src, const TRasterPT<pix> &dst, double radius)</pix></pix>
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
typedef typename Pix::Channel Chan;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
if (radius == 0.0) {
|
|
Toshihiro Shimizu |
890ddd |
// No-op case
|
|
Toshihiro Shimizu |
890ddd |
TRop::copy(dst, src);
|
|
Toshihiro Shimizu |
890ddd |
return;
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Ok, the idea is: consider the maximal embedded square in our circular structuring element.
|
|
Toshihiro Shimizu |
890ddd |
// Erodilating by it consists in the consecutive erodilation by rows and columns with the same
|
|
Toshihiro Shimizu |
890ddd |
// 'square' radius. Now, it's easy to see that the square could be 'bent' so that one of its
|
|
Toshihiro Shimizu |
890ddd |
// edges matches that of a 1/4 of the circle's edge, while remaining inside the circle.
|
|
Toshihiro Shimizu |
890ddd |
// Erodilating by the bent square can be achieved by erodilating first by rows or column for
|
|
Toshihiro Shimizu |
890ddd |
// the square edge radius, followed by perpendicular erodilationg with a fourth of our
|
|
Toshihiro Shimizu |
890ddd |
// circumference. Sum the 4 erodilations needed to complete the circumference - and it's done.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// NOTE: Unfortunately, the above decomposition has lots of intersections among the pieces - yet
|
|
Toshihiro Shimizu |
890ddd |
// it's simple enough and removes an O(radius) from the naive algorithm. Could be done better?
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// First, build the various erodilation data
|
|
Toshihiro Shimizu |
890ddd |
bool dilate = (radius >= 0.0);
|
|
Toshihiro Shimizu |
890ddd |
radius = fabs(radius);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
double inner_square_diameter = radius * tcg::consts::sqrt2;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
double shift = 0.25 * inner_square_diameter; // Shift of the bent square SE needed to avoid
|
|
Toshihiro Shimizu |
890ddd |
// touching the circumference on the other side
|
|
Toshihiro Shimizu |
890ddd |
double row_filter_radius = 0.5 * (inner_square_diameter - shift);
|
|
Toshihiro Shimizu |
890ddd |
double cseShift = 0.5 * shift; // circumference structuring element shift
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
int lx = src->getLx(), ly = src->getLy();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
TRasterPT<chan> temp1(lx, ly), temp2(lx, ly);</chan>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
int radI = tfloor(row_filter_radius);
|
|
Toshihiro Shimizu |
890ddd |
double radR = row_filter_radius - radI;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
if (dilate) {
|
|
Toshihiro Shimizu |
890ddd |
temp2->fill(0); // Initialize with a Func-neutral value
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
if (row_filter_radius > 0.0)
|
|
Toshihiro Shimizu |
890ddd |
for (int y = 0; y != ly; ++y)
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_row(lx, &src->pixels(y)->m, 4, temp1->pixels(y), 1, radI, radR, MaxFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
else
|
|
Toshihiro Shimizu |
890ddd |
::copyMatte(src, temp1);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_quarters(lx, ly, temp1->pixels(0), 1, lx, temp2->pixels(0), 1, lx, radius, cseShift, MaxFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_quarters(lx, ly, temp1->pixels(0) + lx - 1, -1, lx, temp2->pixels(0) + lx - 1, -1, lx, radius, cseShift, MaxFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
if (row_filter_radius > 0.0)
|
|
Toshihiro Shimizu |
890ddd |
for (int x = 0; x != lx; ++x)
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_row(ly, &src->pixels(0)[x].m, 4 * src->getWrap(), temp1->pixels(0) + x, lx, radI, radR, MaxFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
else
|
|
Toshihiro Shimizu |
890ddd |
::copyMatte(src, temp1);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_quarters(ly, lx, temp1->pixels(0), lx, 1, temp2->pixels(0), lx, 1, radius, cseShift, MaxFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_quarters(ly, lx, temp1->pixels(0) + lx * ly - 1, -lx, -1, temp2->pixels(0) + lx * ly - 1, -lx, -1, radius, cseShift, MaxFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
} else {
|
|
Toshihiro Shimizu |
890ddd |
temp2->fill((std::numeric_limits<chan>::max)()); // Initialize with a Func-neutral value</chan>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
if (row_filter_radius > 0.0)
|
|
Toshihiro Shimizu |
890ddd |
for (int y = 0; y != ly; ++y)
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_row(lx, &src->pixels(y)->m, 4, temp1->pixels(y), 1, radI, radR, MinFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
else
|
|
Toshihiro Shimizu |
890ddd |
::copyMatte(src, temp1);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_quarters(lx, ly, temp1->pixels(0), 1, lx, temp2->pixels(0), 1, lx, radius, cseShift, MinFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_quarters(lx, ly, temp1->pixels(0) + lx - 1, -1, lx, temp2->pixels(0) + lx - 1, -1, lx, radius, cseShift, MinFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
if (row_filter_radius > 0.0)
|
|
Toshihiro Shimizu |
890ddd |
for (int x = 0; x != lx; ++x)
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_row(ly, &src->pixels(0)[x].m, 4 * src->getWrap(), temp1->pixels(0) + x, lx, radI, radR, MinFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
else
|
|
Toshihiro Shimizu |
890ddd |
::copyMatte(src, temp1);
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_quarters(ly, lx, temp1->pixels(0), lx, 1, temp2->pixels(0), lx, 1, radius, cseShift, MinFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
::erodilate_quarters(ly, lx, temp1->pixels(0) + lx * ly - 1, -lx, -1, temp2->pixels(0) + lx * ly - 1, -lx, -1, radius, cseShift, MinFunc<chan>());</chan>
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// Remember that we have just calculated the matte values. We still have to apply them to the old RGB
|
|
Toshihiro Shimizu |
890ddd |
// values, which requires depremultiplying from source matte and premultiplying with the new one.
|
|
Toshihiro Shimizu |
890ddd |
if (dilate)
|
|
Toshihiro Shimizu |
890ddd |
::copyChannels_dilate(src, temp2, dst);
|
|
Toshihiro Shimizu |
890ddd |
else
|
|
Toshihiro Shimizu |
890ddd |
::copyChannels_erode(src, temp2, dst);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
} // namespace
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//********************************************************
|
|
Toshihiro Shimizu |
890ddd |
// EroDilate main functions
|
|
Toshihiro Shimizu |
890ddd |
//********************************************************
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
void TRop::erodilate(const TRasterP &src, const TRasterP &dst,
|
|
Toshihiro Shimizu |
890ddd |
double radius, ErodilateMaskType type)
|
|
Toshihiro Shimizu |
890ddd |
{
|
|
Toshihiro Shimizu |
890ddd |
assert(src->getSize() == dst->getSize());
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
src->lock(), dst->lock();
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
if ((TRaster32P)src && (TRaster32P)dst)
|
|
Toshihiro Shimizu |
890ddd |
switch (type) {
|
|
Toshihiro Shimizu |
890ddd |
case ED_rectangular:
|
|
Toshihiro Shimizu |
890ddd |
::rect_erodilate<tpixel32>(src, dst, radius);</tpixel32>
|
|
Toshihiro Shimizu |
890ddd |
CASE ED_circular : ::circular_erodilate<tpixel32>(src, dst, radius);</tpixel32>
|
|
Toshihiro Shimizu |
890ddd |
DEFAULT:
|
|
Toshihiro Shimizu |
890ddd |
assert(!"Unknown mask type");
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
else if ((TRaster64P)src && (TRaster64P)dst)
|
|
Toshihiro Shimizu |
890ddd |
switch (type) {
|
|
Toshihiro Shimizu |
890ddd |
case ED_rectangular:
|
|
Toshihiro Shimizu |
890ddd |
::rect_erodilate<tpixel64>(src, dst, radius);</tpixel64>
|
|
Toshihiro Shimizu |
890ddd |
CASE ED_circular : ::circular_erodilate<tpixel64>(src, dst, radius);</tpixel64>
|
|
Toshihiro Shimizu |
890ddd |
DEFAULT:
|
|
Toshihiro Shimizu |
890ddd |
assert(!"Unknown mask type");
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
else
|
|
Toshihiro Shimizu |
890ddd |
assert(!"Unsupported raster type!");
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
src->unlock(), dst->unlock();
|
|
Toshihiro Shimizu |
890ddd |
}
|