From 6a424f82bd1b6d41523bb11d3e0a29da895dc445 Mon Sep 17 00:00:00 2001 From: Holy Wu Date: Tue, 5 Feb 2019 12:54:50 +0800 Subject: [PATCH] Add parameter sigma_v --- README.md | 8 +++-- TCanny/TCanny.cpp | 72 ++++++++++++++++++++++++++++++--------------- TCanny/TCannyCL.cpp | 66 ++++++++++++++++++++++++++++------------- 3 files changed, 98 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 09b5478..881157e 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,13 @@ Ported from AviSynth plugin http://bengal.missouri.edu/~kes25c/ Usage ===== - tcanny.TCanny(clip clip[, float[] sigma=1.5, float t_h=8.0, float t_l=1.0, int mode=0, int op=1, float gmmax=50.0, int opt=0, int[] planes=[0, 1, 2]]) + tcanny.TCanny(clip clip[, float[] sigma=1.5, float[] sigma_v=sigma, float t_h=8.0, float t_l=1.0, int mode=0, int op=1, float gmmax=50.0, int opt=0, int[] planes=[0, 1, 2]]) * 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. -* sigma: Standard deviation of gaussian blur. If a single `sigma` is specified, it will be used for all planes. If two `sigma` are given then the second value will be used for the third plane as well. The value used internally will be adjusted according to the subsampling of the second and third plane in each direction. +* sigma: Standard deviation of horizontal gaussian blur. If a single `sigma` is specified, it will be used for all planes. If two `sigma` are given then the second value will be used for the third plane as well. + +* sigma_v: Standard deviation of vertical gaussian blur. * t_h: High gradient magnitude threshold for hysteresis. @@ -43,7 +45,7 @@ Usage --- - tcanny.TCannyCL(clip clip[, float[] sigma=1.5, float t_h=8.0, float t_l=1.0, int mode=0, int op=1, float gmmax=50.0, int device=-1, bint list_device=False, bint info=False, int[] planes=[0, 1, 2]]) + tcanny.TCannyCL(clip clip[, float[] sigma=1.5, float[] sigma_v=sigma, float t_h=8.0, float t_l=1.0, int mode=0, int op=1, float gmmax=50.0, int device=-1, bint list_device=False, bint info=False, int[] planes=[0, 1, 2]]) * device: Sets target OpenCL device. Use `list_device` to get the index of the available devices. By default the default device is selected. diff --git a/TCanny/TCanny.cpp b/TCanny/TCanny.cpp index 73df4bc..b9fa79f 100644 --- a/TCanny/TCanny.cpp +++ b/TCanny/TCanny.cpp @@ -492,24 +492,36 @@ static void VS_CC tcannyCreate(const VSMap *in, VSMap *out, void *userData, VSCo if (d->vi->height < 2) throw std::string{ "the clip's height must be greater than or equal to 2" }; - const int numSigma = vsapi->propNumElements(in, "sigma"); - if (numSigma > d->vi->format->numPlanes) - throw std::string{ "more sigma given than the number of planes" }; + const int numSigmaH = vsapi->propNumElements(in, "sigma"); + if (numSigmaH > d->vi->format->numPlanes) + throw std::string{ "more sigma given than there are planes" }; + + const int numSigmaV = vsapi->propNumElements(in, "sigma_v"); + if (numSigmaV > d->vi->format->numPlanes) + throw std::string{ "more sigma_v given than there are planes" }; float sigmaH[3], sigmaV[3]; for (int i = 0; i < 3; i++) { - if (i < numSigma) { - sigmaH[i] = sigmaV[i] = static_cast(vsapi->propGetFloat(in, "sigma", i, nullptr)); - } else if (i == 0) { - sigmaH[0] = sigmaV[0] = 1.5f; - } else if (i == 1) { + if (i < numSigmaH) + sigmaH[i] = static_cast(vsapi->propGetFloat(in, "sigma", i, nullptr)); + else if (i == 0) + sigmaH[0] = 1.5f; + else if (i == 1) sigmaH[1] = sigmaH[0] / (1 << d->vi->format->subSamplingW); - sigmaV[1] = sigmaV[0] / (1 << d->vi->format->subSamplingH); - } else { + else sigmaH[2] = sigmaH[1]; + + if (i < numSigmaV) + sigmaV[i] = static_cast(vsapi->propGetFloat(in, "sigma_v", i, nullptr)); + else if (i < numSigmaH) + sigmaV[i] = sigmaH[i]; + else if (i == 0) + sigmaV[0] = 1.5f; + else if (i == 1) + sigmaV[1] = sigmaV[0] / (1 << d->vi->format->subSamplingH); + else sigmaV[2] = sigmaV[1]; - } } d->t_h = static_cast(vsapi->propGetFloat(in, "t_h", 0, &err)); @@ -552,6 +564,9 @@ static void VS_CC tcannyCreate(const VSMap *in, VSMap *out, void *userData, VSCo for (int i = 0; i < 3; i++) { if (sigmaH[i] < 0.f) throw std::string{ "sigma must be greater than or equal to 0.0" }; + + if (sigmaV[i] < 0.f) + throw std::string{ "sigma_v must be greater than or equal to 0.0" }; } if (d->t_l >= d->t_h) @@ -600,21 +615,28 @@ static void VS_CC tcannyCreate(const VSMap *in, VSMap *out, void *userData, VSCo } for (int plane = 0; plane < d->vi->format->numPlanes; plane++) { - if (d->process[plane] && sigmaH[plane]) { - d->weightsH[plane] = gaussianWeights(sigmaH[plane], d->radiusH[plane]); - d->weightsV[plane] = gaussianWeights(sigmaV[plane], d->radiusV[plane]); - if (!d->weightsH[plane] || !d->weightsV[plane]) - throw std::string{ "malloc failure (weights)" }; - - const int width = d->vi->width >> (plane ? d->vi->format->subSamplingW : 0); - const int height = d->vi->height >> (plane ? d->vi->format->subSamplingH : 0); - const std::string planeOrder{ plane == 0 ? "first" : (plane == 1 ? "second" : "third") }; + if (d->process[plane]) { + if (sigmaH[plane]) { + d->weightsH[plane] = gaussianWeights(sigmaH[plane], d->radiusH[plane]); + if (!d->weightsH[plane]) + throw std::string{ "malloc failure (weightsH)" }; + + const int width = d->vi->width >> (plane ? d->vi->format->subSamplingW : 0); + const std::string planeOrder{ plane == 0 ? "first" : (plane == 1 ? "second" : "third") }; + if (width < d->radiusH[plane] + 1) + throw std::string{ "the " + planeOrder + " plane's width must be greater than or equal to " + std::to_string(d->radiusH[plane] + 1) + " for specified sigma" }; + } - if (width < d->radiusH[plane] + 1) - throw std::string{ "the " + planeOrder + " plane's width must be greater than or equal to " + std::to_string(d->radiusH[plane] + 1) + " for specified sigma" }; + if (sigmaV[plane]) { + d->weightsV[plane] = gaussianWeights(sigmaV[plane], d->radiusV[plane]); + if (!d->weightsV[plane]) + throw std::string{ "malloc failure (weightsV)" }; - if (height < d->radiusV[plane] + 1) - throw std::string{ "the " + planeOrder + " plane's height must be greater than or equal to " + std::to_string(d->radiusV[plane] + 1) + " for specified sigma" }; + const int height = d->vi->height >> (plane ? d->vi->format->subSamplingH : 0); + const std::string planeOrder{ plane == 0 ? "first" : (plane == 1 ? "second" : "third") }; + if (height < d->radiusV[plane] + 1) + throw std::string{ "the " + planeOrder + " plane's height must be greater than or equal to " + std::to_string(d->radiusV[plane] + 1) + " for specified sigma_v" }; + } } } @@ -643,6 +665,7 @@ VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc, VSRegiste registerFunc("TCanny", "clip:clip;" "sigma:float[]:opt;" + "sigma_v:float[]:opt;" "t_h:float:opt;" "t_l:float:opt;" "mode:int:opt;" @@ -656,6 +679,7 @@ VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc, VSRegiste registerFunc("TCannyCL", "clip:clip;" "sigma:float[]:opt;" + "sigma_v:float[]:opt;" "t_h:float:opt;" "t_l:float:opt;" "mode:int:opt;" diff --git a/TCanny/TCannyCL.cpp b/TCanny/TCannyCL.cpp index 75244a2..e368769 100644 --- a/TCanny/TCannyCL.cpp +++ b/TCanny/TCannyCL.cpp @@ -161,7 +161,7 @@ static const VSFrameRef *VS_CC tcannyclGetFrame(int n, int activationReason, voi queue.enqueue_write_image(srcImage, origin, region, srcp, stride); if (d->radiusV[plane]) { - gaussianBlurV.set_args(srcImage, gradientImage, d->weightsV[plane], d->radiusV[plane], d->offset[plane]); + gaussianBlurV.set_args(srcImage, d->radiusH[plane] ? gradientImage : blurImage, d->weightsV[plane], d->radiusV[plane], d->offset[plane]); queue.enqueue_nd_range_kernel(gaussianBlurV, 2, nullptr, globalWorkSize, nullptr); } else { copyPlane.set_args(srcImage, d->radiusH[plane] ? gradientImage : blurImage, d->offset[plane]); @@ -239,24 +239,36 @@ void VS_CC tcannyclCreate(const VSMap *in, VSMap *out, void *userData, VSCore *c (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" }; - const int numSigma = vsapi->propNumElements(in, "sigma"); - if (numSigma > d->vi->format->numPlanes) - throw std::string{ "more sigma given than the number of planes" }; + const int numSigmaH = vsapi->propNumElements(in, "sigma"); + if (numSigmaH > d->vi->format->numPlanes) + throw std::string{ "more sigma given than there are planes" }; + + const int numSigmaV = vsapi->propNumElements(in, "sigma_v"); + if (numSigmaV > d->vi->format->numPlanes) + throw std::string{ "more sigma_v given than there are planes" }; float sigmaH[3], sigmaV[3]; for (int i = 0; i < 3; i++) { - if (i < numSigma) { - sigmaH[i] = sigmaV[i] = static_cast(vsapi->propGetFloat(in, "sigma", i, nullptr)); - } else if (i == 0) { - sigmaH[0] = sigmaV[0] = 1.5f; - } else if (i == 1) { + if (i < numSigmaH) + sigmaH[i] = static_cast(vsapi->propGetFloat(in, "sigma", i, nullptr)); + else if (i == 0) + sigmaH[0] = 1.5f; + else if (i == 1) sigmaH[1] = sigmaH[0] / (1 << d->vi->format->subSamplingW); - sigmaV[1] = sigmaV[0] / (1 << d->vi->format->subSamplingH); - } else { + else sigmaH[2] = sigmaH[1]; + + if (i < numSigmaV) + sigmaV[i] = static_cast(vsapi->propGetFloat(in, "sigma_v", i, nullptr)); + else if (i < numSigmaH) + sigmaV[i] = sigmaH[i]; + else if (i == 0) + sigmaV[0] = 1.5f; + else if (i == 1) + sigmaV[1] = sigmaV[0] / (1 << d->vi->format->subSamplingH); + else sigmaV[2] = sigmaV[1]; - } } float t_h = static_cast(vsapi->propGetFloat(in, "t_h", 0, &err)); @@ -301,6 +313,9 @@ void VS_CC tcannyclCreate(const VSMap *in, VSMap *out, void *userData, VSCore *c for (int i = 0; i < 3; i++) { if (sigmaH[i] < 0.f) throw std::string{ "sigma must be greater than or equal to 0.0" }; + + if (sigmaV[i] < 0.f) + throw std::string{ "sigma_v must be greater than or equal to 0.0" }; } if (t_l >= t_h) @@ -433,17 +448,26 @@ void VS_CC tcannyclCreate(const VSMap *in, VSMap *out, void *userData, VSCore *c } for (int plane = 0; plane < d->vi->format->numPlanes; plane++) { - if (d->process[plane] && sigmaH[plane]) { - float * weightsH = gaussianWeights(sigmaH[plane], d->radiusH[plane]); - float * weightsV = gaussianWeights(sigmaV[plane], d->radiusV[plane]); - if (!weightsH || !weightsV) - throw std::string{ "malloc failure (weights)" }; + if (d->process[plane]) { + if (sigmaH[plane]) { + float * weightsH = gaussianWeights(sigmaH[plane], d->radiusH[plane]); + if (!weightsH) + throw std::string{ "malloc failure (weightsH)" }; - d->weightsH[plane] = compute::buffer{ d->ctx, (d->radiusH[plane] * 2 + 1) * sizeof(cl_float), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR | CL_MEM_HOST_NO_ACCESS, weightsH }; - d->weightsV[plane] = compute::buffer{ d->ctx, (d->radiusV[plane] * 2 + 1) * sizeof(cl_float), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR | CL_MEM_HOST_NO_ACCESS, weightsV }; + d->weightsH[plane] = compute::buffer{ d->ctx, (d->radiusH[plane] * 2 + 1) * sizeof(cl_float), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR | CL_MEM_HOST_NO_ACCESS, weightsH }; - delete[] weightsH; - delete[] weightsV; + delete[] weightsH; + } + + if (sigmaV[plane]) { + float * weightsV = gaussianWeights(sigmaV[plane], d->radiusV[plane]); + if (!weightsV) + throw std::string{ "malloc failure (weightsV)" }; + + d->weightsV[plane] = compute::buffer{ d->ctx, (d->radiusV[plane] * 2 + 1) * sizeof(cl_float), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR | CL_MEM_HOST_NO_ACCESS, weightsV }; + + delete[] weightsV; + } } }