From b24350fd4989a38d0b9cb20a083bb186e2b24f03 Mon Sep 17 00:00:00 2001 From: noizuy <85082087+noizuy@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:08:29 +0000 Subject: [PATCH] Merge Balance modes, add new funcs, remove FixBrightness No point in keeping FixBrightness since SinglePlane is actually faster. --- README.md | 41 +- src/bore.c | 1251 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 872 insertions(+), 420 deletions(-) diff --git a/README.md b/README.md index 96856bf..7e1b731 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,14 @@ -# FixBrightness - -This is a bbmod-style border deringer, which works by multiplying every pixel by the average of dividing each pixel in a line by its nearest pixel in the next line. Unlike bbmod, this doesn't attempt to localize the result and just assumes the same adjustment to be made for the entire row. It's effectively an automated FixBrightness/rektlvls, hence the name. - -## Usage - -``` -core.bore.FixBrightness(clip clip, int top=0, int bottom=0, int left=0, int right=0, clip ignore_mask=None, float thrlo=0.1, float thrhi=8.0, int step=1, int plane=0) -``` - -* `clip`: 32-bit float clip. -* `top = 0`, `bottom = 0`, `left = 0`, `right = 0`: number of lines from each border to adjust. -* `ignore_mask = None`: Ignore mask, needs to be 8-bit. Anything below 128 will be ignored during adjustment calculation. -* `thrlo = 0.1`: Lower limit of adjustment. Any quotient below this will be ignored. -* `thrhi = 8.0`: Upper limit of adjustment. Any quotient above this will be ignored. -* `step = 1`: Speed up processing ever so slightly by lowering the number of pixels used to find the adjustment. Might be an unnecessary parameter. -* `plane = 0`: Plane to process. +This approach to border deringing uses [linear least squares](https://www.gnu.org/software/gsl/doc/html/lls.html) to find a proper adjustment. -# Balance +SinglePlane does simple linear regression between each line and the first clean line. MultiPlane does the same with multiple linear regression using all three planes. SinglePlaneLimited does this for each pixel individually using only the nearest pixels defined by ref_line_size. SinglePlaneWeighted is SinglePlaneLimited with bilateral filter style weighting for distance and difference. SinglePlaneDebug is the same as SinglePlane, but saves adjustment values to frame props instead of applying them. -This approach to border deringing uses [linear least squares](https://www.gnu.org/software/gsl/doc/html/lls.html) to find a proper adjustment. +SinglePlaneLimited and SinglePlaneWeighted are more experimental and should be used with care. -In simple (0) `mode`, it functions similarly to `FixBrightness`, using simple linear regression instead of a simple mean. This is more robust than `FixBrightness`, although a bit slower. -In multiple (1) `mode`, it uses multiple linear regression with all three planes as input parameters, which may help with ringing that was added in a different color space. This should be considered a last resort effort. +The frame props generated by SinglePlanedebug are: +* `BoreAdjustment` +* `BoreCovariance` +* `BoreSumSquares` +These are in order of operation, i.e. top, bottom, left, right, each from innermost to outermost line. ## Requirements * [GSL](https://www.gnu.org/software/gsl/) @@ -29,13 +16,19 @@ In multiple (1) `mode`, it uses multiple linear regression with all three planes ## Usage ``` -core.bore.Balance(clip clip, int top=0, int bottom=0, int left=0, int right=0, int plane=0, int mode=0) +core.bore.SinglePlane(clip clip, int left=0, int right=0, int top=0, int bottom=0, clip ignore_mask=None, int plane=0) +core.bore.MultiPlane(clip clip, int left=0, int right=0, int top=0, int bottom=0, clip ignore_mask=None, int plane=0) +core.bore.SinglePlaneLimited(clip clip, int left=0, int right=0, int top=0, int bottom=0, clip ignore_mask=None, int ref_line_size=100, int plane=0) +core.bore.SinglePlaneWeighted(clip clip, int left=0, int right=0, int top=0, int bottom=0, clip ignore_mask=None, float sigmaS=50.0, float sigmaR=0.5, float sigmaD=1.5, int ref_line_size=100, int plane=0) +core.bore.SinglePlaneDebug(clip clip, int left=0, int right=0, int top=0, int bottom=0, clip ignore_mask=None, int plane=0) ``` * `clip`: 32-bit float clip. -* `top = 0`, `bottom = 0`, `left = 0`, `right = 0`: number of lines from each border to adjust. +* `left = 0`, `right = 0`, `top = 0`, `bottom = 0`: number of lines from each border to adjust. +* `ignore_mask = None`: 8-bit gray mask with pixels to avoid when calculating the adjustment. If more than one plane exists, only the first one is used, so it needs to match the to-be-adjusted plane's resolution. * `plane = 0`: Plane to adjust. -* `mode = 0`: Linear regression mode, 0 for simple and 1 for multiple linear regression using all three planes as parameters (requires no subsampling). Mode 2 is a debugging mode for simple linear regression that attaches the following frame properties: BalanceAdjustment, BalanceCovariance, BalanceSumSquares. Note that the values are in order of operation, i.e. top -> bottom -> left -> right with values for the innermost lines first. +* `sigmaS = 50, sigmaR = 0.5, sigmaD = 1.5`: SinglePlaneWeighted's smoothing parameters, same as a bilateral filter's, sigmaR is difference between pixels in same line, sigmaD is difference between adjustments compared to current pixel and its neighbor. +* `ref_line_size = 100`: Reference line size for SinglePlaneLimited/Weighted. The actual size is `2 * ref_line_size + 1`. # Compilation diff --git a/src/bore.c b/src/bore.c index af8341f..5eb2388 100644 --- a/src/bore.c +++ b/src/bore.c @@ -21,364 +21,182 @@ #include #include +typedef enum LinRegMode { + LINREG_MODE_SINGLE = 1, + LINREG_MODE_MULTI = 2, + LINREG_MODE_SINGLE_LIMITED = 3, + LINREG_MODE_SINGLE_WEIGHTED = 4, + LINREG_MODE_SINGLE_DEBUG = 5 +} LinRegMode; + typedef struct { VSNode *node; VSNode *ignore_mask; - float thrlo; - float thrhi; int plane; int top; int bottom; int left; int right; - int step; -} FixBrightnessData; - -static void processRow(int row, int w, int h, ptrdiff_t stride, float *dstp, FixBrightnessData *d) { - int x; - float cur; - float ref; + int ref_line_size; + double sigmaS; + double sigmaR; + double sigmaD; + void (*processRow)(int, int, int, ptrdiff_t, float*, int, double, double, double, const unsigned char * restrict, ptrdiff_t, int); + void (*processColumn)(int, int, int, ptrdiff_t, float*, int, double, double, double, const unsigned char * restrict, ptrdiff_t, int); +} LinearRegressionData; +static void processRowSLR(int row, int w, int h, ptrdiff_t stride, float *dstp, int ref_line_size, double sigmaS, double sigmaR, double sigmaD, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { int sign = 1; if (row > h / 2) sign = -1; - float tmp; - float sum = 0.f; - int div = 0; - - // go through row and get current and reference - dstp += stride * row; - for (x = 0; x < w; x += d->step) { - cur = (dstp[x]); - ref = (dstp[sign * stride + x]); - tmp = ref / cur; - // add to sum if quotient is within thresholds and there's no sign change - if ((tmp > d->thrlo) && (tmp < d->thrhi) && ((cur > 0 && ref > 0) || (cur < 0 && ref < 0))) { - sum += tmp; - div += 1; - } - } + double *cur, *ref; + int i; - // division by zero => neutral - if (div == 0) - return; - // otherwise just mean of the quotient of the two rows - else - sum /= div; + cur = malloc(sizeof(double) * w); + ref = malloc(sizeof(double) * w); - // adjust each pixel - for (x = 0; x < w; x++) { - dstp[x] *= sum; + dstp += row * stride; + for (i = 0; i < w; i++) { + cur[i] = dstp[i]; + ref[i] = dstp[sign * stride + i]; } -} -static void processRowMasked(int row, int w, int h, ptrdiff_t stride, float *dstp, const unsigned char * restrict imaskp, FixBrightnessData *d) { - int x; - float cur; - float ref; + double cov11, sumsq; - int sign = 1; - if (row > h / 2) - sign = -1; + double c1; - float tmp; - float sum = 0.f; - int div = 0; + int status = gsl_fit_mul(cur, 1, ref, 1, w, &c1, &cov11, &sumsq); - // go through row and get current and reference - dstp += stride * row; - imaskp += stride * row; - for (x = 0; x < w; x += d->step) { - if (imaskp[x] < 128 && imaskp[sign * stride + x] < 128) { - cur = (dstp[x]); - ref = (dstp[sign * stride + x]); - tmp = ref / cur; - // add to sum if quotient is within thresholds and there's no sign change - if ((tmp > d->thrlo) && (tmp < d->thrhi) && ((cur > 0 && ref > 0) || (cur < 0 && ref < 0))) { - sum += tmp; - div += 1; - } + if (!status && isfinite(c1)) { + // adjust each pixel + for (i = 0; i < w; i++) { + dstp[i] *= c1; } } - // division by zero => neutral - if (div == 0) - return; - // otherwise just mean of the quotient of the two rows - else - sum /= div; - - // adjust each pixel - for (x = 0; x < w; x++) { - if (imaskp[x] < 128 && imaskp[sign * stride + x] < 128) { - dstp[x] *= sum; - } - } + free(cur); + free(ref); } -static void processColumn(int column, int w, int h, ptrdiff_t stride, float *dstp, FixBrightnessData *d) { - int x; - float cur; - float ref; - +static void processColumnSLR(int column, int w, int h, ptrdiff_t stride, float *dstp, int ref_line_size, double sigmaS, double sigmaR, double sigmaD, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { int sign = 1; if (column > w / 2) sign = -1; - float tmp; - float sum = 0.f; - int div = 0; - - // go through column and get current and reference - for (x = 0; x < h; x += d->step) { - cur = (dstp[x * stride + column]); - ref = (dstp[x * stride + column + sign]); - tmp = ref / cur; - // add to sum if quotient is within thresholds and there's no sign change - if ((tmp > d->thrlo) && (tmp < d->thrhi) && ((cur > 0 && ref > 0) || (cur < 0 && ref < 0))) { - sum += tmp; - div += 1; - } - } - - // division by zero => neutral - if (div == 0) - return; - // otherwise just mean of the quotient of the two columns - else - sum /= div; - - // adjust each pixel - for (x = 0; x < h; x++) { - dstp[x * stride + column] *= sum; - } -} - -static void processColumnMasked(int column, int w, int h, ptrdiff_t stride, float *dstp, const unsigned char * restrict imaskp, FixBrightnessData *d) { - int x; - float cur; - float ref; - - int sign = 1; - if (column > w / 2) - sign = -1; + double *cur, *ref; + int i; - float tmp; - float sum = 0.f; - int div = 0; - - // go through column and get current and reference - for (x = 0; x < h; x += d->step) { - if (imaskp[x * stride + column] < 128 && imaskp[x * stride + column + sign] < 128) { - cur = (dstp[x * stride + column]); - ref = (dstp[x * stride + column + sign]); - tmp = ref / cur; - // add to sum if quotient is within thresholds and there's no sign change - if ((tmp > d->thrlo) && (tmp < d->thrhi) && ((cur > 0 && ref > 0) || (cur < 0 && ref < 0))) { - sum += tmp; - div += 1; - } - } - } + cur = malloc(sizeof(double) * h); + ref = malloc(sizeof(double) * h); - // division by zero => neutral - if (div == 0) - return; - // otherwise just mean of the quotient of the two columns - else - sum /= div; - - // adjust each pixel - for (x = 0; x < h; x++) { - if (imaskp[x * stride + column] < 128 && imaskp[x * stride + column + sign] < 128) { - dstp[x * stride + column] *= sum; - } + dstp += column; + for (i = 0; i < h; i++) { + cur[i] = dstp[i * stride]; + ref[i] = dstp[sign + stride * i]; } -} - -static const VSFrame *VS_CC fixBrightnessGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { - FixBrightnessData *d = (FixBrightnessData *)instanceData; - - if (activationReason == arInitial) { - vsapi->requestFrameFilter(n, d->node, frameCtx); - if (d->ignore_mask) - vsapi->requestFrameFilter(n, d->ignore_mask, frameCtx); - } else if (activationReason == arAllFramesReady) { - const VSFrame *src = vsapi->getFrameFilter(n, d->node, frameCtx); - const VSFrame *ignore_mask = NULL; - const unsigned char *imaskp = NULL; - VSFrame *dst = vsapi->copyFrame(src, core); + double cov11, sumsq; - ptrdiff_t stride = vsapi->getStride(dst, d->plane) / 4; - int w = vsapi->getFrameWidth(src, d->plane); - int h = vsapi->getFrameHeight(src, d->plane); - float *dstp = (float *) vsapi->getWritePtr(dst, d->plane); + double c1; - if (d->ignore_mask) { - ignore_mask = vsapi->getFrameFilter(n, d->ignore_mask, frameCtx); - imaskp = vsapi->getReadPtr(ignore_mask, d->plane); + int status = gsl_fit_mul(cur, 1, ref, 1, h, &c1, &cov11, &sumsq); - if (d->top != 0) { - for (int row = d->top - 1; row > -1; --row) - processRowMasked(row, w, h, stride, dstp, imaskp, d); - } - if (d->bottom != 0) { - for (int row = h - d->bottom; row < h; ++row) - processRowMasked(row, w, h, stride, dstp, imaskp, d); - } - if (d->left != 0) { - for (int column = d->left - 1; column > -1; --column) - processColumnMasked(column, w, h, stride, dstp, imaskp, d); - } - if (d->right != 0) { - for (int column = w - d->right; column < w; ++column) - processColumnMasked(column, w, h, stride, dstp, imaskp, d); - } - } else { - if (d->top != 0) { - for (int row = d->top - 1; row > -1; --row) - processRow(row, w, h, stride, dstp, d); - } - if (d->bottom != 0) { - for (int row = h - d->bottom; row < h; ++row) - processRow(row, w, h, stride, dstp, d); - } - if (d->left != 0) { - for (int column = d->left - 1; column > -1; --column) - processColumn(column, w, h, stride, dstp, d); - } - if (d->right != 0) { - for (int column = w - d->right; column < w; ++column) - processColumn(column, w, h, stride, dstp, d); - } + if (!status && isfinite(c1)) { + // adjust each pixel + for (i = 0; i < h; i++) { + dstp[i * stride] *= c1; } - - - vsapi->freeFrame(src); - vsapi->freeFrame(ignore_mask); - - return dst; } - return NULL; -} - -static void VS_CC fixBrightnessFree(void *instanceData, VSCore *core, const VSAPI *vsapi) { - FixBrightnessData *d = (FixBrightnessData *)instanceData; - vsapi->freeNode(d->node); - vsapi->freeNode(d->ignore_mask); - free(d); + free(cur); + free(ref); } +static void processRowSLRMasked(int row, int w, int h, ptrdiff_t stride, float *dstp, int ref_line_size, double sigmaS, double sigmaR, double sigmaD, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { + int sign = 1; + if (row > h / 2) + sign = -1; -static void VS_CC fixBrightnessCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) { - FixBrightnessData d; - FixBrightnessData *data; - int err; - - d.node = vsapi->mapGetNode(in, "clip", 0, 0); - const VSVideoInfo *vi = vsapi->getVideoInfo(d.node); + double *cur, *ref; + double *weights; + int i; - if (!vsh_isConstantVideoFormat(vi) || vi->format.sampleType != stFloat) { - vsapi->mapSetError(out, "FixBrightness: only constant format single float input supported"); - vsapi->freeNode(d.node); - return; - } + cur = malloc(sizeof(double) * w); + ref = malloc(sizeof(double) * w); + weights = malloc(sizeof(double) * w); - d.ignore_mask = vsapi->mapGetNode(in, "ignore_mask", 0, &err); - if (err) - d.ignore_mask = NULL; - else { - vsapi->freeNode(d.ignore_mask); + dstp += row * stride; + imaskp += row * imaskstride; + for (i = 0; i < w; i++) { + cur[i] = dstp[i]; + ref[i] = dstp[sign * stride + i]; + if (imaskp[i] < 128 && imaskp[sign * mask_dist * imaskstride + i] < 128) + weights[i] = 1.0; + else + weights[i] = 0.0; } - d.thrlo = (float)vsapi->mapGetFloat(in, "thrlo", 0, &err); - if (err) - d.thrlo = 0.1; - - d.thrhi = (float)vsapi->mapGetFloat(in, "thrhi", 0, &err); - if (err) - d.thrhi = 8.f; - - d.thrlo = (float)vsapi->mapGetFloat(in, "thrlo", 0, &err); - if (err) - d.thrlo = 0.1; - - d.thrhi = (float)vsapi->mapGetFloat(in, "thrhi", 0, &err); - if (err) - d.thrhi = 8.f; - - d.top = vsapi->mapGetInt(in, "top", 0, &err); - if (err) - d.top = 0; + double cov11, sumsq; - else if (d.top > vi->height / 2) { - vsapi->mapSetError(out, "FixBrightness: top must be in [0, height / 2]"); - vsapi->freeNode(d.node); - vsapi->freeNode(d.ignore_mask); - return; - } + double c1; - d.bottom = vsapi->mapGetInt(in, "bottom", 0, &err); - if (err) - d.bottom = 0; + int status = gsl_fit_wmul(cur, 1, weights, 1, ref, 1, w, &c1, &cov11, &sumsq); - else if (d.bottom > vi->height / 2) { - vsapi->mapSetError(out, "FixBrightness: bottom must be in [0, height / 2]"); - vsapi->freeNode(d.node); - vsapi->freeNode(d.ignore_mask); - return; + if (!status && isfinite(c1)) { + // adjust each pixel + for (i = 0; i < w; i++) { + dstp[i] *= c1; + } } + + free(cur); + free(ref); + free(weights); +} - d.left = vsapi->mapGetInt(in, "left", 0, &err); - if (err) - d.left = 0; +static void processColumnSLRMasked(int column, int w, int h, ptrdiff_t stride, float *dstp, int ref_line_size, double sigmaS, double sigmaR, double sigmaD, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { + int sign = 1; + if (column > w / 2) + sign = -1; - else if (d.left > vi->width / 2) { - vsapi->mapSetError(out, "FixBrightness: left must be in [0, width / 2]"); - vsapi->freeNode(d.node); - vsapi->freeNode(d.ignore_mask); - return; - } + double *cur, *ref; + double *weights; + int i; - d.right = vsapi->mapGetInt(in, "right", 0, &err); - if (err) - d.right = 0; + cur = malloc(sizeof(double) * h); + ref = malloc(sizeof(double) * h); + weights = malloc(sizeof(double) * h); - else if (d.right > vi->width / 2) { - vsapi->mapSetError(out, "FixBrightness: right must be in [0, width / 2]"); - vsapi->freeNode(d.node); - vsapi->freeNode(d.ignore_mask); - return; + imaskp += column; + dstp += column; + for (i = 0; i < h; i++) { + cur[i] = dstp[i * stride]; + ref[i] = dstp[sign + stride * i]; + if (imaskp[i * imaskstride] < 128 && imaskp[sign * mask_dist + imaskstride * i] < 128) + weights[i] = 1.0; + else + weights[i] = 0.0; } - d.step = vsapi->mapGetInt(in, "step", 0, &err); - if (err) - d.step = 1; + double cov11, sumsq; - d.plane = vsapi->mapGetInt(in, "plane", 0, &err); - if (err) - d.plane = 0; + double c1; - data = (FixBrightnessData *)malloc(sizeof(d)); - *data = d; + int status = gsl_fit_wmul(cur, 1, weights, 1, ref, 1, h, &c1, &cov11, &sumsq); - VSFilterDependency deps[] = {{d.node, rpStrictSpatial}}; - vsapi->createVideoFilter(out, "FixBrightness", vi, fixBrightnessGetFrame, fixBrightnessFree, fmParallel, deps, 1, data, core); + if (!status && isfinite(c1)) { + // adjust each pixel + for (i = 0; i < h; i++) { + dstp[i * stride] *= c1; + } + } + + free(cur); + free(ref); + free(weights); } -typedef struct { - VSNode *node; - int plane; - int top; - int bottom; - int left; - int right; - int mode; -} LinearRegressionData; - -static void processRowSLR(int row, int w, int h, ptrdiff_t stride, float *dstp) { +static void debugRowSLR(int row, int w, int h, ptrdiff_t stride, float *dstp, VSFrame *dst, const VSAPI *vsapi) { int sign = 1; if (row > h / 2) sign = -1; @@ -399,23 +217,20 @@ static void processRowSLR(int row, int w, int h, ptrdiff_t stride, float *dstp) double c1; - const double *const_cur = cur; - const double *const_ref = ref; - - int status = gsl_fit_mul(const_cur, 1, const_ref, 1, w, &c1, &cov11, &sumsq); + int status = gsl_fit_mul(cur, 1, ref, 1, w, &c1, &cov11, &sumsq); - if (!status && isfinite(c1)) { - // adjust each pixel - for (i = 0; i < w; i++) { - dstp[i] *= c1; - } + if (!status || isfinite(c1)) { + c1 = 0; } + vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BoreAdjustment", c1, maAppend); + vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BoreCovariance", cov11, maAppend); + vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BoreSumSquares", sumsq, maAppend); free(cur); free(ref); } -static void processColumnSLR(int column, int w, int h, ptrdiff_t stride, float *dstp) { +static void debugColumnSLR(int column, int w, int h, ptrdiff_t stride, float *dstp, VSFrame *dst, const VSAPI *vsapi) { int sign = 1; if (column > w / 2) sign = -1; @@ -436,93 +251,101 @@ static void processColumnSLR(int column, int w, int h, ptrdiff_t stride, float * double c1; - const double *const_cur = cur; - const double *const_ref = ref; + int status = gsl_fit_mul(cur, 1, ref, 1, h, &c1, &cov11, &sumsq); - int status = gsl_fit_mul(const_cur, 1, const_ref, 1, h, &c1, &cov11, &sumsq); - - if (!status && isfinite(c1)) { - // adjust each pixel - for (i = 0; i < h; i++) { - dstp[i * stride] *= c1; - } + if (!status || isfinite(c1)) { + c1 = 0; } + vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BoreAdjustment", c1, maAppend); + vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BoreCovariance", cov11, maAppend); + vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BoreSumSquares", sumsq, maAppend); free(cur); free(ref); } -static void debugRowSLR(int row, int w, int h, ptrdiff_t stride, float *dstp, VSFrame *dst, const VSAPI *vsapi) { + +static void debugRowSLRMasked(int row, int w, int h, ptrdiff_t stride, float *dstp, VSFrame *dst, const VSAPI *vsapi, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { int sign = 1; if (row > h / 2) sign = -1; double *cur, *ref; + double *weights; int i; cur = malloc(sizeof(double) * w); ref = malloc(sizeof(double) * w); + weights = malloc(sizeof(double) * w); dstp += row * stride; + imaskp += row * imaskstride; for (i = 0; i < w; i++) { cur[i] = dstp[i]; ref[i] = dstp[sign * stride + i]; + if (imaskp[i] < 128 && imaskp[sign * mask_dist * imaskstride + i] < 128) + weights[i] = 1.0; + else + weights[i] = 0.0; } double cov11, sumsq; double c1; - const double *const_cur = cur; - const double *const_ref = ref; + int status = gsl_fit_wmul(cur, 1, weights, 1, ref, 1, w, &c1, &cov11, &sumsq); - int status = gsl_fit_mul(const_cur, 1, const_ref, 1, w, &c1, &cov11, &sumsq); - - /* if (!status || isfinite(c1)) { */ - /* c1 = 0; */ - /* } */ - vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BalanceAdjustment", c1, maAppend); - vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BalanceCovariance", cov11, maAppend); - vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BalanceSumSquares", sumsq, maAppend); + if (!status || isfinite(c1)) { + c1 = 0; + } + vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BoreAdjustment", c1, maAppend); + vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BoreCovariance", cov11, maAppend); + vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BoreSumSquares", sumsq, maAppend); free(cur); free(ref); + free(weights); } -static void debugColumnSLR(int column, int w, int h, ptrdiff_t stride, float *dstp, VSFrame *dst, const VSAPI *vsapi) { +static void debugColumnSLRMasked(int column, int w, int h, ptrdiff_t stride, float *dstp, VSFrame *dst, const VSAPI *vsapi, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { int sign = 1; if (column > w / 2) sign = -1; double *cur, *ref; + double *weights; int i; cur = malloc(sizeof(double) * h); ref = malloc(sizeof(double) * h); + weights = malloc(sizeof(double) * h); + imaskp += column; dstp += column; for (i = 0; i < h; i++) { cur[i] = dstp[i * stride]; ref[i] = dstp[sign + stride * i]; + if (imaskp[i * imaskstride] < 128 && imaskp[sign * mask_dist + imaskstride * i] < 128) + weights[i] = 1.0; + else + weights[i] = 0.0; } double cov11, sumsq; double c1; - const double *const_cur = cur; - const double *const_ref = ref; - - int status = gsl_fit_mul(const_cur, 1, const_ref, 1, h, &c1, &cov11, &sumsq); + int status = gsl_fit_wmul(cur, 1, weights, 1, ref, 1, h, &c1, &cov11, &sumsq); - /* if (!status || isfinite(c1)) { */ - /* c1 = 0; */ - /* } */ - vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BalanceAdjustment", c1, maAppend); - vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BalanceCovariance", cov11, maAppend); - vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BalanceSumSquares", sumsq, maAppend); + if (!status || isfinite(c1)) { + c1 = 0; + } + vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BoreAdjustment", c1, maAppend); + vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BoreCovariance", cov11, maAppend); + vsapi->mapSetFloat(vsapi->getFramePropertiesRW(dst), "BoreSumSquares", sumsq, maAppend); free(cur); free(ref); + free(weights); } static void processRowMLR(int row, int w, int h, ptrdiff_t stride, float *dstp, float *dstp1, float *dstp2, float *dstp3) { @@ -601,13 +424,453 @@ static void processColumnMLR(int column, int w, int h, ptrdiff_t stride, float * gsl_vector_free(y); } -static const VSFrame *VS_CC linearRegressionGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { - LinearRegressionData *d = (LinearRegressionData *)instanceData; +static void processRowSLRRef(int row, int w, int h, ptrdiff_t stride, float *dstp, int ref_line_size, double sigmaS, double sigmaR, double sigmaD, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { + int sign = 1; + if (row > h / 2) + sign = -1; - if (activationReason == arInitial) { - vsapi->requestFrameFilter(n, d->node, frameCtx); - } else if (activationReason == arAllFramesReady) { + int i; + int start, stop; + int status; + + double c1; + double cov11, sumsq; + + double *cur, *ref; + cur = malloc(sizeof(double) * w); + ref = malloc(sizeof(double) * w); + + dstp += row * stride; + for (i = 0; i < w; i++) { + cur[i] = dstp[i]; + ref[i] = dstp[sign * stride + i]; + } + + for (i = 0; i < w; i++) { + start = i - ref_line_size; + stop = i + ref_line_size + 1; + if (start < 0) + start = 0; + if (stop >= w) + stop = w - 1; + + status = gsl_fit_mul(cur + start, 1, ref + start, 1, stop - start, &c1, &cov11, &sumsq); + + if (!status && isfinite(c1)) + dstp[i] *= c1; + } + + free(cur); + free(ref); +} + +static void processColumnSLRRef(int column, int w, int h, ptrdiff_t stride, float *dstp, int ref_line_size, double sigmaS, double sigmaR, double sigmaD, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { + int sign = 1; + if (column > w / 2) + sign = -1; + + int i; + int start, stop; + int status; + + double c1; + double cov11, sumsq; + + double *cur, *ref; + cur = malloc(sizeof(double) * h); + ref = malloc(sizeof(double) * h); + + dstp += column; + for (i = 0; i < h; i++) { + cur[i] = dstp[i * stride]; + ref[i] = dstp[sign + stride * i]; + } + + for (i = 0; i < h; i++) { + start = i - ref_line_size; + stop = i + ref_line_size + 1; + if (start < 0) + start = 0; + if (stop >= h) + stop = h - 1; + + status = gsl_fit_mul(cur + start, 1, ref + start, 1, stop - start, &c1, &cov11, &sumsq); + + if (!status && isfinite(c1)) + dstp[i * stride] *= c1; + } + + free(cur); + free(ref); +} + +static void processRowSLRRefMasked(int row, int w, int h, ptrdiff_t stride, float *dstp, int ref_line_size, double sigmaS, double sigmaR, double sigmaD, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { + int sign = 1; + if (row > h / 2) + sign = -1; + + int i; + int start, stop; + int status; + + double c1; + double cov11, sumsq; + + double *cur, *ref; + double *weights; + + cur = malloc(sizeof(double) * w); + ref = malloc(sizeof(double) * w); + weights = malloc(sizeof(double) * w); + + dstp += row * stride; + imaskp += row * imaskstride; + for (i = 0; i < w; i++) { + cur[i] = dstp[i]; + ref[i] = dstp[sign * stride + i]; + if (imaskp[i] < 128 && imaskp[sign * mask_dist * imaskstride + i] < 128) + weights[i] = 1.0; + else + weights[i] = 0.0; + } + + for (i = 0; i < w; i++) { + start = i - ref_line_size; + stop = i + ref_line_size + 1; + if (start < 0) + start = 0; + if (stop >= w) + stop = w - 1; + + status = gsl_fit_wmul(cur + start, 1, weights + start, 1, ref + start, 1, stop - start, &c1, &cov11, &sumsq); + + if (!status && isfinite(c1)) + dstp[i] *= c1; + } + + free(cur); + free(ref); + free(weights); +} + +static void processColumnSLRRefMasked(int column, int w, int h, ptrdiff_t stride, float *dstp, int ref_line_size, double sigmaS, double sigmaR, double sigmaD, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { + int sign = 1; + if (column > w / 2) + sign = -1; + + int i; + int start, stop; + int status; + + double c1; + double cov11, sumsq; + + double *cur, *ref; + double *weights; + + cur = malloc(sizeof(double) * h); + ref = malloc(sizeof(double) * h); + weights = malloc(sizeof(double) * h); + + imaskp += column; + dstp += column; + for (i = 0; i < h; i++) { + cur[i] = dstp[i * stride]; + ref[i] = dstp[sign + stride * i]; + if (imaskp[i * imaskstride] < 128 && imaskp[sign * mask_dist + imaskstride * i] < 128) + weights[i] = 1.0; + else + weights[i] = 1.0; + } + + for (i = 0; i < h; i++) { + start = i - ref_line_size; + stop = i + ref_line_size + 1; + if (start < 0) + start = 0; + if (stop >= h) + stop = h - 1; + + status = gsl_fit_wmul(cur + start, 1, weights + start, 1, ref + start, 1, stop - start, &c1, &cov11, &sumsq); + + if (!status && isfinite(c1)) + dstp[i * stride] *= c1; + } + + free(cur); + free(ref); + free(weights); +} + +static void processRowWSLR(int row, int w, int h, ptrdiff_t stride, float *dstp, int ref_line_size, double sigmaS, double sigmaR, double sigmaD, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { + int sign = 1; + if (row > h / 2) + sign = -1; + + int i; + int k; + int start, stop; + int status; + + double c1; + double cov11, sumsq; + + double *cur, *ref; + cur = malloc(sizeof(double) * w); + ref = malloc(sizeof(double) * w); + + dstp += row * stride; + for (i = 0; i < w; i++) { + cur[i] = dstp[i]; + ref[i] = dstp[sign * stride + i]; + } + + double w_s; + double w_c; + double w_d; + + for (i = 0; i < w; i++) { + start = i - ref_line_size; + stop = i + ref_line_size + 1; + if (start < 0) + start = 0; + if (stop >= w) + stop = w - 1; + + double *weights; + weights = malloc(sizeof(double) * (stop - start)); + + for (k = start; k < stop; k++) { + w_s = exp(-((k - i) * (k - i)) / (sigmaS * sigmaS)); + w_c = exp(-((cur[k] - cur[i]) * (cur[k] - cur[i]) / (sigmaR * sigmaR))); + w_d = exp(-(ref[k] / cur[k] - ref[i] / cur[i]) * (ref[k] / cur[k] - ref[i] / cur[i]) / (sigmaD * sigmaD)); + weights[k - start] = w_s * w_c * w_d; + /* weights[k] = exp(-((j - i) * (j - i)) / (sigmaS * sigmaS) - ((cur[k] - cur[ref_line_size]) * (cur[k] - cur[ref_line_size])) / (sigmaR * sigmaR) - (ref[k] / cur[k] - cur[ref_line_size] / dstp[sign * stride + i]) * (ref[k] / cur[k] - cur[ref_line_size] / dstp[sign * stride + i]) / (sigmaD * sigmaD)); */ + /* weights[k] = exp(-((j - i) * (j - i)) / (sigmaS * sigmaS) - ((cur[k] - cur[ref_line_size]) * (cur[k] - cur[ref_line_size])) / (sigmaR * sigmaR)); */ + /* if (i == 1370) { */ + /* dstp[j] = w_d;//(dstp[j] - dstp[i]) * (dstp[j] - dstp[i]); */ + /* dstp[i] = 1.0; */ + /* } */ + } + + + status = gsl_fit_wmul(cur + start, 1, weights, 1, ref + start, 1, stop - start, &c1, &cov11, &sumsq); + + if (!status && isfinite(c1)) + dstp[i] *= c1; + + free(weights); + } + + free(cur); + free(ref); +} + +static void processColumnWSLR(int column, int w, int h, ptrdiff_t stride, float *dstp, int ref_line_size, double sigmaS, double sigmaR, double sigmaD, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { + int sign = 1; + if (column > w / 2) + sign = -1; + + int i; + int k; + int start, stop; + int status; + + double c1; + double cov11, sumsq; + + double *cur, *ref; + cur = malloc(sizeof(double) * h); + ref = malloc(sizeof(double) * h); + + dstp += column; + for (i = 0; i < h; i++) { + cur[i] = dstp[i * stride]; + ref[i] = dstp[sign + stride * i]; + } + + for (i = 0; i < h; i++) { + start = i - ref_line_size; + stop = i + ref_line_size + 1; + if (start < 0) + start = 0; + if (stop >= h) + stop = h - 1; + + double *weights; + weights = malloc(sizeof(double) * (stop - start)); + + for (k = start; k < stop; k++) { + weights[k - start] = exp(-((k - i) * (k - i)) / (sigmaS * sigmaS) - ((cur[k] - dstp[i * stride]) * (cur[k] - dstp[i * stride])) / (sigmaR * sigmaR) - (ref[k] / cur[k] - dstp[i] / dstp[sign * stride + i]) * (ref[k] / cur[k] - dstp[i] / dstp[sign * stride + i]) / (sigmaD * sigmaD)); + } + + status = gsl_fit_wmul(cur + start, 1, weights, 1, ref + start, 1, stop - start, &c1, &cov11, &sumsq); + + if (!status && isfinite(c1)) + dstp[i * stride] *= c1; + + free(weights); + } + + free(cur); + free(ref); +} + + +static void processRowWSLRMasked(int row, int w, int h, ptrdiff_t stride, float *dstp, int ref_line_size, double sigmaS, double sigmaR, double sigmaD, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { + int sign = 1; + if (row > h / 2) + sign = -1; + + int i; + int j; + int k; + int start, stop; + int status; + + double c1; + double cov11, sumsq; + + double *cur, *ref; + cur = malloc(sizeof(double) * w); + ref = malloc(sizeof(double) * w); + + dstp += row * stride; + imaskp += row * imaskstride; + for (i = 0; i < w; i++) { + cur[i] = dstp[i]; + ref[i] = dstp[sign * stride + i]; + } + + double w_s; + double w_c; + double w_d; + + double *weights; + weights = malloc(sizeof(double) * (2 * ref_line_size)); + + sign *= mask_dist; + + for (i = 0; i < w; i++) { + start = i - ref_line_size; + stop = i + ref_line_size + 1; + if (start < 0) + start = 0; + if (stop >= w) + stop = w - 1; + + for (k = 0; k < stop - start; k++) { + j = k + start; + if (imaskp[j] < 128 && imaskp[sign * imaskstride + j] < 128) { + w_s = exp(-((j - i) * (j - i)) / (sigmaS * sigmaS)); + w_c = exp(-((ref[j] - ref[i]) * (ref[j] - ref[i]) / (sigmaR * sigmaR))); + w_d = exp(-(ref[j] / cur[j] - ref[i] / cur[i]) * (ref[j] / cur[j] - ref[i] / cur[i]) / (sigmaD * sigmaD)); + weights[k] = w_s * w_c * w_d; + } else + weights[k] = 0.0; + /* if (i == 1370) { */ + /* dstp[j] = w_d;//(dstp[j] - dstp[i]) * (dstp[j] - dstp[i]); */ + /* dstp[i] = 1.0; */ + /* } */ + } + + status = gsl_fit_wmul(cur + start, 1, weights, 1, ref + start, 1, stop - start, &c1, &cov11, &sumsq); + + if (!status && isfinite(c1)) + dstp[i] *= c1; + + } + + free(weights); + free(cur); + free(ref); +} + +static void processColumnWSLRMasked(int column, int w, int h, ptrdiff_t stride, float *dstp, int ref_line_size, double sigmaS, double sigmaR, double sigmaD, const unsigned char * restrict imaskp, ptrdiff_t imaskstride, int mask_dist) { + int sign = 1; + if (column > w / 2) + sign = -1; + + int i; + int j; + int k; + int start, stop; + int status; + + double c1; + double cov11, sumsq; + + double *cur, *ref; + cur = malloc(sizeof(double) * h); + ref = malloc(sizeof(double) * h); + + double *weights; + weights = malloc(sizeof(double) * (2 * ref_line_size)); + + imaskp += column; + dstp += column; + for (i = 0; i < h; i++) { + cur[i] = dstp[i * stride]; + ref[i] = dstp[sign + stride * i]; + } + + double w_s, w_c, w_d; + + sign *= mask_dist; + + for (i = 0; i < h; i++) { + start = i - ref_line_size; + stop = i + ref_line_size + 1; + if (start < 0) + start = 0; + if (stop >= h) + stop = h - 1; + + for (k = 0; k < stop - start; k++) { + j = k + start; + if (imaskp[j * imaskstride] < 128 && imaskp[sign + imaskstride * j] < 128) { + /* cur_cur = cur[i]; */ + /* cur_ref = cur[j]; */ + /* ref_cur = ref[i]; */ + /* ref_ref = ref[j]; */ + /* w_s = exp(-((j - i) * (j - i)) / (sigmaS * sigmaS)); */ + /* w_c = exp(-((ref_ref - ref_cur) * (ref_ref - ref_cur) + (cur_ref - cur_cur) * (cur_ref - cur_cur)) / (sigmaR * sigmaR)); */ + /* w_d = exp(-(ref_ref / cur_ref - ref_cur / cur_cur) * (ref_ref / cur_ref - ref_cur / cur_cur) / (sigmaD * sigmaD)); */ + w_s = exp(-((j - i) * (j - i)) / (sigmaS * sigmaS)); + w_c = exp(-((ref[j] - ref[i]) * (ref[j] - ref[i]) / (sigmaR * sigmaR))); + w_d = exp(-(ref[j] / cur[j] - ref[i] / cur[i]) * (ref[j] / cur[j] - ref[i] / cur[i]) / (sigmaD * sigmaD)); + weights[k] = w_s * w_c * w_d; + } else + weights[k] = 0.0; + /* if (i == 880) { */ + /* dstp[j * stride] = weights[k]; */ + /* dstp[i * stride] = 0.5; */ + /* } */ + } + + status = gsl_fit_wmul(cur + start, 1, weights, 1, ref + start, 1, stop - start, &c1, &cov11, &sumsq); + + if (!status && isfinite(c1)) + dstp[i * stride] *= c1; + + } + + free(weights); + free(cur); + free(ref); +} + +static const VSFrame *VS_CC singlePlaneGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { + LinearRegressionData *d = (LinearRegressionData *)instanceData; + + if (activationReason == arInitial) { + vsapi->requestFrameFilter(n, d->node, frameCtx); + if (d->ignore_mask) + vsapi->requestFrameFilter(n, d->ignore_mask, frameCtx); + } else if (activationReason == arAllFramesReady) { const VSFrame *src = vsapi->getFrameFilter(n, d->node, frameCtx); + const VSFrame *ignore_mask = NULL; + const unsigned char *imaskp = NULL; VSFrame *dst = vsapi->copyFrame(src, core); @@ -615,44 +878,138 @@ static const VSFrame *VS_CC linearRegressionGetFrame(int n, int activationReason int w = vsapi->getFrameWidth(src, d->plane); int h = vsapi->getFrameHeight(src, d->plane); float *dstp = (float *) vsapi->getWritePtr(dst, d->plane); - if (d->mode == 1) { - float *dstp1 = (float *) vsapi->getWritePtr(dst, 0); - float *dstp2 = (float *) vsapi->getWritePtr(dst, 1); - float *dstp3 = (float *) vsapi->getWritePtr(dst, 2); + if (d->ignore_mask) { + ignore_mask = vsapi->getFrameFilter(n, d->ignore_mask, frameCtx); + ptrdiff_t imaskstride = vsapi->getStride(ignore_mask, 0); + imaskp = vsapi->getReadPtr(ignore_mask, 0); if (d->top != 0) { for (int row = d->top - 1; row > -1; --row) - processRowMLR(row, w, h, stride, dstp, dstp1, dstp2, dstp3); + d->processRow(row, w, h, stride, dstp, d->ref_line_size, d->sigmaS, d->sigmaR, d->sigmaD, imaskp, imaskstride, d->top - row); } if (d->bottom != 0) { for (int row = h - d->bottom; row < h; ++row) - processRowMLR(row, w, h, stride, dstp, dstp1, dstp2, dstp3); + d->processRow(row, w, h, stride, dstp, d->ref_line_size, d->sigmaS, d->sigmaR, d->sigmaD, imaskp, imaskstride, d->bottom + row - h + 1); } if (d->left != 0) { for (int column = d->left - 1; column > -1; --column) - processColumnMLR(column, w, h, stride, dstp, dstp1, dstp2, dstp3); + d->processColumn(column, w, h, stride, dstp, d->ref_line_size, d->sigmaS, d->sigmaR, d->sigmaD, imaskp, imaskstride, d->left - column); } if (d->right != 0) { for (int column = w - d->right; column < w; ++column) - processColumnMLR(column, w, h, stride, dstp, dstp1, dstp2, dstp3); + d->processColumn(column, w, h, stride, dstp, d->ref_line_size, d->sigmaS, d->sigmaR, d->sigmaD, imaskp, imaskstride, d->right + column - w + 1); } - } else if (d->mode == 1) { + } else { + if (d->top != 0) { + for (int row = d->top - 1; row > -1; --row) + d->processRow(row, w, h, stride, dstp, d->ref_line_size, d->sigmaS, d->sigmaR, d->sigmaD, NULL, 0, 0); + } + if (d->bottom != 0) { + for (int row = h - d->bottom; row < h; ++row) + d->processRow(row, w, h, stride, dstp, d->ref_line_size, d->sigmaS, d->sigmaR, d->sigmaD, NULL, 0, 0); + } + if (d->left != 0) { + for (int column = d->left - 1; column > -1; --column) + d->processColumn(column, w, h, stride, dstp, d->ref_line_size, d->sigmaS, d->sigmaR, d->sigmaD, NULL, 0, 0); + } + if (d->right != 0) { + for (int column = w - d->right; column < w; ++column) + d->processColumn(column, w, h, stride, dstp, d->ref_line_size, d->sigmaS, d->sigmaR, d->sigmaD, NULL, 0, 0); + } + } + + vsapi->freeFrame(ignore_mask); + vsapi->freeFrame(src); + + return dst; + } + + return NULL; +} + +static const VSFrame *VS_CC multiPlaneGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { + LinearRegressionData *d = (LinearRegressionData *)instanceData; + + if (activationReason == arInitial) { + vsapi->requestFrameFilter(n, d->node, frameCtx); + } else if (activationReason == arAllFramesReady) { + const VSFrame *src = vsapi->getFrameFilter(n, d->node, frameCtx); + + VSFrame *dst = vsapi->copyFrame(src, core); + + ptrdiff_t stride = vsapi->getStride(dst, d->plane) / 4; + int w = vsapi->getFrameWidth(src, d->plane); + int h = vsapi->getFrameHeight(src, d->plane); + float *dstp = (float *) vsapi->getWritePtr(dst, d->plane); + float *dstp1 = (float *) vsapi->getWritePtr(dst, 0); + float *dstp2 = (float *) vsapi->getWritePtr(dst, 1); + float *dstp3 = (float *) vsapi->getWritePtr(dst, 2); + + if (d->top != 0) { + for (int row = d->top - 1; row > -1; --row) + processRowMLR(row, w, h, stride, dstp, dstp1, dstp2, dstp3); + } + if (d->bottom != 0) { + for (int row = h - d->bottom; row < h; ++row) + processRowMLR(row, w, h, stride, dstp, dstp1, dstp2, dstp3); + } + if (d->left != 0) { + for (int column = d->left - 1; column > -1; --column) + processColumnMLR(column, w, h, stride, dstp, dstp1, dstp2, dstp3); + } + if (d->right != 0) { + for (int column = w - d->right; column < w; ++column) + processColumnMLR(column, w, h, stride, dstp, dstp1, dstp2, dstp3); + } + + vsapi->freeFrame(src); + + return dst; + } + + return NULL; +} + +static const VSFrame *VS_CC singlePlaneDebugGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { + LinearRegressionData *d = (LinearRegressionData *)instanceData; + + if (activationReason == arInitial) { + vsapi->requestFrameFilter(n, d->node, frameCtx); + if (d->ignore_mask) + vsapi->requestFrameFilter(n, d->ignore_mask, frameCtx); + } else if (activationReason == arAllFramesReady) { + const VSFrame *src = vsapi->getFrameFilter(n, d->node, frameCtx); + const VSFrame *ignore_mask = NULL; + const unsigned char *imaskp = NULL; + + VSFrame *dst = vsapi->copyFrame(src, core); + + ptrdiff_t stride = vsapi->getStride(dst, d->plane) / 4; + int w = vsapi->getFrameWidth(src, d->plane); + int h = vsapi->getFrameHeight(src, d->plane); + float *dstp = (float *) vsapi->getWritePtr(dst, d->plane); + + if (d->ignore_mask) { + ignore_mask = vsapi->getFrameFilter(n, d->ignore_mask, frameCtx); + ptrdiff_t imaskstride = vsapi->getStride(ignore_mask, 0); + imaskp = vsapi->getReadPtr(ignore_mask, d->plane); if (d->top != 0) { for (int row = d->top - 1; row > -1; --row) - processRowSLR(row, w, h, stride, dstp); + debugRowSLRMasked(row, w, h, stride, dstp, dst, vsapi, imaskp, imaskstride, d->top - row); } if (d->bottom != 0) { for (int row = h - d->bottom; row < h; ++row) - processRowSLR(row, w, h, stride, dstp); + debugRowSLRMasked(row, w, h, stride, dstp, dst, vsapi, imaskp, imaskstride, d->bottom + row - h + 1); } if (d->left != 0) { for (int column = d->left - 1; column > -1; --column) - processColumnSLR(column, w, h, stride, dstp); + debugColumnSLRMasked(column, w, h, stride, dstp, dst, vsapi, imaskp, imaskstride, d->left - column); } if (d->right != 0) { for (int column = w - d->right; column < w; ++column) - processColumnSLR(column, w, h, stride, dstp); + debugColumnSLRMasked(column, w, h, stride, dstp, dst, vsapi, imaskp, imaskstride, d->right + column - w + 1); } + vsapi->freeFrame(ignore_mask); } else { if (d->top != 0) { for (int row = d->top - 1; row > -1; --row) @@ -671,7 +1028,7 @@ static const VSFrame *VS_CC linearRegressionGetFrame(int n, int activationReason debugColumnSLR(column, w, h, stride, dstp, dst, vsapi); } } - + vsapi->freeFrame(src); return dst; @@ -683,24 +1040,78 @@ static const VSFrame *VS_CC linearRegressionGetFrame(int n, int activationReason static void VS_CC linearRegressionFree(void *instanceData, VSCore *core, const VSAPI *vsapi) { LinearRegressionData *d = (LinearRegressionData *)instanceData; vsapi->freeNode(d->node); + vsapi->freeNode(d->ignore_mask); free(d); } static void VS_CC linearRegressionCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) { + LinRegMode mode = (enum LinRegMode) userData; LinearRegressionData d; LinearRegressionData *data; - int err; + int err, w, h; d.node = vsapi->mapGetNode(in, "clip", 0, 0); const VSVideoInfo *vi = vsapi->getVideoInfo(d.node); + d.plane = vsapi->mapGetInt(in, "plane", 0, &err); + if (err) + d.plane = 0; + + if (!vsh_isConstantVideoFormat(vi) || vi->format.sampleType != stFloat) { + vsapi->mapSetError(out, "bore: only constant format single float clip input is supported"); + vsapi->freeNode(d.node); + return; + } + + if (vi->width == 0 || vi->height == 0) { + vsapi->mapSetError(out, "bore: only constant resolution clip input is supported"); + vsapi->freeNode(d.node); + return; + } + + w = vi->width; + h = vi->height; + + if (d.plane > 0) { + w = w >> vi->format.subSamplingW; + h = h >> vi->format.subSamplingH; + } + + d.ignore_mask = vsapi->mapGetNode(in, "ignore_mask", 0, &err); + if (err) + d.ignore_mask = NULL; + else { + const VSVideoInfo *ivi = vsapi->getVideoInfo(d.ignore_mask); + if (!vsh_isConstantVideoFormat(ivi) || (ivi->format.sampleType != stInteger && ivi->format.bitsPerSample != 8)) { + vsapi->mapSetError(out, "bore: only constant format 8-bit ignore_mask input is supported"); + vsapi->freeNode(d.node); + vsapi->freeNode(d.ignore_mask); + return; + } + + if (ivi->width != w || ivi->height != h) { + vsapi->mapSetError(out, "bore: clip and ignore_mask must have matching dimensions"); + vsapi->freeNode(d.node); + vsapi->freeNode(d.ignore_mask); + return; + } + + if (ivi->width == 0 || ivi->height == 0) { + vsapi->mapSetError(out, "bore: only constant resolution ignore_mask input is supported"); + vsapi->freeNode(d.node); + vsapi->freeNode(d.ignore_mask); + return; + } + } + d.top = vsapi->mapGetInt(in, "top", 0, &err); if (err) d.top = 0; - else if (d.top > vi->height / 2) { - vsapi->mapSetError(out, "Balance: top must be in [0, height / 2]"); + else if (d.top > h / 2) { + vsapi->mapSetError(out, "bore: top must be in [0, height / 2]"); vsapi->freeNode(d.node); + vsapi->freeNode(d.ignore_mask); return; } @@ -708,72 +1119,120 @@ static void VS_CC linearRegressionCreate(const VSMap *in, VSMap *out, void *user if (err) d.bottom = 0; - else if (d.bottom > vi->height / 2) { - vsapi->mapSetError(out, "Balance: bottom must be in [0, height / 2]"); + else if (d.bottom > h / 2) { + vsapi->mapSetError(out, "bore: bottom must be in [0, height / 2]"); vsapi->freeNode(d.node); + vsapi->freeNode(d.ignore_mask); return; } - d.left = vsapi->mapGetInt(in, "left", 0, &err); if (err) d.left = 0; - else if (d.left > vi->width / 2) { - vsapi->mapSetError(out, "Balance: left must be in [0, width / 2]"); + else if (d.left > w / 2) { + vsapi->mapSetError(out, "bore: left must be in [0, width / 2]"); vsapi->freeNode(d.node); + vsapi->freeNode(d.ignore_mask); return; } - d.right = vsapi->mapGetInt(in, "right", 0, &err); if (err) d.right = 0; - else if (d.right > vi->width / 2) { - vsapi->mapSetError(out, "Balance: right must be in [0, width / 2]"); + else if (d.right > w / 2) { + vsapi->mapSetError(out, "bore: right must be in [0, width / 2]"); vsapi->freeNode(d.node); + vsapi->freeNode(d.ignore_mask); return; } - d.plane = vsapi->mapGetInt(in, "plane", 0, &err); - if (err) - d.plane = 0; - - - d.mode = vsapi->mapGetInt(in, "mode", 0, &err); - if (err) - d.mode = 0; - - else if (d.mode > 2 || d.mode < 0) { - vsapi->mapSetError(out, "Balance: mode must be either 0 (simple), 1 (multiple), or 2 (simple debug)"); - vsapi->freeNode(d.node); - return; + if (mode == LINREG_MODE_SINGLE_LIMITED || mode == LINREG_MODE_SINGLE_WEIGHTED) { + d.ref_line_size = vsapi->mapGetInt(in, "ref_line_size", 0, &err); + if (err) + d.ref_line_size = 100; } - if (!vsh_isConstantVideoFormat(vi) || vi->format.sampleType != stFloat) { - vsapi->mapSetError(out, "Balance: only constant format single float input"); - vsapi->freeNode(d.node); - return; - } - if (d.mode == 1) { - if (vi->format.subSamplingH > 0 || vi->format.subSamplingW > 0 || vi->format.numPlanes != 3) { - vsapi->mapSetError(out, "Balance: mode 1 (multiple) requires three planes with no subsampling"); - vsapi->freeNode(d.node); - return; - } + switch (mode) { + case LINREG_MODE_SINGLE: + if (d.ignore_mask) { + d.processRow = &processRowSLRMasked; + d.processColumn = &processColumnSLRMasked; + } else { + d.processRow = &processRowSLR; + d.processColumn = &processColumnSLR; + } + break; + case LINREG_MODE_MULTI: + d.processRow = NULL; + d.processColumn = NULL; + break; + case LINREG_MODE_SINGLE_LIMITED: + if (d.ignore_mask) { + d.processRow = &processRowSLRRefMasked; + d.processColumn = &processColumnSLRRefMasked; + } else { + d.processRow = &processRowSLRRef; + d.processColumn = &processColumnSLRRef; + } + break; + case LINREG_MODE_SINGLE_WEIGHTED: + d.sigmaS = vsapi->mapGetFloat(in, "sigmaS", 0, &err); + if (err) + d.sigmaS = 50.0; + + d.sigmaR = vsapi->mapGetFloat(in, "sigmaR", 0, &err); + if (err) + d.sigmaR = 0.5; + + d.sigmaD = vsapi->mapGetFloat(in, "sigmaD", 0, &err); + if (err) + d.sigmaD = 1.5; + + if (d.ignore_mask) { + d.processRow = &processRowWSLRMasked; + d.processColumn = &processColumnWSLRMasked; + } else { + d.processRow = &processRowSLR; + d.processColumn = &processColumnSLR; + } + break; + case LINREG_MODE_SINGLE_DEBUG: + d.processRow = NULL; + d.processColumn = NULL; } data = (LinearRegressionData *)malloc(sizeof(d)); *data = d; VSFilterDependency deps[] = {{d.node, rpStrictSpatial}}; - vsapi->createVideoFilter(out, "Balance", vi, linearRegressionGetFrame, linearRegressionFree, fmParallel, deps, 1, data, core); + + switch (mode) { + case LINREG_MODE_SINGLE: + vsapi->createVideoFilter(out, "SinglePlane", vi, singlePlaneGetFrame, linearRegressionFree, fmParallel, deps, 1, data, core); + break; + case LINREG_MODE_MULTI: + vsapi->createVideoFilter(out, "MultiPlane", vi, multiPlaneGetFrame, linearRegressionFree, fmParallel, deps, 1, data, core); + break; + case LINREG_MODE_SINGLE_LIMITED: + vsapi->createVideoFilter(out, "SinglePlaneLimited", vi, singlePlaneGetFrame, linearRegressionFree, fmParallel, deps, 1, data, core); + break; + case LINREG_MODE_SINGLE_WEIGHTED: + vsapi->createVideoFilter(out, "SinglePlaneWeighted", vi, singlePlaneGetFrame, linearRegressionFree, fmParallel, deps, 1, data, core); + break; + case LINREG_MODE_SINGLE_DEBUG: + vsapi->createVideoFilter(out, "SinglePlaneDebug", vi, singlePlaneDebugGetFrame, linearRegressionFree, fmParallel, deps, 1, data, core); + break; + } } VS_EXTERNAL_API(void) VapourSynthPluginInit2(VSPlugin *plugin, const VSPLUGINAPI *vspapi) { vspapi->configPlugin("ng.opusga.bore", "bore", "bore plugin", VS_MAKE_VERSION(1, 0), VAPOURSYNTH_API_VERSION, 0, plugin); - vspapi->registerFunction("FixBrightness", "clip:vnode;top:int:opt;bottom:int:opt;left:int:opt;right:int:opt;ignore_mask:vnode:opt;thrlo:float:opt;thrhi:float:opt;step:int:opt;plane:int:opt;", "clip:vnode;", fixBrightnessCreate, NULL, plugin); - vspapi->registerFunction("Balance", "clip:vnode;top:int:opt;bottom:int:opt;left:int:opt;right:int:opt;plane:int:opt;mode:int:opt;", "clip:vnode;", linearRegressionCreate, NULL, plugin); + vspapi->registerFunction("SinglePlane", "clip:vnode;left:int:opt;right:int:opt;top:int:opt;bottom:int:opt;ignore_mask:vnode:opt;plane:int:opt;", "clip:vnode;", linearRegressionCreate, (void *)(LINREG_MODE_SINGLE), plugin); + vspapi->registerFunction("MultiPlane", "clip:vnode;left:int:opt;right:int:opt;top:int:opt;bottom:int:opt;ignore_mask:vnode:opt;plane:int:opt;", "clip:vnode;", linearRegressionCreate, (void *)(LINREG_MODE_MULTI), plugin); + vspapi->registerFunction("SinglePlaneLimited", "clip:vnode;left:int:opt;right:int:opt;top:int:opt;bottom:int:opt;ignore_mask:vnode:opt;ref_line_size:int:opt;plane:int:opt;", "clip:vnode;", linearRegressionCreate, (void *)(LINREG_MODE_SINGLE_LIMITED), plugin); + vspapi->registerFunction("SinglePlaneWeighted", "clip:vnode;left:int:opt;right:int:opt;top:int:opt;bottom:int:opt;ignore_mask:vnode:opt;sigmaS:float:opt;sigmaR:float:opt;sigmaD:float:opt;ref_line_size:int:opt;plane:int:opt;", "clip:vnode;", linearRegressionCreate, (void *)(LINREG_MODE_SINGLE_WEIGHTED), plugin); + vspapi->registerFunction("SinglePlaneDebug", "clip:vnode;left:int:opt;right:int:opt;top:int:opt;bottom:int:opt;ignore_mask:vnode:opt;plane:int:opt;", "clip:vnode;", linearRegressionCreate, (void *)(LINREG_MODE_SINGLE_DEBUG), plugin); }