diff --git a/README.md b/README.md index c4f312c..b5af40c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Usage ttmpsm.TTempSmooth(clip clip[, int maxr=3, int[] thresh=[4, 5, 5], int[] mdiff=[2, 3, 3], int strength=2, float scthresh=12.0, bint fp=True, clip pfclip=None, int[] planes]) -* clip: Clip to process. Any planar format with integer sample type of 8-16 bit depth is supported. +* clip: Clip to process. Any planar format with either integer sample type of 8-16 bit depth or float sample type of 32 bit depth is supported. * maxr: This sets the maximum temporal radius. By the way it works TTempSmooth automatically varies the radius used... this sets the maximum boundary. Possible values are 1 through 7. At 1 TTempSmooth will be (at max) including pixels from 1 frame away in the average (3 frames total will be considered counting the current frame). At 7 it would be including pixels from up to 7 frames away (15 frames total will be considered). With the way it checks motion there isn't much danger in setting this high, it's basically a quality vs. speed option. Lower settings are faster while larger values tend to create a more stable image. diff --git a/TTempSmooth/TTempSmooth.cpp b/TTempSmooth/TTempSmooth.cpp index 5f5e0e8..beb1da3 100644 --- a/TTempSmooth/TTempSmooth.cpp +++ b/TTempSmooth/TTempSmooth.cpp @@ -24,6 +24,7 @@ */ #include +#include #include #include #include @@ -38,13 +39,14 @@ struct TTempSmoothData { double scthresh; bool fp, process[3]; int diameter, shift; + float threshF[3]; unsigned * weight[3], cw; void (*filter[3])(const VSFrameRef *[15], const VSFrameRef *[15], VSFrameRef *, const int, const int, const int, const TTempSmoothData * const VS_RESTRICT, const VSAPI *); }; template -static void filter(const VSFrameRef * src[15], const VSFrameRef * pf[15], VSFrameRef * dst, const int fromFrame, const int toFrame, const int plane, - const TTempSmoothData * const VS_RESTRICT d, const VSAPI * vsapi) noexcept { +static void filterI(const VSFrameRef * src[15], const VSFrameRef * pf[15], VSFrameRef * dst, const int fromFrame, const int toFrame, const int plane, + const TTempSmoothData * const VS_RESTRICT d, const VSAPI * vsapi) noexcept { const int width = vsapi->getFrameWidth(dst, plane); const int height = vsapi->getFrameHeight(dst, plane); const int stride = vsapi->getStride(dst, plane) / sizeof(T1); @@ -144,19 +146,125 @@ static void filter(const VSFrameRef * src[15], const VSFrameRef * pf[15], VSFram } } +template +static void filterF(const VSFrameRef * src[15], const VSFrameRef * pf[15], VSFrameRef * dst, const int fromFrame, const int toFrame, const int plane, + const TTempSmoothData * const VS_RESTRICT d, const VSAPI * vsapi) noexcept { + const int width = vsapi->getFrameWidth(dst, plane); + const int height = vsapi->getFrameHeight(dst, plane); + const int stride = vsapi->getStride(dst, plane) / sizeof(float); + const float * srcp[15] = {}, * pfp[15] = {}; + for (int i = 0; i < d->diameter; i++) { + srcp[i] = reinterpret_cast(vsapi->getReadPtr(src[i], plane)); + pfp[i] = reinterpret_cast(vsapi->getReadPtr(pf[i], plane)); + } + float * VS_RESTRICT dstp = reinterpret_cast(vsapi->getWritePtr(dst, plane)); + + const float thresh = d->threshF[plane]; + const unsigned * const weightSaved = d->weight[plane]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const float c = pfp[d->maxr][x]; + unsigned weights = d->cw; + float sum = srcp[d->maxr][x] * d->cw; + + int frameIndex = d->maxr - 1; + + if (frameIndex > fromFrame) { + float t1 = pfp[frameIndex][x]; + float diff = std::abs(c - t1); + + if (diff < thresh) { + unsigned weight = weightSaved[useDiff ? static_cast(diff * 255.f) : frameIndex]; + weights += weight; + sum += srcp[frameIndex][x] * weight; + + frameIndex--; + int v = 256; + + while (frameIndex > fromFrame) { + const float t2 = t1; + t1 = pfp[frameIndex][x]; + diff = std::abs(c - t1); + + if (diff < thresh && std::abs(t1 - t2) < thresh) { + weight = weightSaved[useDiff ? static_cast(diff * 255.f) + v : frameIndex]; + weights += weight; + sum += srcp[frameIndex][x] * weight; + + frameIndex--; + v += 256; + } else { + break; + } + } + } + } + + frameIndex = d->maxr + 1; + + if (frameIndex < toFrame) { + float t1 = pfp[frameIndex][x]; + float diff = std::abs(c - t1); + + if (diff < thresh) { + unsigned weight = weightSaved[useDiff ? static_cast(diff * 255.f) : frameIndex]; + weights += weight; + sum += srcp[frameIndex][x] * weight; + + frameIndex++; + int v = 256; + + while (frameIndex < toFrame) { + const float t2 = t1; + t1 = pfp[frameIndex][x]; + diff = std::abs(c - t1); + + if (diff < thresh && std::abs(t1 - t2) < thresh) { + weight = weightSaved[useDiff ? static_cast(diff * 255.f) + v : frameIndex]; + weights += weight; + sum += srcp[frameIndex][x] * weight; + + frameIndex++; + v += 256; + } else { + break; + } + } + } + } + + if (d->fp) + dstp[x] = (srcp[d->maxr][x] * (65536 - weights) + sum) / 65536.f; + else + dstp[x] = sum / weights; + } + + for (int i = 0; i < d->diameter; i++) { + srcp[i] += stride; + pfp[i] += stride; + } + dstp += stride; + } +} + static void selectFunctions(TTempSmoothData * d) noexcept { for (int plane = 0; plane < d->vi->format->numPlanes; plane++) { if (d->process[plane]) { if (d->thresh[plane] > d->mdiff[plane] + 1) { if (d->vi->format->bytesPerSample == 1) - d->filter[plane] = filter; + d->filter[plane] = filterI; + else if (d->vi->format->bytesPerSample == 2) + d->filter[plane] = filterI; else - d->filter[plane] = filter; + d->filter[plane] = filterF; } else { if (d->vi->format->bytesPerSample == 1) - d->filter[plane] = filter; + d->filter[plane] = filterI; + else if (d->vi->format->bytesPerSample == 2) + d->filter[plane] = filterI; else - d->filter[plane] = filter; + d->filter[plane] = filterF; } } } @@ -253,8 +361,9 @@ static void VS_CC ttempsmoothCreate(const VSMap *in, VSMap *out, void *userData, d->vi = vsapi->getVideoInfo(d->node); try { - if (!isConstantFormat(d->vi) || d->vi->format->sampleType != stInteger || d->vi->format->bitsPerSample > 16) - throw std::string{ "only constant format 8-16 bit integer input supported" }; + if (!isConstantFormat(d->vi) || (d->vi->format->sampleType == stInteger && d->vi->format->bitsPerSample > 16) || + (d->vi->format->sampleType == stFloat && d->vi->format->bitsPerSample != 32)) + throw std::string{ "only constant format 8-16 bit integer and 32 bit float input supported" }; d->maxr = int64ToIntS(vsapi->propGetInt(in, "maxr", 0, &err)); if (err) @@ -408,7 +517,10 @@ static void VS_CC ttempsmoothCreate(const VSMap *in, VSMap *out, void *userData, d->cw = d->weight[plane][d->maxr]; } - d->thresh[plane] <<= d->shift; + if (d->vi->format->sampleType == stInteger) + d->thresh[plane] <<= d->shift; + else + d->threshF[plane] = d->thresh[plane] / 256.f; } } diff --git a/configure.ac b/configure.ac index 5082019..0fe96dc 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([TTempSmooth], [1], [https://github.com/HomeOfVapourSynthEvolution/VapourSynth-TTempSmooth/issues], [TTempSmooth], [https://github.com/HomeOfVapourSynthEvolution/VapourSynth-TTempSmooth/]) +AC_INIT([TTempSmooth], [2], [https://github.com/HomeOfVapourSynthEvolution/VapourSynth-TTempSmooth/issues], [TTempSmooth], [https://github.com/HomeOfVapourSynthEvolution/VapourSynth-TTempSmooth/]) : ${CXXFLAGS=""}